def test_jacobian_exception(): x = AD.AD(3.0) y = AD.AD(2.0, [0.0, 1.0]) with pytest.raises(Exception, match="Incorrect dimension"): assert AD.AD.get_jacobian([x, y]) with pytest.raises(TypeError, match="Invalid input type: not a function"): assert AD.AD.get_jacobian([x, 3.0]) with pytest.raises(Exception, match="Empty list of functions"): assert AD.AD.get_jacobian([])
def test_jacobian(): x = AD.AD(3.0, [1.0, 0.0, 0.0]) y = AD.AD(2.0, [0.0, 1.0, 0.0]) z = AD.AD(1.0, [0.0, 0.0, 1.0]) vals, ders = AD.AD.get_jacobian([x * y, x + y, z, unary.sin(x)]) test_jac = np.array([[2.0, 3.0, 0.0], [1.0, 1.0, 0.0], [0.0, 0.0, 1.0], [np.cos(3.0), 0.0, 0.0]]) assert (vals[0] == 6.0) assert (vals[2] == 1.0) assert (np.array_equal(ders, test_jac))
def test_add_radd_mult_rmult(): x = AD.AD(2.0) alpha = 2.0 beta = 3.0 functions = [] functions.append(('alpha * x + beta', alpha * x + beta)) functions.append(('x * alpha + beta', x * alpha + beta)) functions.append(('beta + alpha * x', beta + alpha * x)) functions.append(('beta + x * alpha', beta + x * alpha)) for (name, val) in functions: assert (val.val == 7) assert (val.der == 2)
def tanh(x): """ Calculate hyperbolic tangent of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: hyperbolic tangent of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> print(tanh(x)) AD(0.9640275800758169, [0.07065082 0. ]) """ try: return ad.AD(np.tanh(x.val), x.der/np.cosh(x.val)**2) except AttributeError: return np.tanh(x)
def arctan(x): """ Calculate inverse tangent of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: inverse tangent of x EXAMPLES ========= >>> x = ad.AD(2) >>> print(arctan(x)) AD(1.1071487177940906, [0.2]) """ try: return ad.AD(np.arctan(x.val), x.der/(1+x.val**2)) except AttributeError: return np.arctan(x)
def logistic(x): """ Return the logistic of an AD object, integer, or float INPUTS ======= x: the AD object, int, or float RETURNS ======== result: 1/(1+e^(-x)) EXAMPLES ========= >>> x = ad.AD(4.0) >>> print(logistic(x)) AD(0.9820137900379085, [0.0183095]) """ try: return ad.AD(1/(1+np.exp(-x.val)), np.exp(-x.val)*x.der/(1+np.exp(-x.val))**2) except AttributeError: return 1/(1+np.exp(-x))
def log(x, base): """ Calculate the logarithm of an AD object, integer, or float of given base INPUTS ======= x: the AD object, int, or float base: logarithm base RETURNS ======== result: given base logarithm of x EXAMPLES ========= >>> x = ad.AD(4.0) >>> print(log(x,2)) AD(2.0, [0.36067376]) """ try: return ad.AD(math.log(x.val, base), x.der/(x.val*np.log(base))) except AttributeError: return math.log(x,base)
def ln(x): """ Calculate the natural base logarithm of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: natural logarithm of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(ln(x*y)) AD(1.791759469228055, [0.5 0.33333333]) """ try: return ad.AD(np.log(x.val), x.der/x.val) except AttributeError: return np.log(x)
def cosh(x): """ Calculate hyperbolic cosine of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: hyperbolic cosine of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(cosh(x*y)) AD(201.7156361224559, [605.13947211 403.42631474]) """ try: return ad.AD(np.cosh(x.val), x.der*np.sinh(x.val)) except AttributeError: return np.cosh(x)
def sinh(x): """ Calculate hyperbolic sine of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: hyperbolic sine of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(sinh(x+y)) AD(74.20321057778875, [74.20994852 74.20994852]) """ try: return ad.AD(np.sinh(x.val), x.der*np.cosh(x.val)) except AttributeError: return np.sinh(x)
def sin(x): """ Calculate sine of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: sine of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(sin(x*y)) AD(-0.27941549819892586, [2.88051086 1.92034057]) """ try: return ad.AD(np.sin(x.val), x.der*np.cos(x.val)) except AttributeError: return np.sin(x)
def cos(x): """ Calculate cosine of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: cosine of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(cos(x*y)) AD(0.9601702866503661, [0.83824649 0.558831 ]) """ try: return ad.AD(np.cos(x.val), -x.der*np.sin(x.val)) except AttributeError: return np.cos(x)
def arccos(x): """ Calculate inverse cosine of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or float RETURNS ======== result: inverse cosine of x EXAMPLES ========= >>> x = ad.AD(0.5) >>> print(arccos(x)) AD(1.0471975511965976, [-1.15470054]) """ try: if x.val<=-1 or x.val>=1: raise Exception("X out of domain") return ad.AD(np.arccos(x.val), -x.der/np.sqrt(1-x.val**2)) except AttributeError: return np.arccos(x)
def exp(x): """ Calculate the exponential of the input AD object, integer, or float INPUTS ======= x: input value, an AD object, int, or flot RETURNS ======== result: exponential of x EXAMPLES ========= >>> x = ad.AD(2.0, [1.0,0.0]) >>> y = ad.AD(3.0, [0.0,1.0]) >>> print(exp(x)) AD(7.38905609893065, [7.3890561 0. ]) >>> print(exp(x*y)) AD(403.4287934927351, [1210.28638048 806.85758699]) """ try: return ad.AD(np.exp(x.val), np.exp(x.val)*x.der) except AttributeError: return np.exp(x)
def test_add_reg_AD(): x = 2 + AD.AD(1.0) y = AD.AD(3.0, 1.0) assert (x == y)
def test_add_AD_reg(): x = AD.AD(2.0) + 3 y = AD.AD(5.0, 1.0) assert (x == y)
def test_add_AD_AD(): x = AD.AD(2.0) + AD.AD(3.0) y = AD.AD(5.0, 2.0) assert (x == y)
def test_identity(): x = AD.AD(3.0) eq = (2 * x) / (2 * x) y = AD.AD(1.0, 0.0) assert (eq == y)
def test_mult_reg_AD(): x = 2 * AD.AD(1.0) y = AD.AD(2.0, 2.0) assert (x == y)
def test_invalid_init(): x = AD.AD(3.0) with pytest.raises(TypeError, match="Invalid input type"): assert AD.AD(x)
def test_sub_AD_reg(): x = AD.AD(2.0) - 1.0 y = AD.AD(1.0, 1.0) assert (x == y)
def test_mult_AD_AD(): x = AD.AD(2.0) * AD.AD(1.0) y = AD.AD(2.0, 3.0) assert (x == y)
def test_sub_AD_AD(): x = AD.AD(2.0) - AD.AD(1.0) y = AD.AD(1.0, 0.0) assert (x == y)
def test_eq(): x = AD.AD(3.0) y = 3.0 assert (x == y)
def test_sub_reg_AD(): x = 2 - AD.AD(1.0) y = AD.AD(1.0, -1.0) assert (x == y)
def test_ne(): x = AD.AD(3.0, [1.0, 0.0]) y = AD.AD(3.0) assert (x != y)
def test_mult_AD_reg(): x = AD.AD(2.0) * 2.0 y = AD.AD(4.0, 2.0) assert (x == y)
def test_neg(): x = AD.AD(3.0, [1.0, 0.0]) y = AD.AD(2.0, [0.0, 1.0]) f = -x * y assert (f.val == -6.0) assert (np.array_equal(f.der, np.array([-2.0, -3.0])))
def test_truediv_AD_AD(): x = AD.AD(6.0) / AD.AD(3.0) y = AD.AD(2.0, -1 / 3) assert (x == y)
def test_linear_combos(): x = AD.AD(3.0) eq = x**2 + x y = AD.AD(12.0, 7.0) assert (eq == y)