def test_logistic():
    x = ad.Scalar('x', 8)
    y = ad.logistic(x)
    assert (np.isclose(y.getValue(), 1 / (1 + np.exp(-8))))
    assert (np.isclose(y.getDeriv()['x'],
                       (1 / (1 + np.exp(-8))) * (1 - 1 / (1 + np.exp(-8)))))

    x = ad.Scalar('x', 0)
    y = ad.logistic(x)
    assert (np.isclose(y.getValue(), 0.5))
    assert (np.isclose(y.getDeriv()['x'], (0.25)))

    x = ad.Scalar('x', 5)
    y = ad.Scalar('y', 2)
    z = ad.logistic(x * y)
    assert (np.isclose(z.getValue(), 1 / (1 + np.exp(-10))))
    assert (np.isclose(
        z.getDeriv()['x'],
        2 * (1 / (1 + np.exp(-10))) * (1 - 1 / (1 + np.exp(-10)))))
    assert (np.isclose(
        z.getDeriv()['y'],
        5 * (1 / (1 + np.exp(-10))) * (1 - 1 / (1 + np.exp(-10)))))

    x = ad.Scalar('x', 8)
    y = ad.logistic(2 * x)
    assert (np.isclose(y.getValue(), 1 / (1 + np.exp(-16))))
    assert (np.isclose(
        y.getDeriv()['x'],
        2 * (1 / (1 + np.exp(-16))) * (1 - 1 / (1 + np.exp(-16)))))

    assert (np.isclose(ad.logistic(0), 0.5))
def test_truediv():
    x, y = ad.Scalar('x', 3), ad.Scalar('y', 2)
    val = x / y
    assert (val.getValue() == 1.5)
    assert (val.getDeriv()['x'] == 0.5)
    assert (val.getDeriv()['y'] == -0.75)

    x, y = ad.Scalar('x', 3.0), ad.Scalar('y', 2.0)
    val = x / y
    assert (val.getValue() == 1.5)
    assert (val.getDeriv()['x'] == 0.5)
    assert (val.getDeriv()['y'] == -0.75)

    x, y = ad.Scalar('x', 0), ad.Scalar('y', 8)
    val = x / y
    assert (val.getValue() == 0.0)
    assert (val.getDeriv()['x'] == 0.125)
    assert (val.getDeriv()['y'] == -0.0)

    x = ad.Scalar('x', 0)
    val = x / 8
    assert (val.getValue() == 0.0)
    assert (val.getDeriv()['x'] == 0.125)

    x = ad.Scalar('x', 3)
    with pytest.raises(ZeroDivisionError):
        x / 0.0

    x = ad.Scalar('x', 3)
    with pytest.raises(ZeroDivisionError):
        x / ad.Scalar('z', 0.0)

    with pytest.raises(TypeError):
        x / "0"
def test_sqrt():
    #basic test for sqrt function
    b = ad.Scalar('b', 4)
    b_sqrt = ad.sqrt(b)
    assert (b_sqrt.getValue() == 2.0)
    #4**(0.5) = 2
    assert (np.isclose(b_sqrt.getDeriv()['b'], 0.5 * 1 / np.sqrt(4)))

    #harder test for sqrt function
    c = ad.Scalar('c', 11.27)
    c_sqrt = ad.sqrt(c)
    assert (c_sqrt.getValue() == np.sqrt(11.27))
    #4**(0.5) = 2
    assert (np.isclose(c_sqrt.getDeriv()['c'], 0.5 * 1 / np.sqrt(11.27)))

    #test that sqrt works with ints/floats
    sqrt_six = ad.sqrt(6.5)
    assert (sqrt_six == np.sqrt(6.5))

    #test that sqrt throws error for negative value
    with pytest.raises(TypeError):
        ad.sqrt(-1)

    #test that proper error is thrown for sqrt of Scalar=0
    with pytest.raises(ZeroDivisionError):
        z = ad.Scalar('z', 0)
        z_sqrt = ad.sqrt(z)

    #test that sqrt function works with integer 0
    assert (ad.sqrt(0) == np.sqrt(0))
