def test_jacobian():
    g = sad.AutoDiff('g', 3)
    h = sad.AutoDiff('h', -4)
    i = sad.AutoDiff('i', 7)

    # Create functions
    f1 = g + 4
    f2 = h**2 + 2 * h - 12

    variables = ['g', 'h', 'i']
    functions = [f1, f2]
    jacob = sad.jacobian(variables, functions)
    test = np.array([[1., 0., 0.], [0., -6., 0.]])
    for row in range(jacob.shape[0]):
        for col in range(jacob.shape[1]):
            assert jacob[row, col] == pytest.approx(test[row, col])

    functions = sad.AutoDiffVector([f1, f2])
    jacob = sad.jacobian(variables, functions)
    test = np.array([[1., 0., 0.], [0., -6., 0.]])
    for row in range(jacob.shape[0]):
        for col in range(jacob.shape[1]):
            assert jacob[row, col] == pytest.approx(test[row, col])

    with pytest.raises(ValueError):
        sad.jacobian('incorrect', 'input')
def test_eq():
    x1 = sad.AutoDiff('x', 1) * 2
    y1 = sad.AutoDiff('y', 1) * 2
    assert x1 == x1
    assert (x1 == y1) == False
    y1 = sad.AutoDiff('y', 2) * 2
    assert (x1 == y1) == False
    assert (x1 == 0) == False
def test_ne():
    x1 = sad.AutoDiff('x', 1) * 2
    y1 = sad.AutoDiff('y', 2) * 2
    assert x1 != y1
    y1 = sad.AutoDiff('y', 1) * 2
    x2 = sad.AutoDiff('x', 1) * 2
    assert (x1 != x1) == False
    assert (x1 != x2) == False
    assert (x1 != y1)
