def test_array1(self): """ Tests functions with single numpy 1d array output """ def f1(x): # single scalar input return np.array([i*x**i for i in range(5)]) df = jacobian(f1)(3.0) for i, d in enumerate(df): self.assertAlmostEqual(i**2 * 3.0 ** (i - 1), d) def f2(params): # one list, one numpy array input x,y = params[0] A = params[1] return np.linalg.dot(np.sin(A), np.array([x,y**2])) A = np.array([[1.0, 2.0],[3.0, 4.0]]) x,y = 2.0, np.pi params = [[x, y], A] df = jacobian(f2)(params) # df0_dx self.assertAlmostEqual(df[0][0][0], np.sin(A)[0][0]) # df1_dx self.assertAlmostEqual(df[1][0][0], np.sin(A)[1][0]) # df0_dy self.assertAlmostEqual(df[0][0][1], 2*np.sin(A)[0][1]*y) # df1_dy self.assertAlmostEqual(df[1][0][1], 2*np.sin(A)[1][1]*y) # df_dA assert np.linalg.norm(df[0][1][0] - (np.cos(A)*np.array([x,y**2]))[0]) < 1e-10 assert np.linalg.norm(df[1][1][1] - (np.cos(A)*np.array([x,y**2]))[1]) < 1e-10
def test_multi_scalar(self): """ Tests functions with multiple scalar output """ def f1(x): # two scalar input return x**3, np.exp(3*x) df = jacobian(f1)(0.5) self.assertAlmostEqual(3*0.5**2, df[0]) self.assertAlmostEqual(3*np.exp(3*0.5), df[1]) def f2(params): # one list, one numpy array input x,y = params[0] A = params[1] return np.sum(A**2) + np.cos(x) + np.exp(0.5*y) df = jacobian(f2) A = np.array([[1.0, 2.0],[3.0, 4.0]]) params = [[0.5, np.pi], A] diff = df(params) self.assertAlmostEqual(diff[0][0], -np.sin(0.5)) self.assertAlmostEqual(diff[0][1], 0.5*np.exp(0.5*np.pi)) self.assertTrue(np.linalg.norm(2*A - diff[1]) < 1e-10)
def test_linear_system(self): """ Tests taking the derivative across a linear system solve """ def linsolve(params): A, B = params return np.linalg.solve(A, B) B = np.array([1.0, 3.0]) A = np.array([[5.0, 2.0],[1.0, 3.0]]) df = jacobian(linsolve) diff = df([A, B]) Ainv = np.linalg.inv(A) x = np.linalg.solve(A, B) #df_dB assert np.linalg.norm(diff[0][1] - Ainv[0]) < 1e-10 assert np.linalg.norm(diff[1][1] - Ainv[1]) < 1e-10 #df_fA dr_da = np.zeros((2,4)) dr_da[0, 0:2] = x dr_da[1, 2:] = x df_fa = np.linalg.solve(A, -dr_da) assert np.linalg.norm(df_fa[0] - diff[0][0].flatten()) < 1e-10 assert np.linalg.norm(df_fa[1] - diff[1][0].flatten()) < 1e-10
def test_scalar(self): """ Test simple scalar functions - basically a passthrough to autograd.grad """ def f1(x): return 2.0*x self.assertAlmostEqual(jacobian(f1)(5.0), 2.0) def f2(params): return np.exp(params[0]) + params[1][0]**2 + np.cos(params[1][1]) params = [1.0, [2.0, np.pi]] df = jacobian(f2)(params) self.assertAlmostEqual(df[0], np.exp(1.0)) self.assertAlmostEqual(df[1][0], 4.0) self.assertAlmostEqual(df[1][1], 0.0)
def test_mixed1(self): """ Tests functions with mix of scalars and array outputs """ def f1(x): return x, 0.5*x**3, np.array([2*x, x**2]) x = 0.5 diff = jacobian(f1)(x) self.assertAlmostEqual(diff[0], 1.0) self.assertAlmostEqual(diff[1], 3*0.5*x**2) self.assertAlmostEqual(diff[2][0], 2.0) self.assertAlmostEqual(diff[2][1], 2.0*x)
def test_array2(self): """ Tests functions with single numpy 2d array output """ def f1(x): # single scalar input return np.array([[x, 2*x],[x**2, np.exp(0.5*x)]]) x = 0.75 d = np.array([[1.0, 2.0],[2*x, 0.5*np.exp(0.5*x)]]) df = jacobian(f1)(x) assert np.linalg.norm(d - df) < 1e-10 def f2(params): # mixed inputs x = params[0] # float A = params[1] # 2d array B = params[2] # 1d array return np.exp(B**2)/x * A