def test_rpow():
    v1 = ad.create_vector('v', [2, 5])
    v2 = 2 ** v1
    assert(v2[0].getValue() == 4)
    assert(v2[1].getValue() == 32)
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * 4, 0], [0, np.log(2) * 32]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)
    v1 = np.array([x, y])
    v2 = 2 ** v1
    assert(v2[0].getValue() == 4)
    assert(v2[1].getValue() == 32)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * 4, 0], [0, np.log(2) * 32]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x, y])
    v2 = 2 ** (2 * v1)
    assert(v2[0].getValue() == 16)
    assert(v2[1].getValue() == 64)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * 32, 0], [0, np.log(2) * 128]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x, y])
    v2 = (2 ** 2) ** v1
    assert(v2[0].getValue() == 16)
    assert(v2[1].getValue() == 64)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * (2 ** 4) * 2, 0], [0, np.log(2) * (2 ** 6) * 2]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x + y, x])
    v2 = (2 ** 2) ** v1
    assert(v2[0].getValue() == 2 ** 10)
    assert(v2[1].getValue() == 16)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * (2 ** 10) * 2, np.log(2) * (2 ** 10) * 2], [np.log(2) * (2 ** 4) * 2, 0]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x + y, x])
    v2 = 2 ** (2 * v1)
    assert(v2[0].getValue() == 2 ** 10)
    assert(v2[1].getValue() == 16)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.log(2) * (2 ** 10) * 2, np.log(2) * (2 ** 10) * 2], [np.log(2) * (2 ** 4) * 2, 0]])))
def test_imul():
    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)

    x *= y
    assert (x.getValue() == 10)
    assert (x.getDeriv()['x'] == 5)
    assert (x.getDeriv()['y'] == 2)

    x *= -3
    assert (x.getValue() == -30)
    assert (x.getDeriv()['x'] == -15)
    assert (x.getDeriv()['y'] == -6)
def test_isub():
    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)

    x -= y
    assert (x.getValue() == -3)
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == -1)

    x -= 8
    assert (x.getValue() == -11)
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == -1)
def test_sinh():
    x = ad.sinh(ad.Scalar('x', 0))
    assert (np.isclose(x.getValue(), 0))
    assert (np.isclose(x.getDeriv()['x'], 1.0))

    x = ad.sinh(ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.sinh(10)))
    assert (np.isclose(x.getDeriv()['x'], np.cosh(10)))

    x = ad.sinh(2 * ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.sinh(20)))
    assert (np.isclose(x.getDeriv()['x'], 2 * np.cosh(20)))

    assert (np.isclose(ad.sinh(123), np.sinh(123)))
def test_tanh():
    x = ad.tanh(ad.Scalar('x', 0))
    assert (np.isclose(x.getValue(), 0))
    assert (np.isclose(x.getDeriv()['x'], 1))

    x = ad.tanh(ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.tanh(10)))
    assert (np.isclose(x.getDeriv()['x'], 1 - np.tanh(10)**2))

    x = ad.tanh(2 * ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.tanh(20)))
    assert (np.isclose(x.getDeriv()['x'], 2 * (1 - np.tanh(20)**2)))

    assert (np.isclose(ad.tanh(123), np.tanh(123)))
def test_log():
    x = ad.log(ad.Scalar('x', 10), 10)
    assert (np.isclose(x.getValue(), 1))
    assert (np.isclose(x.getDeriv()['x'], 1 / (np.log(10) * 10)))

    x = ad.log(2 * ad.Scalar('x', 10), 10)
    assert (np.isclose(x.getValue(), np.log(20) / np.log(10)))
    assert (np.isclose(x.getDeriv()['x'], 1 / (np.log(10) * 10)))

    x = ad.log(ad.Scalar('x', 10), 10)
    x2 = ad.log(ad.Scalar('x', 100), 10)
    x3 = x - x2
    x4 = ad.log(ad.Scalar('x', 10) / ad.Scalar('x', 100), 10)
    assert (np.isclose(x3.getValue(), -1))
    assert (np.isclose(x3.getDeriv()['x'],
                       1 / (np.log(10) * 10) - 1 / (np.log(10) * 100)))
    assert (np.isclose(x3.getValue(), x4.getValue()))
    assert (np.isclose(x3.getDeriv()['x'], x4.getDeriv()['x']))

    x = ad.ln(ad.Scalar('x', np.e))
    assert (np.isclose(x.getValue(), 1))
    assert (np.isclose(x.getDeriv()['x'], 1 / np.e))

    x = ad.exp(ad.ln(ad.Scalar('x', 10)))
    assert (np.isclose(x.getValue(), 10))
    assert (np.isclose(x.getDeriv()['x'], 1))

    assert (np.isclose(ad.log(100, 10), 2))