def test_div_autodiff_other():
    x1 = sad.AutoDiff('x', 2.0)
    x2 = sad.AutoDiff('x', 2.0)
    f = x1 / x2
    assert f.der['x'] == pytest.approx(0.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x'
    assert f.val == pytest.approx(1.0)
def test_lt():
    x1 = sad.AutoDiff('x', 1) * 1
    y1 = sad.AutoDiff('y', 2) * 2
    assert x1 < y1
    x1 = sad.AutoDiff('x', 2) * 2
    y1 = sad.AutoDiff('y', 1) * 2
    assert (x1 < x1) == False
    assert (x1 < y1) == False
    assert (x1 < 0) == False
def test_le():
    x1 = sad.AutoDiff('x', 1) * 1
    y1 = sad.AutoDiff('y', 2) * 2
    assert x1 <= x1
    assert x1 <= y1
    x1 = sad.AutoDiff('x', 2) * 2
    y1 = sad.AutoDiff('y', 1) * 2
    assert (x1 <= y1) == False
    assert (x1 <= 0) == False
def test_ge():
    x1 = sad.AutoDiff('x', 2) * 1
    y1 = sad.AutoDiff('y', 1) * 2
    assert x1 >= x1
    assert x1 >= y1
    x1 = sad.AutoDiff('x', 1) * 2
    y1 = sad.AutoDiff('y', 2) * 2
    assert (x1 >= y1) == False
    assert (x1 >= 10) == False
def test_gt():
    x1 = sad.AutoDiff('x', 2) * 2
    y1 = sad.AutoDiff('y', 1) * 1
    assert x1 > y1
    x1 = sad.AutoDiff('x', 1) * 2
    y1 = sad.AutoDiff('y', 2) * 2
    assert (x1 > x1) == False
    assert (x1 > y1) == False
    assert (x1 > 10) == False
def test_add_autodiff_other():
    x1 = sad.AutoDiff('x', 2)
    x2 = sad.AutoDiff('x', 2)
    f = x1 + x2
    assert f.der['x'] == pytest.approx(2.0)  # f(x)=x + x; f'(x) = 2; f'(2) = 2
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x + x'
    assert f.val == 4.0
def test_vector_log10():
    f1 = sad.AutoDiff('x', 2)
    f2 = sad.AutoDiff('y', 2)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = sad.log(sad.AutoDiffVector([f1, f2]), base=10)
    vec2 = sad.log(sad.vectorize(['x', 'y'], [2, 2]), base=10)
    f3 = sad.log(f1, base=10)
    #
    der = f3.der['x']
    val = f3.val
    do_vector_tests_no_other(vec1, vec2, der, val)
def test_vector_rpow():
    f1 = sad.AutoDiff('x', 2)
    f2 = sad.AutoDiff('y', 2)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = 3.0**sad.AutoDiffVector([f1, f2])
    vec2 = 3.0**sad.vectorize(['x', 'y'], [2, 2])
    f3 = 3.0**f1
    #
    der = f3.der['x']
    val = f3.val
    do_vector_tests_no_other(vec1, vec2, der, val)
def test_vector_tanh():
    f1 = sad.AutoDiff('x', 0.5)
    f2 = sad.AutoDiff('y', 0.5)
    # do operations on objects
    vec1 = sad.tanh(sad.AutoDiffVector([f1, f2]))
    vec2 = sad.tanh(sad.vectorize(['x', 'y'], [0.5, 0.5]))
    f3 = sad.tanh(f1)
    #
    der = f3.der['x']
    val = f3.val
    do_vector_tests_no_other(vec1, vec2, der, val)
    with pytest.raises(AttributeError):
        x1 = sad.AutoDiff('x', 1)
        sad._tanhV(x1)
def test_vector_ln():
    f1 = sad.AutoDiff('x', 2)
    f2 = sad.AutoDiff('y', 2)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = sad.log(sad.AutoDiffVector([f1, f2]))
    vec2 = sad.log(sad.vectorize(['x', 'y'], [2, 2]))
    f3 = sad.log(f1)
    #
    der = f3.der['x']
    val = f3.val
    do_vector_tests_no_other(vec1, vec2, der, val)
    with pytest.raises(AttributeError):
        x1 = sad.AutoDiff('x', 1)
        sad._logV(x1)
def test_sub_autodiff_self():
    x1 = sad.AutoDiff('x', 2)
    f = x1 - x1
    assert f.der['x'] == pytest.approx(0.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x - x'
    assert f.val == pytest.approx(0.0)
def test_rsub_constant():
    x1 = sad.AutoDiff('x', 2)
    f = 1 - x1
    assert f.der['x'] == pytest.approx(1.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x - 1'
    assert f.val == 1.0
def test_mul_autodiff_self():
    x1 = sad.AutoDiff('x', 2.0)
    f = x1 * x1
    assert f.der['x'] == pytest.approx(4.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x * x'
    assert f.val == pytest.approx(4.0)
def test_rmul_constant():
    x1 = sad.AutoDiff('x', 2)
    f = 3 * x1
    assert f.der['x'] == pytest.approx(3.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == '3 * x'
    assert f.val == pytest.approx(6.0)
def test_rdiv_constant():
    x1 = sad.AutoDiff('x', 4)
    f = 2 / x1
    assert f.der['x'] == pytest.approx(-0.125)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x'
    assert f.val == pytest.approx(0.5)
def test_neg():
    x1 = sad.AutoDiff('x', 2)
    f = -x1
    assert f.der['x'] == pytest.approx(-1.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x'
    assert f.val == pytest.approx(-2.0)
def test_pow():
    x1 = sad.AutoDiff('x', 2.0)
    f = x1**2
    assert f.der['x'] == pytest.approx(4.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(4.0)
def test_add_constant():
    x1 = sad.AutoDiff('x', 2)
    f = x1 + 1
    assert f.der['x'] == pytest.approx(1.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    #assert f.var == 'x + 1'
    assert f.val == pytest.approx(3.0)
def test_logistic():
    x1 = sad.AutoDiff('x', 4.0)
    f = sad.logistic(x1)
    assert f.der['x'] == pytest.approx(np.exp(4) / (1 + np.exp(4))**2)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(1 / (1 + np.exp(-4)))
def test_sqrt():
    x1 = sad.AutoDiff('x', 4.0)
    f = x1**0.5
    assert f.der['x'] == pytest.approx(1 / 4)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(2.0)
def test_rpow():
    x1 = sad.AutoDiff('x', 2.0)
    f = 2**x1
    assert f.der['x'] == pytest.approx(4 * math.log(2, math.e))
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(4.0)
def test_correct_init():
    x1 = sad.AutoDiff('x', 2)
    f = x1
    assert f.der['x'] == pytest.approx(1.0)
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(2.0)
def test_vector_add():
    f1 = sad.AutoDiff('x', 1)
    f2 = sad.AutoDiff('y', 1)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = sad.AutoDiffVector([f1, f2]) + 2
    vec1_other = sad.AutoDiffVector([f1, f2]) + other
    vec2 = sad.vectorize(['x', 'y'], [1, 1]) + 2
    vec2_other = sad.vectorize(['x', 'y'], [1, 1]) + other
    f3 = f1 + 2
    f4 = f1 + other
    #
    der = f3.der['x']
    val = f3.val
    der_other = f4.der['x']
    val_other = f4.val
    do_vector_tests(vec1, vec1_other, vec2, vec2_other, der, der_other, val,
                    val_other)
def test_tanh():
    x1 = sad.AutoDiff('x', 0.5)
    f = sad.tanh(x1)
    assert f.der['x'] == pytest.approx(2 / (np.cosh(2 * 0.5) + 1))
    #Test other attributes
    assert next(iter(f.der)) == 'x'
    assert f.var == 'x'
    assert f.val == pytest.approx(np.tanh(0.5))
    assert sad.tanh(0.5) == pytest.approx(np.tanh(0.5))
def test_vector_div():
    f1 = sad.AutoDiff('x', 2)
    f2 = sad.AutoDiff('y', 2)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = sad.AutoDiffVector([f1, f2]) / 3
    vec1_other = sad.AutoDiffVector([f1, f2]) / other
    vec2 = sad.vectorize(['x', 'y'], [2, 2]) / 3
    vec2_other = sad.vectorize(['x', 'y'], [2, 2]) / other
    f3 = f1 / 3
    f4 = f1 / other
    #
    der = f3.der['x']
    val = f3.val
    der_other = f4.der['x']
    val_other = f4.val
    do_vector_tests(vec1, vec1_other, vec2, vec2_other, der, der_other, val,
                    val_other)
def test_vector_rsub():
    f1 = sad.AutoDiff('x', 1)
    f2 = sad.AutoDiff('y', 1)
    other = sad.AutoDiff('z', 1)
    # do operations on objects
    vec1 = 1 - sad.AutoDiffVector([f1, f2])
    vec1_other = other - sad.AutoDiffVector([f1, f2])
    vec2 = 1 - sad.vectorize(['x', 'y'], [1, 1])
    vec2_other = other - sad.vectorize(['x', 'y'], [1, 1])
    f3 = 1 - f1
    f4 = other - f1
    #
    der = f3.der['x']
    val = f3.val
    der_other = f4.der['x']
    val_other = f4.val
    do_vector_tests(vec1, vec1_other, vec2, vec2_other, der, der_other, val,
                    val_other)
def test_vector_rdiv():
    f1 = sad.AutoDiff('x', 2)
    f2 = sad.AutoDiff('y', 2)
    other = sad.AutoDiff('z', 2)
    # do operations on objects
    vec1 = 3 / sad.AutoDiffVector([f1, f2])
    vec1_other = other / sad.AutoDiffVector([f1, f2])
    vec2 = 3 / sad.vectorize(['x', 'y'], [2, 2])
    vec2_other = other / sad.vectorize(['x', 'y'], [2, 2])
    f3 = 3 / f1
    f4 = other / f1
    #
    der = f3.der['x']
    val = f3.val
    der_other = f4.der['x']
    val_other = f4.val
    do_vector_tests(vec1, vec1_other, vec2, vec2_other, der, der_other, val,
                    val_other)