기본 콘텐츠로 건너뛰기

벡터와 행렬에 관련된 그림들

sympy 식과 연산

내용

sympy operation

symbols

sympy에서 문자를 심벌로 사용하기 위해서는 사용전에 반드시 정의되어야 합니다. 심벌을 정의하기 위해 symbols()를 사용하는데 여러개의 심벌들을 동시에 정의할 수 있습니다.

from sympy import *
x=symbols('x')
x+1 
x + 1
y, z=symbols('y z')
type(y)
sympy.core.symbol.Symbol

심벌은 문자열로 지정이 가능합니다.

crazy =symbols('unrelated')
crazy+1
unrelated + 1

symbols() 함수에 의해 정의한 일반 객체의 이름으로 사용된 문자와는 구분됩니다. 다음 코드에서 symbol로 정의된 x 즉, sympy 식에 사용된 심벌 x와 2가 할당된 객체 이름의 x와는 구분됨을 나타내고 있습니다.

x= symbols('x') #심벌로 x
expr=x+1
expr
x + 1
x=2 #객체이름으로 x
x + 3
5

sympy에서 생성한 식의 심벌에 특정한 수를 대입하기 위해서는 .subs() 메소드를 적용합니다.

x=symbols('x')
expr=x+1
expr
 x + 1
expr.subs(x, 4)
5

등호 기호

python에서 '='는 이 기호를 기준으로 오른쪽의 값을 왼쪽으로 할당함을 의미합니다. sympy 역시 동일한 의미로 작동됩니다. '같다'의 의미로는 ‘==’를 사용하고 bool형 즉, True, False의 결과를 반환합니다.

x=symbols('x')
expr = x+1
expr==3
False
expr1=expr.subs(x, 2)
expr1
3
expr1==3
True

sympy에서 식(expression)을 생성하는 것은 그 객체를 특정한 이름(공간)에 할당하는 것입니다. 그러므로 x+1=4는 두개의 객체들이 결합된 것입니다. 즉, 이를 코드로 표현하기 위해서는 다음과 같습니다.

eq1=x+1
eq2=4
eq1, eq2
(x + 1, 4)

위 두 식이 같음을 표시해야 합니다. 이러한 표현은 Eq() 함수를 사용합니다.

Eq(eq1, eq2)
x + 1 = 4
Eq(x + 1, 4)
x + 1 = 4

==는 값 뿐만 아니라 sympy객체의 구조 동일성 여부를 판단할 수 있습니다. 다음 코드와 같이 우항과 좌항은 결국 동일한 식이지만 그 구조에서 차이를 보이므로 False가 반환됩니다.

eq1=(x+1)**2 
eq2=x**2+2*x+1
eq1==eq2
False

결과적으로 sympy는 심벌을 사용하여 식을 표현하는 것으로 이들을 평가하기 전까지는 단지 문자형일 뿐입니다. 이것은 eq1, eq2 두 객체의 연산에 의해 확인 할 수 있습니다.

eq1-eq2
$\quad \color{navy}{- x^{2} - 2 x + \left(x + 1\right)^{2} - 1}$

위 결과는 정리되지 않은 상태입니다. 이를 정리하여 즉, 연산하기 위해 simplify()함수를 적용합니다. 결과적으로 이 함수는 전개된 식을 연산하여 간단하게 나타내기 위해 사용할 수 있습니다.

simplify(eq1-eq2)
0

또한 이 두 식이 명시적으로 같음을 나타내기 위해서 Eq() 함수를 사용할 수 있습니다.

Eq(eq1, eq2)
$\quad \color{navy}{\left(x + 1\right)^{2} = x^{2} + 2 x + 1}$

위에서 ==는 sympy의 식들은 평가하기 전에 symbol을 문자로 인식하므로 eq1, eq2가 다름으로 결정하였습니다. 그러나 이들을 계산 가능한 symbol로 간주하여 판단하는 equals() 메소드를 사용하면 올바른 결과가 반환됩니다.

eq1.equals(eq2)
True
a=cos(x)**2-sin(x)**2
b=cos(2*x)
simplify(a-b)
0
a==b
False
a.equals(b)
True

^, &, 그리고 /

^은 배타적 논리(Xor()) 연산을 실행합니다.

a=[True, False]
[f"{i} ^ {j}: {i ^ j}" for i in a for j in a]
['True ^ True: False',
     'True ^ False: True',
     'False ^ True: True',
     'False ^ False: False']
[f"{i} ^ {j}: {Xor(i,j)}" for i in a for j in a]
['True ^ True: False',
     'True ^ False: True',
     'False ^ True: True',
     'False ^ False: False']

/는 나눗셈을 수행하는 연산을 표현하는 것으로 일반적인 python에서와 같습니다. sympy에서는 두수의 나눗셈을 기호화 하기 위해 Rational(분자, 분모) 또는 Rational(분자/분모) 메소드를 사용합니다. 이 메소드의 경우 분수형태로 결과를 반환합니다.

1/2
0.5
Rational(1, 2)
$\quad \color{navy}{\frac{1}{2}}$
Rational('1/2')
$\quad \color{navy}{\frac{1}{2}}$
x+Rational(1/2)
$\quad \color{navy}{x + \frac{1}{2}}$

