줄리아의 심볼릭 연산 패키지 Symbolics.jl 소개
개요
줄리아에서 심볼릭 대수 체계symbolic algebra system를 지원하는 패키지인 Symbolics.jl
에 대해 소개한다1. 이 패키지는 특히 줄리아의 기본 문법과 더불어 대단히 직관적이고 강력한 인터페이스를 제공한다.
SymEngine.jl
과의 차이점
Symbolics.jl
는 네이티브하게 줄리아로 구현되어 있으며 성능적으로나 인터페이스로나 더 좋은 측면이 많다. 줄리아에서 심볼릭 연산 하는 방법이라는 포스트에서 소개된 SymEngine.jl
는 원래 C++로 작성된 라이브러리를 줄리아로 래핑한 것이다. 래핑인만큼 당시에는 별도의 기능 개발 없이 즉전감이어서 쓸만했겠지만 지금 와서는 원래 SymEngine
를 익숙하게 써오던 입장이 아니라면 굳이 SymEngine.jl
를 쓸 이유는 없다.
코드
변수 선언 @variables
julia> using Symbolics
julia> @variables t x y z
4-element Vector{Num}:
t
x
y
z
julia> w = log(y) + cos(x) + 2t^3
log(y) + cos(x) + 2(t^3)
심볼릭하게 사용할 변수들은 @variables
뒤에 나열하는 식으로 선언할 수 있다. 이 변수들은 자연스럽게 초월 함수의 인수 등으로도 사용할 수 있다.
전개 expand
julia> (x + y)^3
(x + y)^3
julia> expand((x + y)^3)
x^3 + 3(x^2)*y + 3x*(y^2) + y^3
expand
함수를 통해 전개를 할 수 있다. 함수를 취하지 않으면 성능 상의 이유 등으로 굳이 시키지도 않은 전개를 하지는 않는다.
축약 simplify
julia> simplify(2x + (cos(x))^2 + 3x + (sin(x))^2)
1 + 5x
simplify
함수를 통해 0이 될 항을 제거하거나 계수끼리 더해서 간단하게 만들 수 있다. 예시에서 볼 수 있듯 삼각함수의 제곱의 합은 1이 되는 것을 알아서 계산해내기도 한다.
치환과 대입 substitute
substitute
함수를 통해 변수를 치환하거나 대입할 수 있다. 어떻게 치환이 이루어지는지는 딕셔너리를 만들어서 넣어주면 된다.
$$
w = \log y + \cos x + 2t^3
$$
이 $w$ 에 치환과 대입을 해보자.
julia> substitute(w, Dict(t => x))
log(y) + cos(x) + 2(x^3)
심볼릭 변수끼리의 치환은 당연히 가능하다.
julia> substitute(w, Dict(t => 2, x => π, y => exp(1)))
16.0
$t = 2$, $x = \pi$, $y = e$ 로 대입한 결과는 수치적으로 계산해서 리턴된다.
미분 Differential
julia> Dt = Differential(t)
(::Differential) (generic function with 3 methods)
julia> Dt(w)
Differential(t)(log(y) + cos(x) + 2(t^3))
julia> expand_derivatives(Dt(w))
6(t^2)
특이하게도 Symbolics
에서는 직접 미분하는 함수에 인수로써 적분할 변수를 전달하지 않고 특정 변수에 대한 미분을 하는 미분 작용소을 정의하고 여기에 심볼을 전달하는 방식으로 미분을 한다. 언뜻 보면 코드가 지저분해지고 복잡한 것 같지만 수학자의 관점으로 보면 아주 직관적이고 타당한 인터페이스라고 할 수 있다. 미분한 결과는 전개와 마찬가지로 바로 계산되지 않고 게으르게lazy 기다리다가 expand_derivatives
함수를 통해 결과가 리턴된다.
julia> Dx = Differential(x)
(::Differential) (generic function with 3 methods)
julia> Dy = Differential(y)
(::Differential) (generic function with 3 methods)
julia> Dx(w) |> expand_derivatives
-sin(x)
julia> Dy(w) |> expand_derivatives
1 / y
작용소를 정의한다는 관점에서 봤을 때 편미분 역시 아주 자연스럽게 구현된다.
벡터와 행렬
julia> @variables σ ρ β
3-element Vector{Num}:
σ
ρ
β
julia> Lorenz = [-σ*x + σ*y
-x*y + ρ*x - y
x*y - β*z]
3-element Vector{Num}:
-x*σ + y*σ
-y - x*y + x*ρ
x*y - z*β
줄리아 문법 그대로 벡터를 만들면 된다. 예로써 로렌츠 어트랙터의 우변도 위와 같이 간단히 심볼로 표현할 수 있다.
julia> M = [x*y y^2
2x 9]
2×2 Matrix{Num}:
x*y y^2
2x 9
행렬 역시 매우 직관적으로 정의된다.
자코비안
julia> Symbolics.jacobian(Lorenz, [x, y, z])
3×3 Matrix{Num}:
-σ σ 0
-y + ρ -1 - x 0
y x -β
자코비안 행렬은 Symbolics.jacobian
를 통해 간단히 구할 수 있다.
전체 코드
using Symbolics
@variables t x y z
w = log(y) + cos(x) + 2t^3
(x + y)^3
expand((x + y)^3)
simplify(2x + (cos(x))^2 + 3x + (sin(x))^2)
substitute(w, Dict(t => x))
substitute(w, Dict(t => 2, x => π, y => exp(1)))
Dt = Differential(t)
Dt(w)
expand_derivatives(Dt(w))
Dx = Differential(x)
Dy = Differential(y)
Dx(w) |> expand_derivatives
Dy(w) |> expand_derivatives
@variables σ ρ β
Lorenz = [-σ*x + σ*y
-x*y + ρ*x - y
x*y - β*z]
M = [x*y y^2
2x 9]
Symbolics.jacobian(Lorenz, [x, y, z])
환경
- OS: Windows
- julia: v1.10.0
- Symbolics v5.16.1