def test_simple_getitem2(self): """ test: z = x1*x2 by x1 = x[0] x2 = x[1] z = x1 * x2 """ D,P,N = 1,1,2 cg = CGraph() x = UTPM(numpy.random.rand(*(D,P,N))) Fx = Function(x) Fx1 = Fx[0] Fx2 = Fx[1] Fz = Fx1 * Fx2 cg.independentFunctionList = [Fx1, Fx2] cg.dependentFunctionList = [Fz] zbar = UTPM(numpy.zeros((D,P))) zbar.data[0,:] = 1. cg.pullback([zbar]) assert_array_almost_equal(Fx1.xbar.data , Fx2.x.data) assert_array_almost_equal(Fx2.xbar.data , Fx1.x.data)
def test_dot(self): """ test z = dot(x,y)""" cg = CGraph() D,P,N,M = 2,5,7,11 ax = UTPM(numpy.random.rand(D,P,N,M)) ay = UTPM(numpy.random.rand(D,P,M,N)) fx = Function(ax) fy = Function(ay) fz = Function.dot(fx,fy) cg.independentFunctionList = [fx,fy] cg.dependentFunctionList = [fz] ax = UTPM(numpy.random.rand(D,P,N,M)) ay = UTPM(numpy.random.rand(D,P,M,N)) azbar = UTPM(numpy.random.rand(*fz.x.data.shape)) cg.pushforward([ax,ay]) cg.pullback([azbar]) xbar_reverse = cg.independentFunctionList[0].xbar ybar_reverse = cg.independentFunctionList[1].xbar xbar_symbolic = UTPM.dot(azbar,ay.T) ybar_symbolic = UTPM.dot(ax.T,azbar) assert_array_almost_equal(xbar_reverse.data, xbar_symbolic.data) assert_array_almost_equal(ybar_reverse.data, ybar_symbolic.data)
def test_reverse_on_basic_element_wise_functions(self): cg = CGraph() D,P,N,M = 2,5,7,11 ax = UTPM(numpy.random.rand(D,P,N,M)) ay = UTPM(numpy.random.rand(D,P,N,M)) fx = Function(ax) fy = Function(ay) fv1 = fx * fy fv2 = (fv1 * fx + fy)*fv1 cg.independentFunctionList = [fx,fy] cg.dependentFunctionList = [fv2] v2bar = UTPM(numpy.zeros((D,P,N,M))) v2bar.data[0,:,:,:] = 1. cg.pullback([v2bar]) xbar_reverse = cg.independentFunctionList[0].xbar ybar_reverse = cg.independentFunctionList[1].xbar xbar_symbolic = 3. * ax*ax * ay*ay + ay*ay ybar_symbolic = 2.*ax*ax*ax * ay + 2. * ax * ay # print xbar_symbolic.tc # print xbar_reverse # print ybar_symbolic # print ybar_reverse assert_array_almost_equal(xbar_reverse.data, xbar_symbolic.data) assert_array_almost_equal(ybar_reverse.data, ybar_symbolic.data)
def test_erf(self): """ compute y = erf(x**2 + 3.) """ def f(x): v1 = x**2 + 3. y = algopy.special.erf(v1) return y #FIXME: uncomment the rest when the pullback is implemented # use CGraph #cg = CGraph() #x = Function(numpy.array([1.])) #y = f(x) #cg.independentFunctionList = [x] #cg.dependentFunctionList = [y] #result1 = cg.jac_vec(numpy.array([2.]), numpy.array([1.])) #result2 = cg.jacobian(numpy.array([2.]))[0] # use UTPM x = UTPM.init_jacobian(numpy.array([2.])) y = f(x) result3 = UTPM.extract_jacobian(y)[0]
def test_hyp1f1(self): """ compute y = hyp1f1(1., 2., x**2 + 3.) """ def f(x): v1 = x**2 + 3. y = algopy.special.hyp1f1(1., 2., v1) return y # use CGraph cg = CGraph() x = Function(numpy.array([1.])) y = f(x) cg.independentFunctionList = [x] cg.dependentFunctionList = [y] result1 = cg.jac_vec(numpy.array([2.]), numpy.array([1.])) result2 = cg.jacobian(numpy.array([2.]))[0] # use UTPM x = UTPM.init_jacobian(numpy.array([2.])) y = f(x) result3 = UTPM.extract_jacobian(y)[0] assert_array_almost_equal(result1, result2) assert_array_almost_equal(result2, result3) assert_array_almost_equal(result3, result1)
def test_very_simple_ODOE_objective_function(self): """ compute PHI = trace( (J^T,J)^-1 ) """ D,P,N,M = 2,1,100,3 J = UTPM(numpy.random.rand(D,P,N,M)) cg = CGraph() FJ = Function(J) FJT = Function.transpose(FJ) FM = Function.dot(FJT, FJ) FC = Function.inv(FM) FPHI = Function.trace(FC) cg.independentFunctionList = [FJ] cg.dependentFunctionList = [FPHI] assert_array_equal(FPHI.shape, ()) cg.pushforward([J]) PHIbar = UTPM(numpy.random.rand(*(D,P))) # pullback using the tracer cg.pullback([PHIbar]) # verifying pullback by ybar.T ydot == xbar.T xdot const1 = UTPM.dot(FPHI.xbar, UTPM.shift(FPHI.x,-1)) const2 = UTPM.trace(UTPM.dot(FJ.xbar.T, UTPM.shift(FJ.x,-1))) # print cg # print const1 # print const2 assert_array_almost_equal(const1.data[0,:], const2.data[0,:])
def test_simple_getitem_setitem(self): """ test: z = x*x * 2 by z = UTPM(zeros(...)) z[...] += x*x z *= 2 """ D,P = 1,1 cg = CGraph() x = UTPM(numpy.random.rand(*(D,P))) Fx = Function(x) Fz = Function(UTPM(numpy.zeros((D,P)))) Fz[...] += Fx * Fx Fz *= 3 cg.independentFunctionList = [Fx] cg.dependentFunctionList = [Fz] assert_array_almost_equal(Fz.x.data[0], 3*x.data[0]**2) zbar = UTPM(numpy.zeros((D,P))) zbar.data[0,:] = 1. cg.pullback([zbar]) assert_array_almost_equal(Fx.x.data * 6, Fx.xbar.data)
def test_sqrt_in_norm_computation(self): def eval_f1(x): return algopy.sqrt(algopy.sum(x*x)) def eval_f2(x): return (algopy.sum(x*x))**0.5 cg1 = CGraph() x1 = Function(1.) y1 = eval_f1(x1) cg1.trace_off() cg1.independentFunctionList = [x1] cg1.dependentFunctionList = [y1] cg2 = CGraph() x2 = Function(1.) y2 = eval_f2(x2) cg2.trace_off() cg2.independentFunctionList = [x2] cg2.dependentFunctionList = [y2] x = numpy.random.rand(3) g1 = cg1.gradient([x])[0] g2 = cg2.gradient([x])[0] J1 = UTPM.extract_jacobian(eval_f1(UTPM.init_jacobian(x))) J2 = UTPM.extract_jacobian(eval_f2(UTPM.init_jacobian(x))) assert_array_almost_equal(g1,g2) assert_array_almost_equal(g2,J1) assert_array_almost_equal(J1,J2) assert_array_almost_equal(J2,g1)
def test_tangent_gradient(self): cg = CGraph() x = Function(1.) y1 = algopy.tan(x) cg.trace_off() cg.independentFunctionList = [x] cg.dependentFunctionList = [y1] g1 = cg.gradient([1.])[0] x = UTPM.init_jacobian(1.) assert_array_almost_equal(g1,UTPM.extract_jacobian(algopy.sin(x)/algopy.cos(x))) assert_array_almost_equal(g1,UTPM.extract_jacobian(algopy.tan(x)))
def test_ones_like_utpm(self): D, P, N, M = 3, 4, 5, 6 x = UTPM(numpy.random.random((D, P, N, M))) y = ones_like(x) data = numpy.zeros((D, P, N, M)) data[0, ...] = 1. assert_array_almost_equal(data, y.data)
def test_utpm2dirs(self): D, P, N, M, K = 2, 3, 4, 5, 6 u = UTPM(numpy.arange(D * P * N * M * K).reshape((D, P, N, M, K))) Vbar = utpm2dirs(u) assert_array_almost_equal( Vbar, numpy.arange(D * P * N * M * K).reshape((D, P, N, M, K)).transpose( (2, 3, 4, 1, 0)))
def test_pullback_diag(self): D,P,N = 2,3,4 cg = CGraph() # forward x = Function(UTPM(numpy.random.rand(D,P,N))) X = Function.diag(x) Y = Function.diag(x) cg.trace_off() cg.independentFunctionList = [x] cg.dependentFunctionList = [X,Y] #reverse Xbar = UTPM.diag(UTPM(numpy.random.rand(D,P,N))) Ybar = Xbar.copy() cg.pullback([Xbar, Ybar]) assert_array_almost_equal(x.xbar.data, 2* UTPM.diag(Xbar).data)
def test_more_complicated_ODOE_objective_function(self): """ compute PHI = trace( (J^T,J)^-1 ) """ D,P,N,M = 2,1,100,3 MJs = [UTPM(numpy.random.rand(D,P,N,M)),UTPM(numpy.random.rand(D,P,N,M))] cg = CGraph() FJs= [Function(MJ) for MJ in MJs] FM = Function(UTPM(numpy.zeros((D,P,M,M)))) for FJ in FJs: FJT = Function.transpose(FJ) FM += Function.dot(FJT, FJ) FC = Function.inv(FM) FPHI = Function.trace(FC) cg.independentFunctionList = FJs cg.dependentFunctionList = [FPHI] assert_array_equal(FPHI.shape, ()) # cg.pushforward(MJs) # pullback using the tracer PHIbar = UTPM(numpy.ones((D,P))) cg.pullback([PHIbar]) # # compute pullback by hand # Cbar = UTPM.pb_trace(PHIbar, FC.x, FPHI.x) # assert_array_almost_equal(Cbar.data, FC.xbar.data) # Mbar = UTPM.pb_inv(Cbar, FM.x, FC.x) # assert_array_almost_equal(Mbar.data, FM.xbar.data) # for FJ in FJs: # tmpbar = UTPM.pb_dot(Mbar, FJ.T.x, FJ.x, FM.x) # assert_array_almost_equal(tmpbar[1].data , FJ.xbar.data) # verifying pullback by ybar.T ydot == xbar.T xdot const1 = UTPM.dot(FPHI.xbar, UTPM.shift(FPHI.x,-1)) const2 = UTPM(numpy.zeros((D,P))) for nFJ, FJ in enumerate(FJs): const2 += UTPM.trace(UTPM.dot(FJ.xbar.T, UTPM.shift(FJ.x,-1))) assert_array_almost_equal(const1.data[0,:], const2.data[0,:])
def test_pullback_gradient(self): (D,M,N) = 3,3,2 P = M*N A = UTPM(numpy.zeros((D,P,M,M))) A0 = numpy.random.rand(M,N) for m in range(M): for n in range(N): p = m*N + n A.data[0,p,:M,:N] = A0 A.data[1,p,m,n] = 1. cg = CGraph() A = Function(A) Q,R = qr(A) B = dot(Q,R) y = trace(B) cg.independentFunctionList = [A] cg.dependentFunctionList = [y] # print cg # print y.x.data g1 = y.x.data[1] g11 = y.x.data[2] # print g1 ybar = y.x.zeros_like() ybar.data[0,:] = 1. cg.pullback([ybar]) for m in range(M): for n in range(N): p = m*N + n #check gradient assert_array_almost_equal(y.x.data[1,p], A.xbar.data[0,p,m,n]) #check hessian assert_array_almost_equal(0, A.xbar.data[1,p,m,n])
def test_utpm2base_and_dirs(self): D, P, N, M, K = 2, 3, 4, 5, 6 u = UTPM(numpy.arange(D * P * N * M * K).reshape((D, P, N, M, K))) x, V = utpm2base_and_dirs(u) assert_array_almost_equal(x, numpy.arange(N * M * K).reshape((N, M, K))) assert_array_almost_equal( V, numpy.arange(P * N * M * K, D * P * N * M * K).reshape( (D - 1, P, N, M, K)).transpose((2, 3, 4, 1, 0)))
def test_zeros_utpm_with_mpmath_instances_as_dtype(self): skiptest = False try: import mpmath except: skiptest = True if skiptest == False: x = UTPM(numpy.array([[mpmath.mpf(3)]])) A = zeros((2, 2), dtype=x) assert_equal(True, isinstance(A.data[0, 0, 0, 0], mpmath.mpf))
def test_eigh1_pullback(self): (D,P,N) = 2,1,2 A = UTPM(numpy.zeros((D,P,N,N))) A.data[0,0] = numpy.eye(N) A.data[1,0] = numpy.diag([3,4]) cg = CGraph() FA = Function(A) # print A FL,FQ,Fb = Function.eigh1(FA) cg.trace_off() cg.independentFunctionList = [FA] cg.dependentFunctionList = [FL] Lbar = UTPM.diag(UTPM(numpy.zeros((D,P,N)))) Lbar.data[0,0] = [0.5,0.5] # print cg cg.pullback([Lbar]) L = FL.x; Q = FQ.x; b = Fb.x assert_array_almost_equal(dot(Q, dot(L,Q.T)).data, A.data, decimal = 13) Qbar = UTPM(numpy.zeros((D,P,N,N))) Abar = UTPM.pb_eigh1( Lbar, Qbar, None, A, L, Q, b) assert_array_almost_equal(Abar.data, FA.xbar.data) Abar = Abar.data[0,0] Adot = A.data[1,0] Lbar = Lbar.data[0,0] Ldot = L.data[1,0] Qbar = Qbar.data[0,0] Qdot = Q.data[1,0] assert_almost_equal(numpy.trace(numpy.dot(Abar.T, Adot)), numpy.trace( numpy.dot(Lbar.T, Ldot) + numpy.dot(Qbar.T, Qdot)))
def test_reverse_on_getitem_setitem(self): cg = CGraph() D,P,N,M = 2,3,4,5 ax = UTPM(numpy.random.rand(D,P,N,M)) ay = UTPM(numpy.zeros((D,P,N,M))) fx = Function(ax) fy = Function(ay) for n in range(N): for m in range(M): fy[n,m] = fx[n,m] cg.independentFunctionList = [fx] cg.dependentFunctionList = [fy] assert_array_almost_equal(fx.x.data, fy.x.data) ybar = UTPM(numpy.zeros((D,P,N,M))) ybar.data[0,:,:,:] = 1. cg.pullback([ybar]) assert_almost_equal(ybar.data, fx.xbar.data)
def test_reverse_of_chained_dot(self): cg = CGraph() D,P,N = 1,1,2 ax = UTPM(numpy.random.rand(D,P,N)) ay = UTPM(numpy.random.rand(D,P,N)) fx = Function(ax) fy = Function(ay) fz = Function.dot(fx,fy) + Function.dot(fx,fy) cg.independentFunctionList = [fx] cg.dependentFunctionList = [fz] cg.pushforward([UTPM(numpy.random.rand(D,P,N))]) zbar = UTPM(numpy.zeros((D,P))) zbar.data[0,:] = 1. cg.pullback([zbar]) xbar_correct = 2*ay * zbar assert_array_almost_equal(xbar_correct.data, fx.xbar.data)
def test_pullback_gradient2(self): (D,P,M,N) = 3,9,3,3 A = UTPM(numpy.zeros((D,P,M,M))) A0 = numpy.random.rand(M,N) for m in range(M): for n in range(N): p = m*N + n A.data[0,p,:M,:N] = A0 A.data[1,p,m,n] = 1. cg = CGraph() A = Function(A) B = inv(A) y = trace(B) cg.independentFunctionList = [A] cg.dependentFunctionList = [y] ybar = y.x.zeros_like() ybar.data[0,:] = 1. cg.pullback([ybar]) g1 = y.x.data[1] g2 = A.xbar.data[0,0].ravel() assert_array_almost_equal(g1, g2) tmp = [] for m in range(M): for n in range(N): p = m*N + n tmp.append( A.xbar.data[1,p,m,n]) h1 = y.x.data[2] h2 = numpy.array(tmp) assert_array_almost_equal(2*h1, h2)
def test_simple_getitem(self): """ test: z = x*x by y = x[...] z = x * y """ D,P = 1,1 cg = CGraph() x = UTPM(numpy.random.rand(*(D,P))) Fx = Function(x) Fy = Fx[...] Fz = Fx * Fy cg.independentFunctionList = [Fx] cg.dependentFunctionList = [Fz] assert_array_almost_equal(Fz.x.data[0], x.data[0]**2) zbar = UTPM(numpy.zeros((D,P))) zbar.data[0,:] = 1. cg.pullback([zbar]) assert_array_almost_equal(Fx.x.data * 2, Fx.xbar.data)
def test_tracer_on_mixed_utpm_ndarray_mul(self): D, P = 1, 1 A = numpy.arange(2 * 2, dtype=float).reshape(2, 2) x = UTPM(numpy.zeros((D, P, 2, 2))) def f(x): return sum(A * x) cg = CGraph() ax = Function(x) ay = f(ax) cg.independentFunctionList = [ax] cg.dependentFunctionList = [ay] assert_array_almost_equal(A, cg.gradient(x))
def test_pullback_solve(self): """ test pullback on f = solve(A,x) """ (D,P,M,N,K) = 2,7,10,10,3 A_data = numpy.random.rand(D,P,M,N) # make A_data sufficiently regular for p in range(P): for n in range(N): A_data[0,p,n,n] += (N + 1) A = UTPM(A_data) x = UTPM(numpy.random.rand(D,P,N,K)) # STEP 1: tracing cg = CGraph() fA = Function(A) fx = Function(x) fy = Function.solve(fA,fx) cg.independentFunctionList = [fA] cg.dependentFunctionList = [fy] y = fy.x # STEP 2: pullback ybar_data = numpy.random.rand(*y.data.shape) ybar = UTPM(ybar_data) cg.pullback([ybar]) Abar = fA.xbar xbar = fx.xbar assert_array_almost_equal(x.data, UTPM.dot(A,y).data) for p in range(P): Ab = Abar.data[0,p] Ad = A.data[1,p] xb = xbar.data[0,p] xd = x.data[1,p] yb = ybar.data[0,p] yd = y.data[1,p] assert_almost_equal(numpy.trace(numpy.dot(Ab.T,Ad)) + numpy.trace(numpy.dot(xb.T,xd)), numpy.trace(numpy.dot(yb.T,yd)))
def test_pushforward_of_qr(self): cg = CGraph() D,P,N,M = 1,1,3,3 x = UTPM(numpy.random.rand(D,P,N,M)) fx = Function(x) f = Function.qr(fx) fQ,fR = f cg.independentFunctionList = [fx] cg.dependentFunctionList = [fQ,fR] x = UTPM(numpy.random.rand(D,P,N,M)) cg.pushforward([x]) Q = cg.dependentFunctionList[0].x R = cg.dependentFunctionList[1].x assert_array_almost_equal(x.data,UTPM.dot(Q,R).data)
def test_trace_utpm(self): D, P, N = 3, 4, 5 x = UTPM(numpy.random.rand(D, P, N, N)) assert_array_almost_equal(trace(x).data, UTPM.trace(x).data)
def test_inv_utpm(self): D, P, N = 3, 4, 5 x = UTPM(numpy.random.rand(D, P, N, N)) assert_array_almost_equal(inv(x).data, UTPM.inv(x).data)
def test_dot_utpm(self): D, P, N = 3, 4, 5 x = UTPM(numpy.random.rand(D, P, N, N)) y = UTPM(numpy.random.rand(D, P, N, N)) assert_array_almost_equal(dot(x, y).data, UTPM.dot(x, y).data)
def test_most_drivers(self): def f(x): return x[0]*x[1]*x[2] + 7*x[1] def g(x): out = algopy.zeros(3, dtype=x) out[0] = 2*x[0]**2 out[1] = 7*x[0]*x[1] out[2] = 23*x[0] + x[2] return out x = numpy.array([1,2,3],dtype=float) v = numpy.array([1,1,1],dtype=float) w = numpy.array([4,5,6],dtype=float) # forward mode gradient res1 = UTPM.extract_jacobian(f(UTPM.init_jacobian(x))) # forward mode Jacobian res2 = UTPM.extract_jacobian(g(UTPM.init_jacobian(x))) # forward mode Jacobian-vector res3 = UTPM.extract_jac_vec(g(UTPM.init_jac_vec(x, v))) # trace f cg = algopy.CGraph() fx = algopy.Function(x) fy = f(fx) cg.trace_off() cg.independentFunctionList = [fx] cg.dependentFunctionList = [fy] # trace g cg2 = algopy.CGraph() fx = algopy.Function(x) fy = g(fx) cg2.trace_off() cg2.independentFunctionList = [fx] cg2.dependentFunctionList = [fy] # reverse mode gradient res4 = cg.gradient(x) assert_array_almost_equal(numpy.array( [x[1]*x[2], x[0]*x[2]+7, x[0]*x[1]]), res4) # forward/reverse mode Hessian res5 = cg.hessian(x) assert_array_almost_equal(numpy.array( [[0, x[2], x[1]], [x[2], 0., x[0]], [x[1], x[0], 0]]), res5) # forward/reverse mode Hessian-vector res6 = cg.hess_vec(x,v) assert_array_almost_equal(numpy.dot(res5, v), res6) # reverese mode Jacobian res7 = cg2.jacobian(x) assert_array_almost_equal(numpy.array( [[4*x[0], 0, 0], [7*x[1], 7*x[0], 0], [23., 0, 1]]), res7) # reverse mode vector-Jacobian res8 = cg2.vec_jac(w,x) assert_array_almost_equal(numpy.dot(w,res7), res8) # forward mode Jacobian-vector res9 = cg2.jac_vec(x,v) assert_array_almost_equal(numpy.dot(res7,v), res9) # forward/reverse mode vector-Hessian-vector res10 = cg2.vec_hess_vec(w,x,v) assert_array_almost_equal(numpy.array([4*v[0]*w[0]+ 7*v[1]*w[1], 7*w[1], 0]), res10)
def test_zeros_like_utpm(self): D, P, N, M = 3, 4, 5, 6 x = UTPM(numpy.random.rand(*(D, P, N, M))) y = zeros_like(x) assert_array_almost_equal(numpy.zeros((D, P, N, M)), y.data)
def test_binary_function_utpm(self): D, P, N, M = 3, 4, 5, 6 x = UTPM(numpy.random.rand(*(D, P, N, M))) y = UTPM(numpy.random.rand(*(D, P, M, N))) assert_array_almost_equal(dot(x, y).data, UTPM.dot(x, y).data)
def test_unary_function_utpm(self): D, P, N = 3, 4, 5 x = UTPM(numpy.ones((D, P, N, N))) assert_array_almost_equal(trace(x).data, N * numpy.ones((D, P)))
# compute pullback WQ = Qbar_data WR = Rbar_data tic = time() out = AP.reverse([WQ, WR]) toc = time() runtime_pyadolc_pullback = toc - tic #---------------------------------------------- # STEP 2: # QR decomposition using LAPACK # using algopy for the differentiation #---------------------------------------------- # comute push forward A = UTPM( numpy.ascontiguousarray(A_data.transpose((3, 2, 0, 1)))) Q = UTPM(numpy.zeros((D, P, N, N))) R = UTPM(numpy.zeros((D, P, N, N))) tic = time() Q, R = UTPM.qr(A, out=(Q, R)) toc = time() runtime_algopy_push_forward = toc - tic # compute pullback Qbar = UTPM( numpy.ascontiguousarray(Qbar_data[0, ...].transpose( (3, 2, 0, 1)))) Rbar = UTPM( numpy.ascontiguousarray(Rbar_data[0, ...].transpose( (3, 2, 0, 1)))) tic = time()
out = AP.reverse([WQ, WR]) toc = time() runtime_pyadolc_pullback = toc - tic #---------------------------------------------- # STEP 2: # QR decomposition using LAPACK # using algopy for the differentiation #---------------------------------------------- # comute push forward A = UTPM(numpy.ascontiguousarray(A_data.transpose((3,2,0,1)))) Q = UTPM(numpy.zeros((D,P,N,N))) R = UTPM(numpy.zeros((D,P,N,N))) tic = time() Q,R = UTPM.qr(A, out = (Q,R)) toc = time() runtime_algopy_push_forward = toc - tic # compute pullback Qbar = UTPM(numpy.ascontiguousarray(Qbar_data[0,...].transpose((3,2,0,1)))) Rbar = UTPM(numpy.ascontiguousarray(Rbar_data[0,...].transpose((3,2,0,1)))) tic = time() Q,R = UTPM.qr(A) Abar = UTPM.pb_qr(Qbar, Rbar, A, Q, R) toc = time() runtime_algopy_pullback = toc - tic push_forward_ratio = runtime_algopy_push_forward/runtime_pyadolc_push_forward pullback_ratio = runtime_algopy_pullback/runtime_pyadolc_pullback print 'relative runtime of the push forward: algopy/pyadolc =', push_forward_ratio