def test_rtruediv():

    x, y = ad.Scalar('x', 3), ad.Scalar('y', 2)
    val = x / y
    assert (val.getValue() == 1.5)
    assert (val.getDeriv()['x'] == 0.5)
    assert (val.getDeriv()['y'] == -0.75)

    x, y = ad.Scalar('x', 3.0), ad.Scalar('y', 2.0)
    val = x / y
    assert (val.getValue() == 1.5)
    assert (val.getDeriv()['x'] == 0.5)
    assert (val.getDeriv()['y'] == -0.75)

    y = ad.Scalar('y', 1)
    val = 10 / y
    assert (val.getValue() == 10.0)
    assert (val.getDeriv()['y'] == -10.0)

    y = ad.Scalar('y', 2)
    val = 8 / y
    assert (val.getValue() == 4.0)
    assert (val.getDeriv()['y'] == -2.0)

    y = ad.Scalar('x', 0.0)
    with pytest.raises(ZeroDivisionError):
        3 / y

    z = ad.Scalar('x', 34)
    with pytest.raises(TypeError):
        "3" / z
def test_arccos():
    x = ad.Scalar('x', 0.5)
    y = ad.arccos(x)
    assert (np.isclose(y.getValue(), np.arccos(0.5)))
    assert (np.isclose(y.getDeriv()['x'], -1 / np.sqrt(1 - 0.5**2)))

    y = ad.arccos(-0.1)
    assert (np.isclose(y, np.arccos(-0.1)))

    #test function with two variables
    x = ad.Scalar('x', 0.5)
    y = ad.Scalar('y', -0.2)
    z = ad.arccos(x * y)
    assert (np.isclose(z.getValue(), np.arccos(0.5 * -0.2)))
    assert (np.isclose(z.getDeriv()['x'], -0.2 * -1 / np.sqrt(1 - (-0.1)**2)))
    assert (np.isclose(z.getDeriv()['y'], 0.5 * -1 / np.sqrt(1 - (-0.1)**2)))
def test_arctan():
    x = ad.Scalar('x', 0.5)
    y = ad.arctan(x)
    assert (np.isclose(y.getValue(), np.arctan(0.5)))
    assert (np.isclose(y.getDeriv()['x'], 1 / (1 + 0.5**2)))

    y = ad.arctan(-0.1)
    assert (np.isclose(y, np.arctan(-0.1)))

    #test function with two variables
    x = ad.Scalar('x', 0.5)
    y = ad.Scalar('y', -0.2)
    z = ad.arctan(x * y)
    assert (np.isclose(z.getValue(), np.arctan(0.5 * -0.2)))
    assert (np.isclose(z.getDeriv()['x'], -0.2 * 1 / (1 + (-0.1)**2)))
    assert (np.isclose(z.getDeriv()['y'], 0.5 * 1 / (1 + (-0.1)**2)))
def test_itruediv():
    x = ad.Scalar('x', 3)
    y = ad.Scalar('y', 2)
    x /= y
    assert (x.getValue() == 1.5)
    assert (x.getDeriv()['x'] == 0.5)
    assert (x.getDeriv()['y'] == -0.75)

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)
    x._deriv['x'] = -3
    x /= y
    assert (abs(x.getDeriv()['x'] - (-0.6)) < 1e-6)

    with pytest.raises(TypeError):
        x /= "5"

    with pytest.raises(ZeroDivisionError):
        x /= 0

    with pytest.raises(ZeroDivisionError):
        x /= ad.Scalar('z', 0)

    x = ad.Scalar('x', 3)
    y = ad.Scalar('y', 2)
    y._deriv['y'] = -2
    x /= y
    assert (x.getValue() == 1.5)
    assert (x.getDeriv()['y'] == 1.5)
