def base_and_dirs2utpm(x, V): """ x_utpm = base_and_dirs2utpm(x,V) where x_utpm is an instance of UTPM V.shape = x.shape + (P,D) then x_utpm.data.shape = (D+1,P) = x.shape """ x = numpy.asarray(x) V = numpy.asarray(V) xshp = x.shape Vshp = V.shape P, D = Vshp[-2:] Nxshp = len(xshp) NVshp = len(Vshp) numpy.testing.assert_array_equal(xshp, Vshp[:-2], err_msg='x.shape does not match V.shape') tc = numpy.zeros((D + 1, P) + xshp) for p in range(P): tc[0, p, ...] = x[...] axes_ids = tuple(numpy.arange(NVshp)) tc[1:, ...] = V.transpose((axes_ids[-1], axes_ids[-2]) + axes_ids[:-2]) return algopy.UTPM(tc)
def vec_jac(self, w, x): """ computes the Jacobian-vector product w^T*J of a function F:R^N --> R^M in the reverse mode wJ = self.vec_jac(w, x) Parameters ---------- x: array_like x.ndim = 1 w: array_like w.ndim = 1 Returns ------- wJ: array_like the vector-Jacobian product """ x = numpy.asarray(x) w = numpy.asarray(w) if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) if w.ndim != 1: raise ValueError("w.ndim must be 1 but provided %d"%w.ndim) M = self.dependentFunctionList[0].size tmp = numpy.zeros((1,1) + numpy.shape(x)) tmp[0,...] = x utpm_x_list = [algopy.UTPM(tmp)] self.pushforward(utpm_x_list) ybar = algopy.UTPM(numpy.zeros((1,1,M))) ybar.data[0,0,:] = w self.pullback([ybar]) return self.independentFunctionList[0].xbar.data[0,0,...]
def vec_hess_vec(self, w, x, v): """ computes d^2(w F) v, where F:R^N ---> R^M lHv = self.vec_hess_mat(w, x, v) Parameters ---------- lagra: array_like "Lagrange multipliers" x: array_like x.ndim = 1, base point v: array_like v.ndim = 1 input directions Returns ------- lHv: array two-dimensional array containing the result """ x = numpy.asarray(x) v = numpy.asarray(v) w = numpy.asarray(w) if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) if v.ndim != 1: raise ValueError("v.ndim must be 1 but provided %d"%v.ndim) if w.ndim != 1: raise ValueError("w.ndim must be 1 but provided %d"%w.ndim) if x.shape != v.shape or x.shape != w.shape: raise ValueError("x.shape must be the same as v.shape or v.shape, but provided x.shape=%s, v.shape=%s and w.shape=%s"%(x.shape, v.shape, w.shape)) # raise NotImplementedError('this function does not work correctly yet') xtmp = numpy.zeros((2,1) + x.shape) xtmp[0,:] = x; xtmp[1,...] = v xtmp = algopy.UTPM(xtmp) self.pushforward([xtmp]) ybar = self.dependentFunctionList[0].x.zeros_like() ybar.data[0,0,...] = w self.pullback([ybar]) return self.independentFunctionList[0].xbar.data[1,0,:]
def hess_vec(self, x, v): """ computes the Hessian vector product dot(H,v) Hv = self.hess_vec(x, v) Parameters ---------- x: array_like x.ndim == 1 v: array_like x.ndim == 1 Returns ------- Hv: array one-dimensional array containing the Hessian vector product """ x = numpy.asarray(x) v = numpy.asarray(v) if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) if v.ndim != 1: raise ValueError("v.ndim must be 1 but provided %d"%v.ndim) if x.shape != v.shape: raise ValueError("x.shape must be the same as v.shape, but provided x.shape=%s and v.shape=%s"%(x.shape, v.shape)) xtmp = numpy.zeros((2,1) + numpy.shape(x)) xtmp[0,0] = x; xtmp[1,0] = v xtmp = algopy.UTPM(xtmp) self.pushforward([xtmp]) ybar = self.dependentFunctionList[0].x.zeros_like() ybar.data[0,:] = 1. self.pullback([ybar]) return self.independentFunctionList[0].xbar.data[1,0]
def jac_vec(self, x, v): """ computes the Jacobian-vector product J*v of a function F:R^N --> R^M in the forward mode Jv = self.jac_vec(x, v) Parameters ---------- x: array_like x.ndim = 1 v: array_like w.ndim = 1 Returns ------- Jv: array_like the Jacobian-vector product """ x = numpy.asarray(x) v = numpy.asarray(v) if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) if v.ndim != 1: raise ValueError("v.ndim must be 1 but provided %d"%v.ndim) if x.shape != v.shape: raise ValueError("x.shape must be the same as v.shape but provided x.shape=%s, v.shape=%s "%(x.shape, v.shape)) N = self.independentFunctionList[0].size tmp = numpy.zeros((2,1) + numpy.shape(x)) tmp[0,...] = x tmp[1,0,...] = v utpm_x_list = [algopy.UTPM(tmp)] self.pushforward(utpm_x_list) return self.dependentFunctionList[0].x.data[1,0,...]
def f_fcn(x): A = algopy.zeros((2, 2), dtype=x) A[0, 0] = x[0] A[1, 0] = x[1] * x[0] A[0, 1] = x[1] Q, R = algopy.qr(A) return R[0, 0] # Method 1: Complex-step derivative approximation (CSDA) h = 10**-20 x0 = numpy.array([3, 2], dtype=float) x1 = numpy.array([1, 0]) yc = numpy.imag(f_fcn(x0 + 1j * h * x1) - f_fcn(x0 - 1j * h * x1)) / (2 * h) # Method 2: univariate Taylor polynomial arithmetic (UTP) ax = algopy.UTPM(numpy.zeros((2, 1, 2))) ax.data[0, 0] = x0 ax.data[1, 0] = x1 ay = f_fcn(ax) # Method 3: finite differences h = 10**-8 yf = (f_fcn(x0 + h * x1) - f_fcn(x0)) / h # Print results print('CSDA result =', yc) print('UTP result =', ay.data[1, 0]) print('FD result =', yf)
dirs[count + 1] += Id[n1] dirs[count + 1] -= Id[n2] count += 2 print('dirs =') print(dirs) # STEP 2: use these directions to initialize the UTPM instance xdata = numpy.zeros((D, 2 * N * N, N)) xdata[0] = [1, 2, 3] xdata[1] = dirs # STEP 3: compute function F in UTP arithmetic x = algopy.UTPM(xdata) y = eval_F(x) # STEP 4: use polarization identity to build univariate Taylor polynomial # of the Jacobian J Jdata = numpy.zeros((D - 1, N, N, N)) count, count2 = 0, 0 # build J_0 for n in range(N): Jdata[0, :, :, n] = y.data[1, 2 * n * (N + 1), :] / 2. # build J_1 count = 0 for n1 in range(N): for n2 in range(N):
def jacobian(self, x): """ computes the Jacobian of a function F:R^N --> R^M in the reverse mode J = self.jacobian(x) If x is a UTPM instance, the Taylor series of the entries of the Jacobian are computed. Parameters ---------- x: array_like or UTPM instance x.ndim = 1 Returns ------- J: array_like or UTPM instance the Jacobian evaluated at x J.ndim = 2 when M>1 and J.ndim = 1 when M == 1 Example ------- import algopy def f(x): return x**2 cg = algopy.CGraph() x = algopy.Function(numpy.array([3.,7.])) y = f(x) cg.trace_off() cg.independentFunctionList = [x] cg.dependentFunctionList = [y] print cg.jacobian(numpy.array([1.,2.])) """ if len(self.independentFunctionList) != 1: err_str = 'len(self.independentFunctionList) must be 1 but provided %d' % \ len(self.independentFunctionList) raise ValueError(err_str) if len(self.dependentFunctionList) != 1: err_str = 'len(self.dependentFunctionList) must be 1 but provided %d' % \ len(self.dependentFunctionList) raise ValueError(err_str) if isinstance(x, algopy.UTPM): if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) M = self.dependentFunctionList[0].size D,P = x.data.shape[:2] shp = x.shape # if P != 1: # raise ValueError("x.data.shape[1] must be 1, but provided %d" % x.data.shape[1]) tmp = numpy.zeros((D,M*P) + x.shape) for p in range(P): tmp[:, p*M:(p+1)*M, ...] = x.data[:, p:p+1, ...] utpm_x_list = [algopy.UTPM(tmp)] self.pushforward(utpm_x_list) ybar = algopy.UTPM(numpy.zeros((D, P*M, M))) for p in range(P): ybar.data[0, p*M:(p+1)*M, :] = numpy.eye(M) self.pullback([ybar]) return algopy.UTPM(self.independentFunctionList[0].xbar.data.reshape((D, P, M) + shp)) else: x = numpy.asarray(x) if x.ndim != 1: raise ValueError("x.ndim must be 1 but provided %d"%x.ndim) M = self.dependentFunctionList[0].size tmp = numpy.zeros((1,M) + numpy.shape(x)) tmp[0,...] = x utpm_x_list = [algopy.UTPM(tmp)] self.pushforward(utpm_x_list) ybar = algopy.UTPM(numpy.zeros((1,M,M))) ybar.data[0,:,:] = numpy.eye(M) self.pullback([ybar]) return self.independentFunctionList[0].xbar.data[0,:]
def gradient(self, x): """ computes the gradient of a function f: R^N --> R g = gradient(self, x_list) Parameters ---------- x: array_like or list of array_like Example 1 --------- import algopy def f(x): return x**2 cg = algopy.CGraph() x = algopy.Function(3.) y = f(x) cg.trace_off() cg.independentFunctionList = [x] cg.dependentFunctionList = [y] print(cg.gradient(7.)) Example 2 --------- import algopy def f(x): return x[0]*x[1] cg = algopy.CGraph() x = algopy.Function([3., 7.]) y = f(x) cg.trace_off() cg.independentFunctionList = [x] cg.dependentFunctionList = [y] print(cg.gradient([1.,2.])) """ if self.dependentFunctionList[0].ndim != 0: raise Exception('you are trying to compute the gradient of a non-scalar valued function') if isinstance(x, list): if isinstance(x[0], numpy.ndarray) or isinstance(x[0], list): x_list = x else: x_list = [numpy.asarray(x)] else: x_list = [x] utpm_x_list = [] for xi in x_list: element = numpy.asarray(xi).reshape((1,1) + numpy.shape(xi)) utpm_x_list.append(algopy.UTPM(element)) self.pushforward(utpm_x_list) ybar = self.dependentFunctionList[0].x.zeros_like() ybar.data[0,:] = 1. self.pullback([ybar]) if isinstance(x, list): return [x.xbar.data[0,0] for x in self.independentFunctionList] else: return self.independentFunctionList[0].xbar.data[0,0]
# "defect_QR_minus_A": (algopy.dot(Q,R) - A).data, # "defect_QTQ_minus_Id": (algopy.dot(Q.T,Q) - numpy.eye(M)).data # }) import mpmath mpmath.mp.prec = 200 # increase lenght of mantissa print(mpmath.mp) print('QR decomposition based on Householder') D,P,M,N = 50,1,3,2 # in float64 arithmetic data = numpy.random.random((D,P,M,N)) data = numpy.asarray(data, dtype=numpy.float64) A = algopy.UTPM(data) Q,R = qr_house(A.copy()) # in multiprecision arithmetic data2 = numpy.asarray(data)*mpmath.mpf(1) A2 = algopy.UTPM(data2) Q2,R2 = qr_house(A2.copy()) print('-'*20) print(A.data[-1]) print('-'*20) print((Q.data[-1] - Q2.data[-1])/Q2.data[-1]) print('-'*20) print(algopy.dot(Q, R).data[-1] - A.data[-1]) print('-'*20) print(algopy.dot(Q2, R2).data[-1] - A2.data[-1])
import numpy, algopy from IPython.core.debugger import set_trace x = algopy.UTPM(numpy.ones((2, 1))) y = algopy.UTPM(2 * numpy.ones((2, 1))) # right z = algopy.zeros(2, dtype=x) z[0] = x z[1] = y set_trace()