기본 연산

대체(substitution)

기호화된 식을 연산하기 위해서는 먼저 심벌에 어떤 값을 부여해야 합니다. 이것을 위해서 subs(변수, 값) 메소드를 사용합니다.

x, y, z=symbols('x y z')
expr=cos(x)+1
expr
$\quad \color{navy}{\cos{\left(x \right)} + 1}$
expr.subs(x, y)
$\quad \color{navy}{\cos{\left(y \right)} + 1}$
expr.subs(x, 30)
$\quad \color{navy}{\cos{\left(30 \right)} + 1}$

subs() 메소드의 특성은 다음과 같습니다.

1) 식을 평가하기 위해 사용합니다. 다음의 경우는 x에 0을 지정함으로서 식을 계산합니다.

expr.subs(x, 0)
$\quad \color{navy}{2}$

2) 어떤 식을 다른 식으로 대체하기 위해 사용합니다.

ex=x**y
ex
$\quad \color{navy}{x^{y}}$
ex=ex.subs(y, x**y)
ex
$\quad \color{navy}{x^{x^{y}}}$

3) 어떤 식을 단순화하기 위해 사용합니다. 예를 들어 sin(2x)+cos(2x)의 식에서 sin(2x)=2sin(x)co(x), cos(2x)=2cos(x)-1로 간단하게 정리될 수 있습니다. 이러한 삼각함수의 변환은 expand_trig()함수를 사용할 수 있지만 이 함수는 전달되는 모든 식을 변환하여 반환합니다.

ex=sin(2*x)+cos(2*x)
ex
$\quad \color{navy}{\sin{\left(2 x \right)} + \cos{\left(2 x \right)}}$
expand_trig(ex)
$\quad \color{navy}{2 \sin{\left(x \right)} \cos{\left(x \right)} + 2 \cos^{2}{\left(x \right)} - 1}$

만약 위 식의 첫번째 항만을 변화하고자 할 경우는 위 함수를 사용할 수 없습니다. 이 경우 subs() 메소드를 사용할 수 있습니다.

ex.subs(sin(2*x), 2*sin(x)*cos(x))
$\quad \color{navy}{2 \sin{\left(x \right)} \cos{\left(x \right)} + \cos{\left(2 x \right)}}$

4)subs() 메소드에 인수를 다음과 같이 전달하여 여러 변수에 대한 대체 연산을 동시에 실시할 수 있습니다.

  • subs([(x1, value1), (x2, vlaue2), …])
  • subs({x1:value1, x2:value2, …})
ex=x**3+4*x*y+-z
ex.subs([(x,2), (y,4), (z, 0)])
$\quad \color{navy}{40}$
ex.subs({x:2, y:4, z:0})
$\quad \color{navy}{40}$

문자열을 sympy 식으로 전환

문자열을 sympy 표현식(expression)으로 전환하기 위해 sympify() 함수를 사용합니다.

str_eq='x**2+3*x-1/2'
str_eq
'x**2+3*x-1/2'
eq=sympify(str_eq)
eq
$\quad \color{navy}{x^{2} + 3 x - \frac{1}{2}}$
eq.subs(x, 2)
$\quad \color{navy}{\frac{19}{2}}$

evalf, N

숫자로 표현된 식을 평가하여 부동소수(floating point)로 나타내기 위해 evalf(유효자리수) 메소드 또는 N(식, 유효자리수) 함수를 사용합니다. 이 메소드와 함수에서 유효자리수를 전달하지 않은 경우 기본값인 15에 대응하는 결과를 반환합니다.

eq=sqrt(8)
eq
$\quad \color{navy}{2 \sqrt{2}}$
eq.evalf(), N(eq)
(2.82842712474619, 2.82842712474619)
pi.evalf(3), N(pi, 3)
(3.14, 3.14)

삼각함수와 같이 특정한 표현식에 값을 전달하여 계산 결과를 유도하기 위해 subs() 메소드를 사용하는 경우 그 결과는 수학적 기호로 나타냅니다. 즉, 모든 경우가 숫자화되어 반환되지는 않습니다. 다음의 경우 cos()에 값을 전달하였지만 결과는 수치가 아닌 cos(value)의 식으로 반환됩니다. 이러한 경우 역시 evalf()함수로 그 값을 평가할 수 있습니다.

eq=cos(2*x)
eq.subs(x, 30)
$\quad \color{navy}{\cos{\left(60 \right)}}$
eq.subs(x, 30).evalf(3), N(eq.subs(x, 30), 3)
(-0.952, -0.952)

evalf() 함수와 subs() 메소드를 같이 사용하여 연산을 수행할 수 있습니다. 이 경우 전달하는 인수는 사전(dictionary)형식을 취해야 합니다.

eq.evalf(subs={x: 30})
-0.952412980415156

반환값에 에러항이 포함되는 경우 그 에러항을 제거하기 위해 evalf()메소드 내의 chop 매개변수를 사용합니다.