def test_pow():

    x = ad.Scalar('x', 2)
    val = x**2
    assert (val.getValue() == 4.0)
    assert (val.getDeriv()['x'] == 4.0)

    x = ad.Scalar('x', 2.0)
    val = x**2
    assert (val.getValue() == 4.0)
    assert (val.getDeriv()['x'] == 4.0)

    x = ad.Scalar('x', 1)
    val = x**2
    assert (val.getValue() == 1.0)
    assert (val.getDeriv()['x'] == 2.0)

    x = ad.Scalar('x', -2)
    val = x**2
    assert (np.isclose(val.getValue(), 4.0))
    assert (np.isclose(val.getDeriv()['x'], -4.0))

    x = ad.Scalar('x', -2)
    val = x**3
    assert (np.isclose(val.getValue(), -8.0))
    assert (np.isclose(val.getDeriv()['x'], 12.0))

    #make sure Scalar**Scalar is right
    x = ad.Scalar('x', 4)
    z = ad.Scalar('z', 2.3)
    val = x**z
    assert (np.isclose(val.getValue(), 4**2.3))
    assert (np.isclose(val.getDeriv()['x'], 2.3 * (4**1.3)))
    assert (np.isclose(val.getDeriv()['z'],
                       np.log(4) * 4**2.3))

    x = ad.Scalar('x', 0)
    val = x**2
    assert (val.getValue() == 0.0)
    assert (val.getDeriv()['x'] == 0.0)

    x = ad.Scalar('x', 0)
    with pytest.raises(ZeroDivisionError):
        x**0.8
    with pytest.raises(ZeroDivisionError):
        x**-0.1

    y = x**3.5
    assert (y.getValue() == 0)
    assert (y.getDeriv()['x'] == 0)
def test_add():
    v1 = ad.create_vector('v', [1, 2])
    v2 = ad.create_vector('v', [1, 5])
    v3 = v1 + v2 
    assert(v3[0].getValue() == 2)
    assert(v3[1].getValue() == 7)
    jacobian = ad.get_jacobian(v3, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[2, 0], [0, 2]])))

    v1 = ad.create_vector('v', [1, 2])
    v2 = v1 + 10
    assert(v2[0].getValue() == 11)
    assert(v2[1].getValue() == 12)
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, 1]])))

    v1 = ad.create_vector('v', [1, 2])
    v2 = ad.Scalar('v2', 4)
    v3 = ad.Scalar('v1', 7)
    v4 = v1 + np.array([v2, v3])
    assert(v4[0].getValue() == 5)
    assert(v4[1].getValue() == 9)
    jacobian = ad.get_jacobian(v4, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[1, 1], [1, 1]])))    

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v1 = np.array([x, y])
    v2 = ad.create_vector('v', [1, 5])
    v3 = v1 + v2
    assert(v3[0].getValue() == 2)
    assert(v3[1].getValue() == 7)   
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, 1]])))    

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v1 = np.array([x, y])
    v2 = np.array([x + y, x])
    v3 = v1 + v2
    assert(v3[0].getValue() == 4)
    assert(v3[1].getValue() == 3)   
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[2, 1], [1, 1]])))    

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v1 = np.array([x, y])
    v2 = np.array([y, 10])
    v3 = v1 + v2
    assert(v3[0].getValue() == 3)
    assert(v3[1].getValue() == 12)  
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[1, 1], [0, 1]])))    
def test_sub():

    x = ad.Scalar('x', 1)
    val = x - 5
    assert (val.getValue() == -4.0)
    assert (val.getDeriv()['x'] == 1.0)

    x = ad.Scalar('x', 1.0)
    val = x - 5
    assert (val.getValue() == -4.0)
    assert (val.getDeriv()['x'] == 1.0)

    x = ad.Scalar('x', 0)
    val = x - 3.3
    assert (val.getValue() == -3.3)
    assert (val.getDeriv()['x'] == 1.0)

    with pytest.raises(TypeError):
        x - "5"
