def rm_test_overload_abs(): x = DualNumber(-5,Reverse=True) y = abs(x) assert y.val == 5 and y.der == 0 x = DualNumber(5,1,Reverse=True) y = abs(x) assert y.val == 5 and y.der == 0
def test_overload_abs(): x = DualNumber(-5,-1) y = abs(x) assert y.val == 5 and y.der == 1 x = DualNumber(5,1) y = abs(x) assert y.val == 5 and y.der == 1
def test_overload_ne(): w = DualNumber(5,1) x = DualNumber(5,1) y = DualNumber(6,1) z = DualNumber(5,2) assert not w != x assert x != y assert x != z
def rm_test_overload_multiply(): x = DualNumber(5,Reverse=True) y = DualNumber(7,Reverse=True) assert (x * y).val == 35 and (x * y).der == 0 assert (x * 5).val == 25 and (x * 5).der == 0 assert (x * 5.0).val == 25 and (x * 5.0).der == 0 assert (5.0 * x).val == 25 and (5.0 * x).der == 0 assert (5 * x).val == 25 and (5 * x).der == 0
def rm_test_overload_add(): x = DualNumber(5,Reverse=True) y = DualNumber(7,Reverse=True) assert (x + y).val == 12 and (x + y).der == 0 assert (x + 5).val == 10 and (x + 5).der == 0 assert (x + 5.0).val == 10 and (x + 5.0).der == 0 assert (5 + x).val == 10 and (5 + x).der == 0 assert (5.0 + x).val == 10 and (5.0 + x).der == 0
def test_overload_multiply(): x = DualNumber(5) y = DualNumber(7,2) assert (x * y).val == 35 and (x * y).der == 17 assert (x * 5).val == 25 and (x * 5).der == 5 assert (x * 5.0).val == 25 and (x * 5.0).der == 5 assert (5.0 * x).val == 25 and (5.0 * x).der == 5 assert (5 * x).val == 25 and (5 * x).der == 5
def test_ArcCos(): Test_Dual_Number_1 = DualNumber(0.5) assert EF.ArcCos(Test_Dual_Number_1).val == np.arccos(0.5) and EF.ArcCos(Test_Dual_Number_1).der == -1 / np.sqrt( 0.75) Test_Dual_Number_1 = 0.5 assert EF.ArcCos(Test_Dual_Number_1).val == np.arccos(0.5) and EF.ArcCos(Test_Dual_Number_1).der == 0 with pytest.raises(ValueError): x = DualNumber(5) EF.ArcCos(x)
def test_overload_add(): x = DualNumber(5,1) y = DualNumber(7,1) assert (x + y).val == 12 and (x + y).der == 2 assert (x + 5).val == 10 and (x + 5).der == 1 assert (x + 5.0).val == 10 and (x + 5.0).der == 1 assert (5 + x).val == 10 and (5 + x).der == 1 assert (5.0 + x).val == 10 and (5.0 + x).der == 1
def rm_test_overload_power(): x = DualNumber(2,Reverse=True) y = DualNumber(3,Reverse=True) assert (x**y).val == 8 and (x**y).der == 0 assert (x.__rpow__(y)).val == 9 and (x.__rpow__(y)).der == 0 assert (x**2).val == 4 and (x**2).der == 0 assert (x**2.0).val == 4 and (x**2.0).der == 0 assert (2**x).val == 4 and (2**x).der == 0 assert (2.0**x).val == 4 and (2.0**x).der == 0
def rm_test_overload_truediv(): x = DualNumber(10,Reverse=True) y = DualNumber(2,Reverse=True) assert (x / y).val == 5 and (x / y).der == 0 assert (x.__rtruediv__(y)).val == 0.2 and (x.__rtruediv__(y)).der == 0 assert (x / 5).val == 2 and (x / 5).der == 0 assert (x / 5.0).val == 2.0 and (x / 5.0).der == 0 assert (5 / x).val == 0.5 and (5 / x).der == 0 assert (5.0 / x).val == 0.5 and (5.0 / x).der == 0
def rm_test_overload_sub(): x = DualNumber(5,Reverse=True) y = DualNumber(7,Reverse=True) assert (x - y).val == -2 and (x - y).der == 0 assert (x.__rsub__(y)).val == 2 and (x.__rsub__(y)).der == 0 assert (x - 5).val == 0 and (x - 5).der == 0 assert (x - 5.0).val == 0 and (x - 5.0).der == 0 assert (5 - x).val == 0 and (5 - x).der == 0 assert (5.0 - x).val == 0 and (5.0 - x).der == 0
def test_Log(): Test_Dual_Number_1 = DualNumber(1) assert EF.Log(Test_Dual_Number_1).val == 0 and EF.Log(Test_Dual_Number_1).der == 1 Test_Dual_Number_1 = 1 assert EF.Log(Test_Dual_Number_1).val == 0 and EF.Log(Test_Dual_Number_1).der == 0 Test_Dual_Number_1 = DualNumber(2) assert EF.Log(Test_Dual_Number_1,base=10).val == np.log(2) / np.log(10) and EF.Log(Test_Dual_Number_1,base=10).der == 1/(np.log(10)*2) Test_Dual_Number_1 = 2 assert EF.Log(Test_Dual_Number_1,base=10).val == np.log(2) / np.log(10) and EF.Log(Test_Dual_Number_1,base=10).der == 0
def test_overload_truediv(): x = DualNumber(10,2) y = DualNumber(2,4) assert (x / y).val == 5 and (x / y).der == (2*2-10*4)/(2**2) assert (x.__rtruediv__(y)).val == 0.2 and (x.__rtruediv__(y)).der == (4*10-2*2)/(10*10) assert (x / 5).val == 2 and (x / 5).der == 0.4 assert (x / 5.0).val == 2.0 and (x / 5.0).der == 0.4 assert (5 / x).val == 0.5 and (5 / x).der == -0.1 assert (5.0 / x).val == 0.5 and (5.0 / x).der == -0.1
def test_overload_sub(): x = DualNumber(5,2) y = DualNumber(7,1) assert (x - y).val == -2 and (x - y).der == 1 assert (x.__rsub__(y)).val == 2 and (x.__rsub__(y)).der == -1 assert (x - 5).val == 0 and (x - 5).der == 2 assert (x - 5.0).val == 0 and (x - 5.0).der == 2 assert (5 - x).val == 0 and (5 - x).der == -2 assert (5.0 - x).val == 0 and (5.0 - x).der == -2
def test_overload_power(): x = DualNumber(2,2) y = DualNumber(3,4) assert (x**y).val == 8 and (x**y).der == 8*(3/2*2+4*np.log(2)) assert (x.__rpow__(y)).val == 9 and (x.__rpow__(y)).der == 9*(2/3*4+2*np.log(3)) assert (x**2).val == 4 and (x**2).der == 8 assert (x**2.0).val == 4 and (x**2.0).der == 8 assert (2**x).val == 4 and (2**x).der == np.log(2)*4*2 assert (2.0**x).val == 4 and (2.0**x).der == np.log(2)*4*2
def get_value(self, loc): """ DESCRIPTION ======= A class method to get the value of user-specified vector function. See doctests for details on usage. User inputs location of vector-valued function, and get_value returns the value at the specified location. >>> PAD = Parallelized_AD(fun=['_x * arcSin(_y*_z)+_x'], var=['x', 'y', 'z']) >>> print(PAD.get_value([0.4,0.2,1])) [0.48054317] >>> print(PAD.get_Jacobian([0.4,0.2,1])) [[1.20135792 0.40824829 0.08164966]] """ assert len(loc) == len(self.varname) self._value = np.zeros((len(self.function))) # for each function, if forward, do forward mode calculation, else do reverse # see documentation for details on reverse mode calculation for i, fun in enumerate(self.function): # pre-process each function to be differentatied translated_fun = self.preprocess(fun) # for each variable, take the derivative at the value specified # for each variable, take the derivative at the value specified self.variable = [DualNumber(value) for value in loc] element = eval(translated_fun) self._value[i] = element.val return self._value
def Sin(x): ''' >>> print(Sin(DualNumber(5,1))) Derivative: 0.28 Value: -0.96 >>> print(Sin(DualNumber(5,Reverse=True))) Derivative: 0.00 Value: -0.96 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.sin(x._val), Reverse=True) x.children.append((np.cos(x._val), z)) return z return DualNumber(np.sin(x._val), np.cos(x._val) * x._der) else: return DualNumber(np.sin(x), 0)
def Sqrt(x): ''' >>> print(Sqrt(DualNumber(9,1))) Derivative: 0.17 Value: 3.00 >>> print(Sqrt(DualNumber(9,Reverse = True))) Derivative: 0.00 Value: 3.00 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.sqrt(x._val), Reverse=True) x.children.append((1 / (2 * np.sqrt(x._val)), z)) return z return DualNumber(np.sqrt(x._val), 1 / (2 * np.sqrt(x._val)) * x._der) else: return DualNumber(np.sqrt(x), 0)
def ArcTan(x): ''' >>> print(ArcTan(DualNumber(0.5))) Derivative: 0.80 Value: 0.46 >>> print(ArcTan(DualNumber(0.5,Reverse = True))) Derivative: 0.00 Value: 0.46 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.arctan(x._val), Reverse=True) x.children.append((1 / (1 + x._val**2), z)) return z return DualNumber(np.arctan(x._val), 1 / (1 + x._val**2) * x._der) else: return DualNumber(np.arctan(x), 0)
def Power(x, n): ''' >>> print(Power(DualNumber(5,1),2)) Derivative: 10.00 Value: 25.00 >>> print(Power(DualNumber(5,Reverse = True),2)) Derivative: 0.00 Value: 25.00 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(x._val**n, Reverse=True) x.children.append(((n * (x._val**(n - 1)), z))) return z return DualNumber(x._val**n, n * (x._val**(n - 1)) * x._der) else: return DualNumber(x**n, 0)
def Exp(x): ''' >>> print(Exp(DualNumber(5,1))) Derivative: 148.41 Value: 148.41 >>> print(Exp(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: 148.41 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.exp(x._val), Reverse=True) x.children.append((np.exp(x._val), z)) return z return DualNumber(np.exp(x._val), np.exp(x._val) * x._der) else: return DualNumber(np.exp(x), 0)
def Logistic(x): ''' >>> print(Logistic(DualNumber(5,1))) Derivative: 0.01 Value: 0.99 >>> print(Logistic(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: 0.99 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(1 / (1 + np.exp(-x._val)), Reverse=True) x.children.append((np.exp(x._val) / (1 + np.exp(x._val))**2, z)) return z return DualNumber(1 / (1 + np.exp(-x._val)), (np.exp(x._val) / (1 + np.exp(x._val))**2) * x._der) else: return DualNumber(1 / (1 + np.exp(-x)), 0)
def Tan(x): ''' >>> print(Tan(DualNumber(5,1))) Derivative: 12.43 Value: -3.38 >>> print(Tan(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: -3.38 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.tan(x._val), Reverse=True) x.children.append(((1 + np.tan(x._val) * np.tan(x._val)), z)) return z return DualNumber(np.tan(x._val), (1 + np.tan(x._val) * np.tan(x._val)) * x._der) else: return DualNumber(np.tan(x), 0)
def Cosh(x): ''' >>> print(Cosh(DualNumber(5,1))) Derivative: 74.20 Value: 74.21 >>> print(Cosh(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: 74.21 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber((np.exp(x._val) + np.exp(-x._val)) / 2, Reverse=True) x.children.append(((np.exp(x._val) - np.exp(-x._val)) / 2, z)) return z return DualNumber((np.exp(x._val) + np.exp(-x._val)) / 2, ((np.exp(x._val) - np.exp(-x._val)) / 2) * x._der) else: return DualNumber((np.exp(x) + np.exp(-x)) / 2, 0)
def Log(x, base=np.exp(1)): ''' >>> print(Log(DualNumber(5,1))) Derivative: 0.20 Value: 1.61 >>> print(Log(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: 1.61 ''' if data_type_check(x) == 0: if x._rev: z = DualNumber(np.log(x._val) / np.log(base), Reverse=True) x.children.append((1 / (x._val * np.log(base)), z)) return z return DualNumber( np.log(x._val) / np.log(base), (1 / (x._val * np.log(base))) * x._der) else: return DualNumber(np.log(x) / np.log(base), 0)
def get_Jacobian(self, loc, forward=False): """ DESCRIPTION ======= A class method to get the Jacobian of user-specified vector function. See doctests for details on usage. User inputs location to take Jacobian, and function returns the Jacobian at that point. Assert conditions ensure dimension of location input is the same as the number of variables. forward argument specifies whether reverse (False) or forward (True) mode is used """ assert len(loc) == len(self.varname) self._Jacobian = np.zeros((len(self.function), len(self.varname))) # for each function, if forward, do forward mode calculation, else do reverse # see documentation for details on reverse mode calculation for i, fun in enumerate(self.function): if forward: # pre-process each function to be differentatied translated_fun = self.preprocess(fun) # for each variable, take the derivative at the value specified for j in range(len(self.varname)): self.variable = [ DualNumber(value, dual=0) for value in loc ] self.variable[j] = DualNumber(loc[j], dual=1) element = eval(translated_fun) self._Jacobian[i, j] = element.der # similarly, if reverse, carry out reverse mode else: self.variable = [ DualNumber(value, Reverse=True) for value in loc ] translated_fun = self.preprocess(fun) element = eval(translated_fun) element.set_der(1) for j in range(len(self.varname)): self._Jacobian[i, j] = self.variable[j].der return self._Jacobian
def ArcCos(x): ''' >>> print(ArcCos(DualNumber(0.5))) Derivative: -1.15 Value: 1.05 >>> print(ArcCos(DualNumber(0.5,Reverse = True))) Derivative: 0.00 Value: 1.05 ''' if abs(x) >= 1: raise ValueError('ArcCos is only defined on (-1,1)!') if data_type_check(x) == 0: if x._rev: z = DualNumber(np.arccos(x._val), Reverse=True) x.children.append((-1 / np.sqrt(1 - x._val**2), z)) return z return DualNumber(np.arccos(x._val), -1 / np.sqrt(1 - x._val**2) * x._der) else: return DualNumber(np.arccos(x), 0)
def Tanh(x): ''' >>> print(Tanh(DualNumber(5,1))) Derivative: 0.00 Value: 1.00 >>> print(Tanh(DualNumber(5,Reverse = True))) Derivative: 0.00 Value: 1.00 ''' if data_type_check(x) == 0: Z = (np.exp(x._val) - np.exp(-x._val)) / (np.exp(x._val) + np.exp(-x._val)) if x._rev: z = DualNumber(Z, Reverse=True) x.children.append((1 - Z**2, z)) return z return DualNumber(Z, (1 - Z**2) * x._der) else: return DualNumber((np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x)), 0)
def Demo_gradient(x): x=DualNumber(x); y=-EF.Sin(x)*EF.Cos(x)*EF.Tan(x)+EF.Exp(x)*EF.Log(x)*EF.Sqrt(x)*2 return y.der #y=sin(x)cos(x)tan(x)-2exp(x)log(x)sqrt(x)
def Given_function(x): x = DualNumber(x) y = EF.Sin(x) * EF.Cos(x) * EF.Tan(x) - EF.Exp(x) * EF.Log(x) * EF.Sqrt( x) * 2 return y #y=sin(x)cos(x)tan(x)-2exp(x)log(x)sqrt(x)