one=cos(1)**2+sin(1)**2
one.evalf()
$\quad \color{navy}{1.0}$
one-1
$\quad \color{navy}{-1 + \cos^{2}{\left(1 \right)} + \sin^{2}{\left(1 \right)}}$
(one-1).evalf()
$\quad \color{navy}{-4.0 \cdot 10^{-124}}$
(one-1).evalf(chop=True)
$\quad \color{navy}{0}$

lambdify

sympy 표현식에 여러가지의 값을 전달하여 평가하기 위해 eval()과(또는) subs()를 반복문에 적용하여 실행할 수 있습니다.

import numpy as np
import math
a=np.arange(10)
eq=sin(x)
[N(eq.evalf(subs={x:i}), 3) for i in a]
[0, 0.841, 0.909, 0.141, -0.757, -0.959, -0.279, 0.657, 0.989, 0.412]

위 방법외에 eq(sympy 식)를 함수로 만들어 사용할 수도 있습니다. 이 변환은 lambdify(식의 심벌, 식, 연산을 위해 사용하는 모듈이름) 함수를 사용합니다.

다음 코드는 sympy 식 eq를 numpy 타입의 함수로 변환한 것입니다.

f=lambdify(x, eq, "numpy")
np.around(f(a), 3)
array([ 0.   ,  0.841,  0.909,  0.141, -0.757, -0.959, -0.279,  0.657,
            0.989,  0.412])

위 식 eq를 math 모듈을 사용하기 위해서는 다음과 같이 lambdify()에서 모듈의 이름을 변경하면 됩니다.

f=lambdify(x, eq, "math")
round(f(0.2), 3)
0.199

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같...

[sympy] Sympy객체의 표현을 위한 함수들

Sympy객체의 표현을 위한 함수들 General simplify(x): 식 x(sympy 객체)를 간단히 정리 합니다. import numpy as np from sympy import * x=symbols("x") a=sin(x)**2+cos(x)**2 a $\sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$ simplify(a) 1 simplify(b) $\frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}$ simplify(b) x - 1 c=gamma(x)/gamma(x-2) c $\frac{\Gamma\left(x\right)}{\Gamma\left(x - 2\right)}$ simplify(c) $\displaystyle \left(x - 2\right) \left(x - 1\right)$ 위의 예들 중 객체 c의 감마함수(gamma(x))는 확률분포 등 여러 부분에서 사용되는 표현식으로 다음과 같이 정의 됩니다. 감마함수는 음이 아닌 정수를 제외한 모든 수에서 정의됩니다. 식 1과 같이 자연수에서 감마함수는 factorial(!), 부동소수(양의 실수)인 경우 적분을 적용하여 계산합니다. $$\tag{식 1}\Gamma(n) =\begin{cases}(n-1)!& n:\text{자연수}\\\int^\infty_0x^{n-1}e^{-x}\,dx& n:\text{부동소수}\end{cases}$$ x=symbols('x') gamma(x).subs(x,4) $\displaystyle 6$ factorial 계산은 math.factorial() 함수를 사용할 수 있습니다. import math math.factorial(3) 6 a=gamma(x).subs(x,4.5) a.evalf(3) 11.6 simpilfy() 함수의 알고리즘은 식에서 공통사항을 찾아 정리하...

sympy.solvers로 방정식해 구하기

sympy.solvers로 방정식해 구하기 대수 방정식을 해를 계산하기 위해 다음 함수를 사용합니다. sympy.solvers.solve(f, *symbols, **flags) f=0, 즉 동차방정식에 대해 지정한 변수의 해를 계산 f : 식 또는 함수 symbols: 식의 해를 계산하기 위한 변수, 변수가 하나인 경우는 생략가능(자동으로 인식) flags: 계산 또는 결과의 방식을 지정하기 위한 인수들 dict=True: {x:3, y:1}같이 사전형식, 기본값 = False set=True :{(x,3),(y,1)}같이 집합형식, 기본값 = False ratioal=True : 실수를 유리수로 반환, 기본값 = False positive=True: 해들 중에 양수만을 반환, 기본값 = False 예 $x^2=1$의 해를 결정합니다. solve() 함수에 적용하기 위해서는 다음과 같이 식의 한쪽이 0이 되는 형태인 동차식으로 구성되어야 합니다. $$x^2-1=0$$ import numpy as np from sympy import * x = symbols('x') solve(x**2-1, x) [-1, 1] 위 식은 계산 과정은 다음과 같습니다. $$\begin{aligned}x^2-1=0 \rightarrow (x+1)(x-1)=0 \\ x=1 \; \text{or}\; -1\end{aligned}$$ 예 $x^4=1$의 해를 결정합니다. solve() 함수의 인수 set=True를 지정하였으므로 결과는 집합(set)형으로 반환됩니다. eq=x**4-1 solve(eq, set=True) ([x], {(-1,), (-I,), (1,), (I,)}) 위의 경우 I는 복소수입니다.즉 위 결과의 과정은 다음과 같습니다. $$x^4-1=(x^2+1)(x+1)(x-1)=0 \rightarrow x=\pm \sqrt{-1}, \; \pm 1=\pm i,\; \pm1$$ 실수...