def test_cosh():
    x = ad.cosh(ad.Scalar('x', 0))
    assert (np.isclose(x.getValue(), 1))
    assert (np.isclose(x.getDeriv()['x'], 0))

    x = ad.cosh(ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.cosh(10)))
    assert (np.isclose(x.getDeriv()['x'], np.sinh(10)))

    x = ad.cosh(2 * ad.Scalar('x', 10))
    assert (np.isclose(x.getValue(), np.cosh(20)))
    assert (np.isclose(x.getDeriv()['x'], 2 * np.sinh(20)))

    assert (np.isclose(ad.cosh(123), np.cosh(123)))

    x = ad.cosh(ad.Scalar('x', 3))
    x2 = ad.sinh(ad.Scalar('x', 3))
    x3 = x**2 - x2**2
    assert (np.isclose(x3.getValue(), 1))
    assert (np.isclose(x3.getDeriv()['x'], 0))
def test_mul():
    v1 = ad.create_vector('v', [1, 2])
    v2 = ad.create_vector('w', [3, 5])
    v3 = v1 * v2 
    assert(v3[0].getValue() == 3)
    assert(v3[1].getValue() == 10)
    jacobian = ad.get_jacobian(v3, ['v1', 'v2', 'w1', 'w2'])
    assert(np.array_equal(jacobian, np.array([[3, 0, 1, 0], [0, 5, 0, 2]])))    

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v = ad.Scalar('v', 3)
    v1 = np.array([x, y])
    v2 = np.array([v, 3 * v])
    v3 = v1 * v2
    assert(v3[0].getValue() == 3)
    assert(v3[1].getValue() == 18)  
    jacobian = ad.get_jacobian(v3, ['x', 'y', 'v'])
    assert(np.array_equal(jacobian, np.array([[3, 0, 1], [0, 9, 6]])))    


    v1 = ad.create_vector('v', [2, 3])
    v3 = v1 * v1
    assert(v3[0].getValue() == 4)
    assert(v3[1].getValue() == 9)
    jacobian = ad.get_jacobian(v3, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[4, 0], [0, 6]])))    


    v1 = ad.create_vector('v', [1, 2])
    v2 = v1 * 10
    assert(v2[0].getValue() == 10)
    assert(v2[1].getValue() == 20)
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[10, 0], [0, 10]])))    


    x = ad.Scalar('x', 5)
    y = ad.Scalar('y', 2)
    v1 = np.array([x, y])
    v2 = np.array([x * y, (x + y)])
    v3 = v1 * v2
    assert(v3[0].getValue() == 50)
    assert(v3[1].getValue() == 14)   
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[20, 25], [2, 9]])))    

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v1 = np.array([x, y])
    v2 = np.array([y, 10])
    v3 = v1 * v2
    assert(v3[0].getValue() == 2)
    assert(v3[1].getValue() == 20)  
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[2, 1], [0, 10]])))    
def test_cos():

    x = ad.Scalar('x', 2)
    val = ad.cos(x)

    assert (np.isclose(val.getValue(), -0.41614683654))
    assert (np.isclose(val.getDeriv()['x'], -0.9092974268256817))

    x = ad.Scalar('x', 2)
    val = ad.cos(2 * x)
    assert (np.isclose(val.getValue(), np.cos(4)))
    assert (np.isclose(val.getDeriv()['x'], -2 * np.sin(4)))

    x = ad.Scalar('x', -2)
    val = ad.cos(x)

    assert (np.isclose(val.getValue(), -0.4161468365471424))
    assert (np.isclose(val.getDeriv()['x'], 0.9092974268256817))

    x = ad.Scalar('x', 0)
    val = ad.cos(x)

    assert (np.isclose(val.getValue(), 1.0))
    assert (np.isclose(val.getDeriv()['x'], 0.0))

    x = ad.cos(ad.Scalar('x', 100))
    x2 = ad.sin(ad.Scalar('x', 100))
    val = x**2 + x2**2
    assert (np.isclose(val.getValue(), 1))
    assert (np.isclose(val.getDeriv()['x'], 0.0))

    assert (ad.cos(0) == 1)
    assert (ad.cos(13443) == np.cos(13443))
