Выражения в SymPy
В прошлом уроке мы вспомнили базовые понятия анализа функций: что такое функция, производная и интеграл, а также как выглядят простейшие дифференциальные уравнения. Теперь перейдём к практике: разберём библиотеку SymPy, которая позволяет делать математические преобразования в символьном виде.
В этом уроке вы научитесь создавать в SymPy числа, символы и выражения, брать производные и интегралы, решать некоторые уравнения (в том числе численно), получать решения дифференциальных уравнений, строить разложения в ряды и строить графики. В конце — практика с готовыми решениями и чек‑лист.
Главная мысль: SymPy — это “математика как объекты”, где выражение вроде \((x-y)(x-y)\) понимается именно как математика и может автоматически преобразовываться в \((x-y)^2\), а производная и интеграл возвращают не числа, а новые символические выражения.
Содержание
1. Цели урока
- Понять, из каких базовых объектов состоит SymPy: числа, символы, выражения, уравнения и функции.
- Научиться делать символьные преобразования: упрощение, раскрытие скобок, подстановки значений.
- Освоить символьное дифференцирование (
diff) и интегрирование (integrate) для выражений. - Познакомиться с решением уравнений (
solve) и численным решением (nsolve). - Увидеть, как решаются простые дифференциальные уравнения через
dsolve. - Научиться получать разложения в ряд (например, ряд Тейлора) и строить графики.
2. Что такое SymPy и зачем она нужна
В любой формальной системе — программировании, математике, логике — есть “базовые кирпичики”, из которых строится всё остальное. В Python мы уже видели, что огромную роль играют базовые типы, а также функции и классы. В математике фундамент — это множества, числа, точки и операции над ними.
В SymPy тоже есть набор ключевых объектов, вокруг которых строится работа с библиотекой. В первую очередь это числа (ведь SymPy создана для математики), а также символы и выражения.
Частая путаница: SymPy и обычный Python
float, вы потеряете часть преимуществ SymPy.Юридическое примечание
↑ К оглавлению3. Объекты SymPy: числа и константы
3.1. Какие численные типы есть в SymPy
SymPy реализует собственные численные типы, чтобы поддерживать символьную математику и точные рациональные значения. Среди часто используемых объектов можно выделить:
Integer: целые числа;Rational: рациональные числа в виде дроби;RealNumber: вещественные числа (внутренние представления вещественных);Float: числа с плавающей точкой (приближённые значения).
Например, можно создать целое число как обычный int Python, а можно — как sympy.Integer. Визуально они выглядят одинаково, но “внутренне” это разные типы.
import sympy as sp
a = 42
print(a, type(a)) # 42 <class 'int'>
b = sp.Integer(42) # 42 <class 'sympy.core.numbers.Integer'>
print(b, type(b))
3.2. Рациональные числа, арифметика и точность
Рациональные числа удобно задавать как дроби: \(\frac{p}{q}\), где \(p\) и \(q\) — целые, \(q\ne 0\). В обычном Python для “точной дроби” нужно отдельное решение, а SymPy даёт это прямо из коробки через Rational.
import sympy as sp
c = sp.Rational(3, 7)
print(c, type(c)) # 3/7
print(c**2) # 9/49 (и числитель, и знаменатель возводятся в квадрат)
В SymPy есть и известные константы, например \(\pi\) и золотое сечение. Если нужно получить численное приближение, используют float(...) или sp.N(...).
import sympy as sp
print("Pi (sym):", sp.pi)
print("Pi (float):", float(sp.pi))
print("Golden ratio (sym):", sp.GoldenRatio)
print("Golden ratio (float):", float(sp.GoldenRatio))
# Более контролируемо:
print(sp.N(sp.pi, 50)) # 50 знаков
float переходите тогда, когда вам действительно нужно приблизительное число (например, для численного вывода или графика).3.3. Типичные ошибки
Ошибка 1: “сразу всё превратить в float”
Если вы делаете float(sp.Rational(1, 3)), вы превращаете точное значение \(\frac{1}{3}\) в приближённое число. После этого SymPy уже не сможет “восстановить” точность, и в дальнейших преобразованиях могут появиться округления.
Ошибка 2: ожидать, что Float ведёт себя как точная дробь
sp.Float — это приближённое число, как и float в Python. Оно удобно, но это не “точная рациональная дробь”. Если нужна дробь — используйте Rational.
4. Символы, выражения и подстановки
4.1. Символы (Symbol)
Кроме чисел SymPy работает с математическими объектами, где есть неизвестные — переменные и параметры. В обычном Python выражение без конкретных чисел “не живёт” само по себе: интерпретатор должен всё посчитать. SymPy, наоборот, умеет хранить выражение как объект, даже если в нём есть неизвестные.
Базовый “кирпичик” для неизвестной переменной — это символ, который создаётся через Symbol.
import sympy as sp
x = sp.Symbol('x')
y = sp.Symbol('y')
print(x, type(x)) # x <class 'sympy.core.symbol.Symbol'>
print(y, type(y)) # y <class 'sympy.core.symbol.Symbol'>
4.2. Выражения и преобразования
Из символов можно собирать выражения. SymPy старается интерпретировать их “по-математически”. Например, произведение одинаковых скобок естественно преобразуется в квадрат.
import sympy as sp
x, y = sp.symbols('x y')
expr = (x - y) * (x - y)
print(expr) # (x - y)**2
expanded = sp.expand(expr)
print(expanded) # x**2 - 2*x*y + y**2
Внутренних типов у выражений много (сумма, произведение, степень и т.д.). Для нас ключевая идея такая: выражение — это объект SymPy, который можно передавать в функции библиотеки. При необходимости можно явно привести к “общему типу выражений” через Expr.
import sympy as sp
x, y = sp.symbols('x y')
e = sp.Expr(x*y)
print(e, type(e)) # Expr(x*y) <class 'sympy.core.expr.Expr'>
Ещё один полезный момент: SymPy умеет превращать выражение в LaTeX‑строку. Это удобно для красивого вывода в конспектах и ноутбуках.
import sympy as sp
x = sp.Symbol('x')
expr = (x + 1)**3
print(sp.latex(expr)) # \left(x + 1\right)^{3}
4.3. Подстановка значений (subs) и замены (replace)
Когда выражение содержит символы, мы часто хотим подставить конкретные значения или заменить часть выражения на другую часть. Для этого обычно используют:
subs— подстановка (замена символа/подвыражения на значение);replace— более общий механизм замены выражения на выражение по правилу.
import sympy as sp
x, y = sp.symbols('x y')
expr = x**2 + 2*x*y + y**2 # (x+y)^2
# Подставим конкретные значения:
val = expr.subs({x: 3, y: -1})
print(val) # 4
print(int(val)) # 4
# Замена подвыражения на другое:
expr2 = expr.replace(x**2, 10)
print(expr2) # 2*x*y + y**2 + 10
subs — это “подставить значения/выражения”, а replace — это “заменить по правилу”. Иногда обе операции дают похожий вид результата, но логика у них разная.4.4. Типичные ошибки
Ошибка 1: использовать math вместо sympy для символьных выражений
Функции из модуля math работают с числами, а не с символами. Если вы напишете math.sin(x), где x — символ SymPy, будет ошибка. Для символики используйте sp.sin(x), sp.log(x) и т.д.
Ошибка 2: путать “равенство Python” и “уравнение SymPy”
Запись x == 2 в Python — это булево сравнение. Для уравнения в SymPy используйте sp.Eq(левая_часть, правая_часть). Это особенно важно перед решением через solve или nsolve.
5. Уравнения: solve и nsolve
5.1. Как задавать уравнение
Уравнение — это два выражения, соединённые знаком равенства. В SymPy для этого есть класс‑обёртка Eq.
import sympy as sp
x = sp.Symbol('x')
eq = sp.Eq(x**2 - x - 1, 0)
print(eq)
5.2. Аналитическое решение (solve)
Если уравнение относится к типам, для которых в SymPy реализованы алгоритмы аналитического решения, можно использовать solve. Тогда результатом будет список корней.
import sympy as sp
x = sp.Symbol('x')
eq = sp.Eq(x**2 - x - 1, 0)
roots = sp.solve(eq, x)
print(roots) # [-sqrt(5)/2 + 1/2, sqrt(5)/2 + 1/2]
Но важно помнить: далеко не для всех уравнений SymPy умеет выдавать точную формулу решения. Для некоторых выражений (особенно с несколькими “генераторами” вроде \(x\) и \(\sin(x)\) одновременно) может не быть реализованного алгоритма.
import sympy as sp
x = sp.Symbol('x')
eq2 = sp.Eq(sp.sin(x), x**2)
try:
print(sp.solve(eq2, x))
except NotImplementedError as e:
print("NotImplementedError:", e)
# NotImplementedError: multiple generators [x, sin(x)]
# No algorithms are implemented to solve equation -x**2 + sin(x)
5.3. Численное решение (nsolve) и стартовая точка
Даже если аналитическое решение недоступно, часто можно найти корни численно. Для этого используют nsolve. Принцип важный: численный метод обычно требует стартовую точку, с которой начинается поиск.
import sympy as sp
x = sp.Symbol('x')
eq2 = sp.Eq(sp.sin(x), x**2)
r1 = sp.nsolve(eq2, -1) # стартуем слева
r2 = sp.nsolve(eq2, 1) # стартуем справа
print(float(r1)) # -2.8548207047493545e-55
print(float(r2)) # 0.8767262153950625
Разные стартовые точки могут привести к разным найденным корням (если уравнение имеет несколько решений). Это нормальная ситуация для численных методов: вы фактически “подводите” алгоритм к нужному корню.
5.4. Типичные ошибки
Ошибка 1: забыть указать переменную в solve
Для простых случаев SymPy иногда “догадается”, но лучше всегда писать явно: sp.solve(eq, x). Это делает код понятнее и снижает шанс неожиданных результатов.
Ошибка 2: ждать от nsolve “все корни сразу”
nsolve обычно находит один корень рядом со стартовой точкой. Если решений несколько, запускайте nsolve с разными стартовыми приближениями и сравнивайте результаты.
6. Дифференцирование, интегрирование, диф. уравнения, ряды и графики
6.1. Дифференцирование: diff
Любое выражение SymPy зависит от некоторых символов. Если рассматривать его как функцию от этих символов, можно применять дифференцирование. Для этого используется sp.diff.
Важно: нужно явно указывать переменную дифференцирования, особенно если переменных несколько.
import sympy as sp
x, y = sp.symbols('x y')
expr = x**2 + x*y + y**2
dx = sp.diff(expr, x)
dy = sp.diff(expr, y)
print(dx) # 2*x + y
print(dy) # x + 2*y
Производные высших порядков можно получать, передавая кортеж (переменная, порядок). Например, вторая производная по \(y\):
import sympy as sp
x, y = sp.symbols('x y')
expr = x**2 + x*y + y**2
d2y = sp.diff(expr, (y, 2))
print(d2y) # 2
6.2. Интегрирование: integrate
Интеграл, как мы вспоминали ранее, бывает двух основных типов: неопределённый (возвращает выражение‑первообразную с точностью до константы) и определённый (даёт число, соответствующее площади/сумме на интервале).
В SymPy для интегрирования используют sp.integrate. Для неопределённого интеграла достаточно указать выражение и переменную.
import sympy as sp
x = sp.Symbol('x')
expr = 3*x**2 - 3*x
F = sp.integrate(expr, x)
print(F) # x**3 - 3*x**2/2
Для определённого интеграла передают кортеж (переменная, левая граница, правая граница).
import sympy as sp
x = sp.Symbol('x')
expr = x**2 - 3*x
res = sp.integrate(expr, (x, -2, 4))
print(res) # 6
integrate умеет и неопределённые, и определённые интегралы. Если вы видите, что результат — дробь вроде \( \frac{a}{b} \), это хорошая новость: SymPy сохранил точность.6.3. Функции как символы: Function
SymPy умеет создавать не только переменные‑символы, но и “неизвестные функции” — функции как символические объекты. Это нужно, чтобы записывать дифференциальные уравнения, интегральные уравнения и их комбинации.
import sympy as sp
x = sp.Symbol('x')
f = sp.Function('f')
expr = f(x)
print(expr) # f(x)
print(sp.diff(expr, x)) # Derivative(f(x), x)
print(sp.diff(expr, x, 2)) # Derivative(f(x), (x, 2))
6.4. Дифференциальные уравнения: dsolve
Если неизвестная величина — это функция, мы получаем дифференциальное уравнение. Для некоторых типов таких уравнений SymPy умеет находить общее решение через dsolve.
Рассмотрим пример уравнения второго порядка: \[ y''(x) - y(x) = 0. \] Здесь порядок — 2, поэтому в общем решении обычно появляется две произвольные константы.
import sympy as sp
x = sp.Symbol('x')
y = sp.Function('y')
ode = sp.Eq(sp.diff(y(x), x, 2) - y(x), 0)
sol = sp.dsolve(ode)
print(sol) # Eq(y(x), C1*exp(-x) + C2*exp(x))
В решениях вы часто увидите константы вида C1, C2. Их количество связано с максимальным порядком производной в уравнении: для уравнения второго порядка обычно появляется две константы.
6.5. Ряды: series и summation
SymPy умеет строить разложения в ряд (например, ряд Тейлора около точки). Обычно используют метод series, указывая переменную, точку разложения и порядок.
import sympy as sp
x = sp.Symbol('x')
expr = sp.sin(x) / x
# Разложение около 0 до членов порядка x^6 (включая O(x^7))
s = sp.series(expr, x, 0, 7)
print(s)
# Если нужно убрать "O(...)":
print(s.removeO())
# 1 - x**2/6 + x**4/120 - x**6/5040 + O(x**7)
# -x**6/5040 + x**4/120 - x**2/6 + 1
Кроме разложений в ряд, часто полезно вычислять суммы рядов. Для этого используют summation.
import sympy as sp
k = sp.Symbol('k', integer=True, positive=True)
S = sp.summation(1 / k**2, (k, 1, sp.oo))
print(S) # pi**2/6
print(sp.N(S)) # 1.64493406684823 <- численное приближение
6.6. Построение графиков
В SymPy есть модуль построения графиков. Он удобен тем, что принимает символьные выражения. На практике чаще всего графики смотрят в Jupyter/IDE, где есть окно вывода. Ниже — базовый пример построения графика без использования изображений в конспекте (код вы запускаете у себя).
import sympy as sp
from sympy.plotting import plot
x = sp.Symbol('x')
p = plot(sp.sin(x), (x, -2*sp.pi, 2*sp.pi), show=False)
p.show()

