import dezero as dz
from dezero.functions import Square, Exp
import numpy as np


def numerical_diff(f, x, eps=1e-4):
    """p.58에 나오는 수치 미분 함수. 중앙 차분으로 구현

    컴퓨터는 극한을 취급할 수 없어서 극한과 비슷한, 아주 작은 값으로 극한값을 대체하여 미분 연산에 대신 사용.

    수치 미분의 결과에는 자릿수 누락 때문에 오차가 포함되기 쉽다는 문제가 있으며,
    더 큰 문제로는 개산량이 매우 많다는 점이 있음.

    :param eps: epsilon의 약어. 아주 작은 양의 부동소수점값을 담는 변수.
    """
    x0 = dz.Variable(x.data - eps)
    x1 = dz.Variable(x.data + eps)
    y0 = f(x0)
    y1 = f(x1)
    return (y1.data - y0.data) / (2 * eps)


f = Square()
x1 = dz.Variable(np.array(2.0))
dy = numerical_diff(f, x1)
print(dy)

# 예상과 달리 eps가 더 작아지니 오차가 더 커짐

dy_1e6 = numerical_diff(f, x1, eps=1e-6)
print(dy_1e6)

dy_1e9 = numerical_diff(f, x1, eps=1e-9)
print(dy_1e9)

print("--------------------")


def f(x):
    """p.59에 나오는 임의의 연산 함수 """
    A = Square()
    B = Exp()
    C = Square()
    return C(B(A(x)))

x2 = dz.Variable(np.array(0.5))
dy2 = numerical_diff(f, x2)
print(dy2)