def test_pow():
    v1 = ad.create_vector('v', [2, 5])
    v2 = v1 ** 2
    assert(v2[0].getValue() == 4)
    assert(v2[1].getValue() == 25)
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[4, 0], [0, 10]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)
    v1 = np.array([x, y])
    v2 = v1 ** 2
    assert(v2[0].getValue() == 4)
    assert(v2[1].getValue() == 25)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[4, 0], [0, 10]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x, y])
    v2 = (v1 ** 2) ** 3
    assert(v2[0].getValue() == 64)
    assert(v2[1].getValue() == 729)
    jacobian = ad.get_jacobian(v2, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[6 * (2 ** 5), 0], [0, 6 * (3 ** 5)]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = np.array([x, y])
    v2 = np.array([y, 2])
    v3 = v1 ** v2
    assert(v3[0].getValue() == 8)
    assert(v3[1].getValue() == 9)
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[12, np.log(2) * 8], [0, 6]])))
def test_sin():
    v1 = ad.create_vector('v', [0, 100])
    v2 = ad.sin(v1)
    assert(v2[0].getValue() == 0)
    assert(np.isclose(v2[1].getValue(), np.sin(100)))
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, np.cos(100)]])))

    v1 = ad.Scalar('x', 4)
    v2 = ad.Scalar('y', 10)
    v3 = ad.sin(np.array([v1, v2])) / ad.sin(np.array([v1, v2]))
    assert(np.isclose(v3[0].getValue(), 1))
    assert(np.isclose(v3[1].getValue(), 1))
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.isclose(jacobian, np.array([[0, 0], [0, 0]])).all())

    v1 = ad.Scalar('x', 4)
    v2 = ad.Scalar('y', 10)
    v3 = ad.sin(np.array([v1, v2])) ** 2
    assert(np.isclose(v3[0].getValue(), np.sin(4) ** 2))
    assert(np.isclose(v3[1].getValue(), np.sin(10) ** 2))
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.isclose(jacobian, np.array([[2 * np.sin(4) * np.cos(4), 0], [0, 2 * np.sin(10) * np.cos(10)]])).all())

    v1 = ad.Scalar('x', 4)
    v2 = ad.Scalar('y', 10)
    v3 = ad.sin(np.array([v1 * v2, v1 + v2])) ** 2
    assert(np.isclose(v3[0].getValue(), np.sin(40) ** 2))
    assert(np.isclose(v3[1].getValue(), np.sin(14) ** 2))
    jacobian = ad.get_jacobian(v3, ['x', 'y'])
    assert(np.isclose(jacobian, np.array([[2 * np.sin(40) * np.cos(40)  * 10, 2 * np.sin(40) * np.cos(40)  * 4], 
            [2 * np.sin(14) * np.cos(14), 2 * np.sin(14) * np.cos(14)]])).all())
def test_rsub():

    x = 2
    y = ad.Scalar('y', 5)
    z = x - y
    assert (z.getValue() == -3)
    assert (z.getDeriv()['y'] == -1)

    x, y = ad.Scalar('x', 2.0), ad.Scalar('y', 5.0)
    val = 2 - (x - y)
    assert (val.getValue() == 5.0)
    assert (val.getDeriv()['x'] == -1.0)
    assert (val.getDeriv()['y'] == 1.0)

    y = ad.Scalar('y', 1)
    y._deriv['y'] = -3
    z = -5.2 - y
    assert (z.getValue() == -6.2)
    assert (z.getDeriv()['y'] == 3)

    with pytest.raises(TypeError):
        "3" - y
def test_rmul():
    x = 2
    y = ad.Scalar('y', 5)

    z = x * y
    assert (z.getValue() == 10)
    assert (z.getDeriv()['y'] == 2)

    z = -3 * z
    assert (z.getDeriv()['y'] == -6)

    with pytest.raises(TypeError):
        "8" * z
