Примеры
Разложение exp(x) с помощью taylor_expand()
Функция taylor_expand
принимает в качестве первого аргумента функцию, которую нужно разложить, а в качестве второго — точку, в окрестности которой нужно выполнить разложение. Именованный аргумент order
определяет, до какого порядка будет выполняться разложение:
julia> using TaylorSeries
julia> taylor_expand(exp, 0, order=2)
1.0 + 1.0 t + 0.5 t² + 𝒪(t³)
Готово! Вычислить простой многочлен Тейлора действительно очень просто. Следующий пример несколько сложнее.
Разложение exp(x) с помощью символьного объекта
Альтернативный способ вычислить однопеременное разложение по Тейлору для функции заключается в определении переменной типа Taylor1
и ее использовании в функции, которую нужно разложить. Аргумент, заданный конструктору Taylor1
, — это порядок, до которого нужно выполнить разложение:
julia> using TaylorSeries
julia> x = Taylor1(2)
1.0 t + 𝒪(t³)
julia> exp(x)
1.0 + 1.0 t + 0.5 t² + 𝒪(t³)
Давайте также избавимся от выводимой ошибки для следующих нескольких примеров и зададим выводимой независимой переменной значение x
:
julia> displayBigO(false)
false
julia> set_taylor1_varname("x")
"x"
julia> exp(x)
1.0 + 1.0 x + 0.5 x²
Изменение точки разложения
Переменная, построенная с помощью Taylor1()
, автоматически выполняет разложение в окрестности точки x=0
. Но что если вы хотите использовать символьный объект для разложения в окрестности точки, отличной от нуля? Поскольку разложение exp(x)
в окрестности x=1
точно такое же, как и разложение exp(x+1)
в окрестности x=0
, просто замените x
в выражении на x+1
, чтобы выполнить разложение в x=1
:
julia> p = exp(x+1)
2.718281828459045 + 2.718281828459045 x + 1.3591409142295225 x²
julia> p(0.01)
2.7456005608350584
julia> exp(1.01)
2.7456010150169163
Дополнительные примеры
Вы можете использовать пользовательские функции.
julia> f(a) = 1/(a+1)
f (generic function with 1 method)
julia> f(x)
1.0 - 1.0 x + 1.0 x²
Функции могут быть вложенными
julia> sin(f(x))
0.8414709848078965 - 0.5403023058681398 x + 0.11956681346419151 x²
и далее усложняться по модульному принципу.
julia> sin(exp(x+2))/(x+2)+cos(x+2)+f(x+2)
0.364113974242596 + 0.41259243488717107 x - 11.843864409375039 x²
Тождество четырех квадратов
Первый пример показывает, что тождество четырех квадратов выполняется так:
что первоначально было доказано Эйлером. Код также можно найти в этом тесте пакета.
Сначала мы задаем максимальную степень многочлена равной 4, так как в RHS уравнения априори есть члены четвертого порядка, и определяем 8 независимых переменных.
julia> using TaylorSeries
julia> # Определяем переменные α₁, ..., α₄, β₁, ..., β₄
make_variable(name, index::Int) = string(name, TaylorSeries.subscriptify(index))
make_variable (generic function with 1 method)
julia> variable_names = [make_variable("α", i) for i in 1:4]
4-element Vector{String}:
"α₁"
"α₂"
"α₃"
"α₄"
julia> append!(variable_names, [make_variable("β", i) for i in 1:4])
8-element Vector{String}:
"α₁"
"α₂"
"α₃"
"α₄"
"β₁"
"β₂"
"β₃"
"β₄"
julia> # Создаем переменные TaylorN variables (order=4, numvars=8)
a1, a2, a3, a4, b1, b2, b3, b4 = set_variables(variable_names, order=4)
8-element Vector{TaylorN{Float64}}:
1.0 α₁
1.0 α₂
1.0 α₃
1.0 α₄
1.0 β₁
1.0 β₂
1.0 β₃
1.0 β₄
julia> a1 # переменная a1
1.0 α₁
Теперь вычислим каждый член в уравнении. (\ref{eq:Euler})
julia> # левая часть
lhs1 = a1^2 + a2^2 + a3^2 + a4^2 ;
julia> lhs2 = b1^2 + b2^2 + b3^2 + b4^2 ;
julia> lhs = lhs1 * lhs2;
julia> # правая часть
rhs1 = (a1*b1 - a2*b2 - a3*b3 - a4*b4)^2 ;
julia> rhs2 = (a1*b2 + a2*b1 + a3*b4 - a4*b3)^2 ;
julia> rhs3 = (a1*b3 - a2*b4 + a3*b1 + a4*b2)^2 ;
julia> rhs4 = (a1*b4 + a2*b3 - a3*b2 + a4*b1)^2 ;
julia> rhs = rhs1 + rhs2 + rhs3 + rhs4;
Теперь сравним две части тождества
julia> lhs == rhs
true
Тождество выполнено.
Тест Фейтмана
Ричард Фейтман (Richard J. Fateman) из Беркли в качестве строгой проверки умножения многочленов предложил вычисление , где . Оно реализовано в функции fateman1
ниже. Мы также рассмотрим форму в fateman2
, которая предполагает меньше операций (и более справедливо сравнивает то, что делает система Mathematica).
julia> using TaylorSeries
julia> const order = 20
20
julia> const x, y, z, w = set_variables(Int128, "x", numvars=4, order=2order)
4-element Vector{TaylorN{Int128}}:
1 x₁
1 x₂
1 x₃
1 x₄
julia> function fateman1(degree::Int)
T = Int128
s = one(T) + x + y + z + w
s = s^degree
s * ( s + one(T) )
end
fateman1 (generic function with 1 method)
(В следующих строках, которые запускаются при сборке документации, по какой-то причине значения времени появляются перед выполнением команды.)
julia> @time fateman1(0);
0.040507 seconds (54.66 k allocations: 28.621 MiB, 16.04% gc time, 57.50% compilation time)
julia> @time f1 = fateman1(20);
1.502543 seconds (1.23 k allocations: 29.089 MiB, 0.45% gc time)
Другая реализация того же самого, но с использованием оптимизаций, связанных с ^2
, выдает следующее:
julia> function fateman2(degree::Int)
T = Int128
s = one(T) + x + y + z + w
s = s^degree
s^2 + s
end
fateman2 (generic function with 1 method)
julia> fateman2(0);
julia> @time f2 = fateman2(20); # время появляется выше
0.740009 seconds (1.16 k allocations: 27.014 MiB, 0.90% gc time)
Заметим, что приведенные выше функции используют разложения в Int128
. На самом деле это необходимо, поскольку некоторые коэффициенты больше typemax(Int)
:
julia> getcoeff(f2, (1,6,7,20)) # коэффициент x y^6 z^7 w^{20}
128358585324486316800
julia> ans > typemax(Int)
true
julia> length(f2)
41
julia> sum(TaylorSeries.size_table)
135751
Эти примеры показывают, что fateman2
почти в два раза быстрее, чем fateman1
, и что ряд содержит 135751 одночлен в четырех переменных.
Тесты производительности
Описанные выше функции были сопоставлены с системой Mathematica v11.1. Соответствующие файлы, использовавшиеся для тестов производительности, можно найти здесь. При выполнении на MacPro с процессорами Intel-Xeon 2,7 ГГц мы получили, что системе Mathematica требуется в среднем (5 выполнений) 3,075957 секунды на вычисление, а для fateman1
и fateman2
выше мы получили 2,15408 и 1,08337, соответственно.
Затем, в текущей версии TaylorSeries.jl
и с использованием Julia версии 0.7.0 наша реализация fateman1
примерно на 30—40 % быстрее. (Исходный тест Фейтмана соответствует fateman1
выше, что позволяет избежать некоторых оптимизаций, связанных с возведением в квадрат. Реализация в системе Mathematica выполнена таким образом, что эти оптимизации отсутствуют.)