Если вы строите графики через matplotlib, можно сначала превратить выражение в “обычную функцию” через lambdify. Это мост между символьным миром SymPy и численным миром NumPy/Matplotlib.
import sympy as sp
x = sp.Symbol('x')
expr = sp.sin(x) / (1 + x**2)
f_num = sp.lambdify(x, expr, 'math')
print(f_num(0.5)) # 0.3835404308833624
6.7. Типичные ошибки
Ошибка 1: не указать переменную в diff
Если выражение зависит от нескольких символов, SymPy не должен угадывать, по чему вы дифференцируете. Всегда пишите sp.diff(expr, x) или sp.diff(expr, y).
Ошибка 2: ожидать, что integrate всегда найдёт “красивую первообразную”
SymPy очень мощная, но не всесильная. Иногда первообразная выражается через специальные функции, а иногда SymPy не может найти её в замкнутом виде. В таких случаях приходится менять подход: упрощать выражение, использовать подстановку вручную или переходить к численным методам.
Ошибка 3: путать “функцию как символ” и “функцию как выражение”
y = sp.Function('y') задаёт неизвестную функцию. А выражение вроде sp.sin(x) — это уже конкретная функция, заданная формулой. В дифференциальных уравнениях часто нужна именно неизвестная функция.
Ошибка 4: пытаться “показать график” там, где окружение не поддерживает вывод
В некоторых окружениях (например, в консоли без GUI) plot(...) не сможет отобразить окно. Тогда используйте Jupyter, IDE с поддержкой графиков или экспорт в файл.
7. Практика: типовые задачи (с решениями)
Блок 1: числа и точность
Задача 1: создать точную дробь и выполнить вычисления без округлений
Создайте число 5/12 как Rational и вычислите квадрат. Убедитесь, что ответ тоже рациональный.
import sympy as sp
r = sp.Rational(5, 12)
print(r) # 5/12
print(r**2) # 25/144
print(type(r))
Задача 2: численное приближение с контролем точности
Возьмите pi и выведите 6 знаков после запятой через sp.N.
import sympy as sp
print(sp.N(sp.pi, 7)) # 3.141593
Блок 2: выражения и подстановки
Задача 3: раскрыть скобки и упростить выражение
Постройте (x+2y)^2, раскройте скобки, затем проверьте, что factor возвращает форму квадрата выражения.
import sympy as sp
x, y = sp.symbols('x y')
expr = (x + 2*y)**2
expanded = sp.expand(expr)
factored = sp.factor(expanded)
print("expr:", expr) # expr: (x + 2*y)**2
print("expanded:", expanded) # expanded: x**2 + 4*x*y + 4*y**2
print("factored:", factored) # factored: (x + 2*y)**2
Задача 4: подстановка значений через subs
Подставьте x=1, y=3 в выражение x^2 + 2xy + y^2.
import sympy as sp
x, y = sp.symbols('x y')
expr = x**2 + 2*x*y + y**2
print(expr.subs({x: 1, y: 3})) # 16
Блок 3: производные/интегралы/уравнения
Задача 5: производные по разным переменным
Для f(x, y) = x^2 + xy + y^2 найдите ∂f/∂x и ∂f/∂y.
import sympy as sp
x, y = sp.symbols('x y')
f = x**2 + x*y + y**2
print(sp.diff(f, x)) # 2*x + y
print(sp.diff(f, y)) # x + 2*y
Задача 6: определённый интеграл
Вычислите ∫ от -2 до 4 для (x^2 - 3x) dx.
import sympy as sp
x = sp.Symbol('x')
expr = x**2 - 3*x
print(sp.integrate(expr, (x, -2, 4))) # 6
Задача 7: решить уравнение аналитически и численно
Решите x^2 - x - 1 = 0 через solve. Затем решите sin(x) = x^2 через nsolve с двумя стартовыми точками.
import sympy as sp
x = sp.Symbol('x')
# Аналитическое решение:
eq1 = sp.Eq(x**2 - x - 1, 0)
print(sp.solve(eq1, x))
# Численное решение:
eq2 = sp.Eq(sp.sin(x), x**2)
print(float(sp.nsolve(eq2, -1)))
print(float(sp.nsolve(eq2, 1)))
Блок 4: диф. уравнения/ряды/графики
Задача 8: решить дифференциальное уравнение второго порядка
Найдите общее решение y''(x) - y(x) = 0 через dsolve.
import sympy as sp
x = sp.Symbol('x')
y = sp.Function('y')
ode = sp.Eq(sp.diff(y(x), x, 2) - y(x), 0)
print(sp.dsolve(ode))
Задача 9: получить разложение в ряд
Постройте ряд для sin(x)/x около 0 до порядка 6 (включая O(x^7)), затем удалите O(...).
import sympy as sp
x = sp.Symbol('x')
s = sp.series(sp.sin(x)/x, x, 0, 7)
print(s)
print(s.removeO())
Задача 10: подготовить выражение к построению графика через lambdify
Сделайте численную функцию для sin(x)/(1+x^2) и посчитайте значение в точке x=0.5.
import sympy as sp
x = sp.Symbol('x')
expr = sp.sin(x) / (1 + x**2)
f_num = sp.lambdify(x, expr, 'math')
print(f_num(0.5))
8. Чек‑лист самопроверки знаний
Отметьте пункты, которые вы действительно понимаете и можете применить без подсказок.
| ✓ | Навык | Проверка |
|---|---|---|
| Типы чисел SymPy | Могу объяснить разницу между Integer, Rational и приближёнными числами (Float). |
|
| Рациональные дроби | Могу создать дробь через Rational(p, q) и получить точный результат вычислений. |
|
| Символы | Могу создать переменные через Symbol/symbols и использовать их в выражениях. |
|
| Преобразования выражений | Могу раскрывать скобки (expand), факторизовать (factor), упрощать (simplify). |
|
| Подстановки | Могу подставлять значения в выражение через subs и отличать это от replace. |
|
| Уравнения | Могу правильно задавать уравнение через Eq и решать его через solve, если возможно. |
|
| Численное решение | Могу применять nsolve и понимаю, зачем нужна стартовая точка. |
|
| Дифференцирование | Могу взять производную выражения по нужной переменной через diff, в том числе высшего порядка. |
|
| Интегрирование | Могу вычислять неопределённые и определённые интегралы через integrate. |
|
| Дифференциальные уравнения | Могу задать неизвестную функцию через Function и получить общее решение через dsolve (для поддерживаемых типов уравнений). |
|
| Ряды | Могу получить разложение в ряд через series и интерпретировать остаток \(O(\cdot)\). |
|
| Графики | Могу построить график символьной функции через sympy.plotting или подготовить численную функцию через lambdify. |