def test_exp():
    v1 = ad.create_vector('v', [2, 5])
    v2 = ad.exp(v1)
    assert(np.isclose(v2[0].getValue(), np.exp(2)))
    assert(np.isclose(v2[1].getValue(), np.exp(5)))
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[np.exp(2), 0], [0, np.exp(5)]])))

    v1 = ad.create_vector('v', [2, 5])
    v2 = ad.exp(2 * v1)
    assert(np.isclose(v2[0].getValue(), np.exp(4)))
    assert(np.isclose(v2[1].getValue(), np.exp(10)))
    jacobian = ad.get_jacobian(v2, ['v1', 'v2'])
    assert(np.array_equal(jacobian, 2 * np.array([[np.exp(4), 0], [0, np.exp(10)]])))

    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 3)
    v1 = ad.exp(np.array([x + y, x * y]))
    assert(np.isclose(v1[0].getValue(), np.exp(5)))
    assert(np.isclose(v1[1].getValue(), np.exp(6)))
    jacobian = ad.get_jacobian(v1, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[np.exp(5), np.exp(5)], [3 * np.exp(6), 2 * np.exp(6)]])))
def test_radd():

    x = 2
    y = ad.Scalar('y', 5)

    z = x + y
    assert (z.getValue() == 7)
    assert (z.getDeriv()['y'] == 1)

    x, y = ad.Scalar('x', 2.0), ad.Scalar('y', 5.0)
    val = 2 + (x + y)
    assert (val.getValue() == 9.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == 1.0)

    y = ad.Scalar('y', 1)
    y._deriv['y'] = -3
    z = -5.2 + y
    assert (z.getValue() == -4.2)
    assert (z.getDeriv()['y'] == -3)

    with pytest.raises(TypeError):
        "3" + y
def test_iadd():
    x = ad.Scalar('x', 2)
    y = ad.Scalar('y', 5)

    x += y
    assert (x.getValue() == 7)
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == 1)

    y = ad.Scalar('y', 0)
    y._deriv['y'] = -3
    x += y
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == -2)

    x += 8
    assert (x.getValue() == 15)
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == -2)

    x += y + 8
    assert (x.getValue() == 23)
    assert (x.getDeriv()['x'] == 1)
    assert (x.getDeriv()['y'] == -5)
def test_add():

    x = ad.Scalar('x', 10)
    y = x + 2
    assert (y.getValue() == 12)
    assert (y.getDeriv()['x'] == 1.0)

    x = ad.Scalar('x', 10, 212342)
    y = x + 2
    assert (y.getValue() == 12)
    assert (y.getDeriv()['x'] == 212342.0)

    x, y = ad.Scalar('x', 2), ad.Scalar('y', 5)
    val = x + y
    assert (val.getValue() == 7.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == 1.0)

    x, y = ad.Scalar('x', 2.0), ad.Scalar('y', 5.0)
    val = x + y
    assert (val.getValue() == 7.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == 1.0)

    x, y = ad.Scalar('x', 0), ad.Scalar('y', 0)
    val = x + y
    assert (val.getValue() == 0.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == 1.0)

    x, y = ad.Scalar('x', 2), ad.Scalar('y', 5)
    y._deriv['y'] = -3
    val = x + y
    assert (val.getValue() == 7.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == -3.0)
def test_exp():
    x = ad.Scalar('x', 8)
    y = ad.exp(x)
    assert (np.isclose(y.getValue(), np.exp(8)))
    assert (np.isclose(y.getDeriv()['x'], np.exp(8)))

    x = ad.Scalar('x', 8)
    y = ad.exp(2 * x)
    assert (np.isclose(y.getValue(), np.exp(16)))
    assert (np.isclose(y.getDeriv()['x'], 2 * np.exp(16)))

    x = ad.Scalar('x', -3)
    x._deriv['x'] = -2.3
    y = ad.exp(x)
    assert (np.isclose(y.getValue(), np.exp(-3)))
    assert (np.isclose(y.getDeriv()['x'], -2.3 * np.exp(-3)))

    assert (ad.exp(0) == 1)
    assert np.isclose(ad.exp(4), np.exp(4))

    x = ad.Scalar('x', -3)
    y = ad.Scalar('y', 5)
    z = ad.exp(x) * ad.exp(y)
    assert (np.isclose(z.getValue(), np.exp(2)))
    assert (np.isclose(z.getDeriv()['x'], np.exp(2)))
    assert (np.isclose(z.getDeriv()['y'], np.exp(2)))

    x = ad.Scalar('x', -3)
    y = ad.Scalar('y', 5)
    z = ad.exp(x + y)
    assert (np.isclose(z.getValue(), np.exp(2)))
    assert (np.isclose(z.getDeriv()['x'], np.exp(2)))
    assert (np.isclose(z.getDeriv()['y'], np.exp(2)))

    x = ad.Scalar('x', -3)
    y = ad.Scalar('y', 5)
    z = ad.exp(x * y)
    assert (np.isclose(z.getValue(), np.exp(-15)))
    assert (np.isclose(z.getDeriv()['x'], 5 * np.exp(-15)))
    assert (np.isclose(z.getDeriv()['y'], -3 * np.exp(-15)))
def test_create_vector():
    v = ad.create_vector('v', [1, 2])
    assert(v[0].getValue() == 1)
    assert(v[1].getValue() == 2)
    derivs = ad.get_deriv(v) 
    assert(np.array_equal(np.array([deriv.get('v1', 0) for deriv in derivs]), np.array([1, 0])))
    assert(np.array_equal(np.array([deriv.get('v2', 0) for deriv in derivs]), np.array([0, 1])))
    jacobian = ad.get_jacobian(v, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, 1]])))
    jacobian = ad.get_jacobian(v, ['v1', 'v2', 'hello'])
    assert(np.array_equal(jacobian, np.array([[1, 0, 0], [0, 1, 0]])))

    v = ad.create_vector('v', [1, 2], [3, 4])
    assert(v[0].getValue() == 1)
    assert(v[1].getValue() == 2)
    derivs = ad.get_deriv(v) 
    assert(np.array_equal(np.array([deriv.get('v1', 0) for deriv in derivs]), np.array([3, 0])))
    assert(np.array_equal(np.array([deriv.get('v2', 0) for deriv in derivs]), np.array([0, 4])))
    jacobian = ad.get_jacobian(v, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[3, 0], [0, 4]])))
    jacobian = ad.get_jacobian(v, ['v1', 'v2', 'hello'])
    assert(np.array_equal(jacobian, np.array([[3, 0, 0], [0, 4, 0]])))

    with pytest.raises(Exception):    
        v = ad.create_vector('v', [1, 2], [3, 4, 5])
    
    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v = np.array([x, y])
    assert(np.array_equal(ad.get_value(v), np.array([1, 2])))
    jacobian = ad.get_jacobian(v, ['v1', 'v2'])
    assert(np.array_equal(jacobian, np.array([[0, 0], [0, 0]])))
    jacobian = ad.get_jacobian(v, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, 1]])))

    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v = np.array([x, 2 * y])
    assert(np.array_equal(ad.get_value(v), np.array([1, 4])))
    jacobian = ad.get_jacobian(v, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[1, 0], [0, 2]])))
    jacobian = ad.get_jacobian(v, ['y', 'x'])
    assert(np.array_equal(jacobian, np.array([[0, 1], [2, 0]])))
    
    x = ad.Scalar('x', 1)
    y = ad.Scalar('y', 2)
    v = np.array([x + y, 2 * y])
    assert(np.array_equal(ad.get_value(v), np.array([3, 4])))
    jacobian = ad.get_jacobian(v, ['x', 'y'])
    assert(np.array_equal(jacobian, np.array([[1, 1], [0, 2]])))
    jacobian = ad.get_jacobian(v, ['y', 'x'])
    assert(np.array_equal(jacobian, np.array([[1, 1], [2, 0]])))
def test_neg():
    x, y = ad.Scalar('x', 5), ad.Scalar('y', 1)
    val = x - y
    assert (val.getValue() == 4.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == -1.0)

    x, y = ad.Scalar('x', 5.0), ad.Scalar('y', 1.0)
    val = x - y
    assert (val.getValue() == 4.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == -1.0)

    x, y = ad.Scalar('x', 3), ad.Scalar('y', 5)
    val = x - y
    assert (val.getValue() == -2.0)
    assert (val.getDeriv()['x'] == 1.0)
    assert (val.getDeriv()['y'] == -1.0)