def ad_Jacobian(F, x0): ''' Computes Jacobian using automatic differentiation ''' a_x = ad.independent(x0) a_F = F(a_x) return ad.adfun(a_x, a_F).jacobian(x0)
def MakeParameterization(plus_func, x0, delta0): global_size = len(x0) local_size = len(delta0) in_var = pycppad.independent( np.hstack( [x0, delta0] ) ) x_part, delta_part = np.split(in_var, [global_size]) out_var = plus_func( x_part, delta_part ) jac_func = pycppad.adfun(in_var, out_var).jacobian def jac_delta(x): inp = np.hstack( [x, np.zeros(local_size) ]) J = check_nan(jac_func(inp)) return J[:, global_size:] class AutoDiffLocalParameterization(LocalParameterization): def __init__(self): super(AutoDiffLocalParameterization, self).__init__() def Plus(self, x, delta): return plus_func(x, delta) def ComputeJacobian(self, x): return jac_delta(x) def GlobalSize(self): return global_size def LocalSize(self): return local_size return AutoDiffLocalParameterization
def _create_tape(self, xVec): """ Generate main CppAD tape Normally there is no need to call this function manually as tapes are generated as needed. """ # Create derivative vector a_xVec = ad.independent(xVec) # perform actual calculation # Linear contribution a_out = np.dot(self.G, a_xVec) xin = np.zeros(len(xVec), dtype = type(a_out[0])) # Nonlinear contribution for elem in self.ckt.nD_nlinElem: # first have to retrieve port voltages from a_xVec xin[:len(elem.controlPorts)] = 0. #import pdb; pdb.set_trace() set_xin(xin, elem.nD_vpos, elem.nD_vneg, a_xVec) (outV, qVec) = elem.eval_cqs(xin) # Update iVec. outV may have extra charge elements but # they are not used in the following set_i(a_out, elem.nD_cpos, elem.nD_cneg, outV) # Save main function tape self._func = ad.adfun(a_xVec, a_out) # optimize main function tape self._func.optimize()
def ad_Jacobian(F,x0): ''' Computes Jacobian using automatic differentiation ''' a_x = ad.independent(x0) a_F = F(a_x) return ad.adfun(a_x,a_F).jacobian(x0)
def _create_tape(self, xVec): """ Generate main CppAD tape Normally there is no need to call this function manually as tapes are generated as needed. """ # Create derivative vector a_xVec = ad.independent(xVec) # perform actual calculation # Linear contribution a_out = np.dot(self.G, a_xVec) xin = np.zeros(len(xVec), dtype=type(a_out[0])) # Nonlinear contribution for elem in self.ckt.nD_nlinElem: # first have to retrieve port voltages from a_xVec xin[:len(elem.controlPorts)] = 0. #import pdb; pdb.set_trace() set_xin(xin, elem.nD_vpos, elem.nD_vneg, a_xVec) (outV, qVec) = elem.eval_cqs(xin) # Update iVec. outV may have extra charge elements but # they are not used in the following set_i(a_out, elem.nD_cpos, elem.nD_cneg, outV) # Save main function tape self._func = ad.adfun(a_xVec, a_out) # optimize main function tape self._func.optimize()
def _trace_cons(self, x): ax = pycppad.independent(x) ay = self.cons(ax) if not isinstance(ay, np.ndarray): ay = np.array([ay]) self._cppad_adfun_cons = pycppad.adfun(ax, ay)
def _trace_cons_pos(self, x): ax = pycppad.independent(x) ay = self.cons_pos(ax) if not isinstance(ay, np.ndarray): ay = np.array([ay]) self._cppad_adfun_cons_pos = pycppad.adfun(ax, ay)
def _trace_lag(self, x, z): if self.m == 0 and self.nbounds == 0: self._cppad_adfun_lag = self._cppad_adfun_obj return axz = pycppad.independent(np.concatenate((x, z))) ax = axz[:self.nvar] az = axz[self.nvar:] ay = self.lag(ax, az) self._cppad_adfun_lag = pycppad.adfun(axz, np.array([ay]))
def _add_constraint(self, g, g_jac, x_blocks, l_blocks): xl_vec = [b.array for b in x_blocks + l_blocks] xl_sizes = [len(vec) for vec in xl_vec] """ 1. Generate Jacobian function by cppad if g_jac is not supplied""" if g_jac is None: xl_indices = np.cumsum(xl_sizes)[:-1] var = np.hstack(xl_vec) var_in = pycppad.independent(var) var_out = np.atleast_1d(g(*np.split(var_in, xl_indices))) var_jacobian = pycppad.adfun(var_in, var_out).jacobian def g_jac(*vec): J = var_jacobian(np.hstack(vec)) return np.split(J, xl_indices, axis=1) """ 2. Sanity check of size and validation""" tmp_res = np.atleast_1d(g(*xl_vec)) tmp_jac = list(g_jac(*xl_vec)) inequal_size = [ j.shape != (len(tmp_res), size) for j, size in zip(tmp_jac, xl_sizes) ] if len(tmp_jac) != len(xl_sizes) or np.any(inequal_size): raise RuntimeError("Jacobian Size Not fit") valid_value = [ np.isfinite(m).all() and not np.all(m == 0) for m in [tmp_res] + tmp_jac ] if not np.all(valid_value): raise RuntimeError("return value of function Not valid") dim_res = len(tmp_res) """ 3. Make and append compound vector for constraint residual """ res_off, res_vec = self.cv_res.NewSegment(dim_res) """ 4. Generate functor that use the mapped vectors to calcuate residual and jacobians""" def g_residual(): res_vec[:] = g(*xl_vec) def g_jacobians(): jac = list(g_jac(*xl_vec)) jac.reverse() # reversed, to pop(-1) instead of pop(0) for dm in dms: dm.Write(check_allzero(jac.pop())) """ 5. Make new DenseMatrix that will hold the jacobians """ dms = [] for b in x_blocks + l_blocks: #'array', 'dim', 'isfixed', 'param', 'jac' new_dm = DenseMatrix(res_off, 0) new_dm.shape = [dim_res, 0] b.jac.append(new_dm) dms.append(new_dm) """ 6. new record in the system""" self.constraint_blocks.append( GaussHelmertProblem.ConstraintBlock(res_off, g_residual, g_jacobians))
def create_OP_tape(dev, vPort): """ Generate operating point CppAD tape Normally there is no need to call this function manually as tapes are generated as needed. """ assert dev.isNonlinear a_vPort = ad.independent(vPort) (i_out, q_out, a_opvars) = dev.eval_cqs(a_vPort, saveOP=True) # Save operating point variable tape dev._opfunc = ad.adfun(a_vPort, a_opvars)
def setParams(self, params): ''' Save parameters in the object and create automatic differentiation object ''' self.A = params['A'] self.w = params['w'] self.mu = params['mu'] ax = pcad.independent(np.zeros(self.w.shape[0]*2)) ay = self.oscillator(ax) self.gf = pcad.adfun(ax, ay) self.gf.optimize()
def ad_Hessian(F, x0): ''' Computes Hessian of F using automatic differentiation ''' a_x = ad.independent(x0) a_F = F(a_x) n = x0.shape[0] m = a_F.shape[0] HF = np.empty((m, n, n)) I = np.eye(m) adF = ad.adfun(a_x, a_F) for i in range(m): HF[i, :, :] = adF.hessian(x0, I[i]) return HF
def MakeJacobianFunction(g, *args): arg_sizes = [len(np.atleast_1d(vec)) for vec in args] arg_indices = np.cumsum(arg_sizes)[:-1] var = np.hstack(args) var_in = pycppad.independent(var) var_out = np.atleast_1d(g(*np.split(var_in, arg_indices))) var_jacobian = pycppad.adfun(var_in, var_out).jacobian def g_jac_auto(*vec): J = var_jacobian(np.hstack(vec)) check_nan(J) check_allzero(J) return np.split(J, arg_indices, axis=1) return g_jac_auto
def ad_Hessian(F,x0): ''' Computes Hessian of F using automatic differentiation ''' a_x = ad.independent(x0) a_F = F(a_x) n = x0.shape[0] m = a_F.shape[0] HF = np.empty((m,n,n)) I = np.eye(m) adF = ad.adfun(a_x,a_F) for i in range(m): HF[i,:,:] = adF.hessian(x0,I[i]) return HF
def create_tape(dev, vPort): """ Generate main CppAD tape Normally there is no need to call this function manually as tapes are generated as needed. """ #import pdb; pdb.set_trace() assert dev.isNonlinear # Create derivative vector a_vPort = ad.independent(vPort) # perform actual calculation (i_out, q_out) = dev.eval_cqs(a_vPort) # Concatenate vectors as we want only one tape to be generated a_out = np.concatenate((i_out, q_out), axis=0) # Save main function tape dev._func = ad.adfun(a_vPort, a_out) # optimize main function tape dev._func.optimize()
def ode_function(num_step, age_local, all_local, scipy=False): global age, incidence, remission, excess, all_cause global susceptible, condition if scipy == False: N = len(age_local) age = age_local all_cause = all_local susceptible = pycppad.ad(numpy.zeros(N)) condition = pycppad.ad(numpy.zeros(N)) incidence = .00 * numpy.ones(N) remission = .00 * numpy.ones(N) excess = .00 * numpy.ones(N) s0 = 0. c0 = 0. x = numpy.hstack((incidence, remission, excess, s0, c0)) x = pycppad.independent(x) incidence = x[(0 * N):(1 * N)] remission = x[(1 * N):(2 * N)] excess = x[(2 * N):(3 * N)] s0 = x[3 * N] c0 = x[3 * N + 1] ode_integrate(N, num_step, s0, c0) y = numpy.hstack((susceptible, condition)) fun = pycppad.adfun(x, y) return fun if scipy == True: res = integrate.solve_ivp(fun=odefun, t_span=(age[0], age[-1]), y0=[s0, c0], method='RK45', t_eval=age) print(res.message) s = res.y[0, :] c = res.y[1, :] return s, c
def ode_function(num_step, age_local, all_local) : global age, incidence, remission, excess, all_cause global susceptible, condition N = len( age_local ) age = age_local all_cause = all_local susceptible = pycppad.ad( numpy.zeros(N) ) condition = pycppad.ad( numpy.zeros(N) ) incidence = .00 * numpy.ones(N) remission = .00 * numpy.ones(N) excess = .00 * numpy.ones(N) s0 = 0. c0 = 0. x = numpy.hstack( (incidence, remission, excess, s0, c0) ) x = pycppad.independent( x ) incidence = x[(0*N):(1*N)] remission = x[(1*N):(2*N)] excess = x[(2*N):(3*N)] s0 = x[3*N] c0 = x[3*N+1] ode_integrate(N, num_step, s0, c0) y = numpy.hstack( (susceptible, condition) ) fun = pycppad.adfun(x, y) return fun
def ode_function(num_step, age_local, all_local): global age, incidence, remission, excess, all_cause global susceptible, condition N = len(age_local) age = age_local all_cause = all_local susceptible = pycppad.ad(numpy.zeros(N)) condition = pycppad.ad(numpy.zeros(N)) incidence = .00 * numpy.ones(N) remission = .00 * numpy.ones(N) excess = .00 * numpy.ones(N) s0 = 0. c0 = 0. x = numpy.hstack((incidence, remission, excess, s0, c0)) x = pycppad.independent(x) incidence = x[(0 * N):(1 * N)] remission = x[(1 * N):(2 * N)] excess = x[(2 * N):(3 * N)] s0 = x[3 * N] c0 = x[3 * N + 1] ode_integrate(N, num_step, s0, c0) y = numpy.hstack((susceptible, condition)) fun = pycppad.adfun(x, y) return fun
def _trace_obj(self, x): ax = pycppad.independent(x) ay = self.obj(ax) self._cppad_adfun_obj = pycppad.adfun(ax, np.array([ay]))
if __name__ == "__main__": N_max = 120 reps = 100 Ns = list(range(2,N_max,5)) adolc_gradient_runtimes = [] cppad_gradient_runtimes = [] adolc_hessian_runtimes = [] cppad_hessian_runtimes = [] for N in Ns: # cppad timing x = numpy.zeros(N, dtype=float) ax = pycppad.independent(x) atmp = [] for n in range(N): atmp.append(numpy.sin( numpy.sum(ax[:n]))) ay = numpy.array( [ ax[0] * numpy.sin( numpy.sum(atmp)) ] ) f = pycppad.adfun(ax, ay) x = numpy.random.rand(N) w = numpy.array( [ 1.] ) # compute Hessian of x0 * sin(x1) cppad_hessian_runtime = timeit.Timer('f.hessian(x, w)', 'from __main__ import f,x,w').timeit(number=reps)/reps cppad_gradient_runtime = timeit.Timer('f.jacobian(x)', 'from __main__ import f,x').timeit(number=reps)/reps # adolc timing x = numpy.zeros(N, dtype=float) adolc.trace_on(0)
if __name__ == "__main__": N_max = 120 reps = 100 Ns = range(2, N_max, 5) adolc_gradient_runtimes = [] cppad_gradient_runtimes = [] adolc_hessian_runtimes = [] cppad_hessian_runtimes = [] for N in Ns: # cppad timing x = numpy.zeros(N, dtype=float) ax = pycppad.independent(x) atmp = [] for n in range(N): atmp.append(numpy.sin(numpy.sum(ax[:n]))) ay = numpy.array([ax[0] * numpy.sin(numpy.sum(atmp))]) f = pycppad.adfun(ax, ay) x = numpy.random.rand(N) w = numpy.array([1.]) # compute Hessian of x0 * sin(x1) cppad_hessian_runtime = timeit.Timer( 'f.hessian(x, w)', 'from __main__ import f,x,w').timeit(number=reps) / reps cppad_gradient_runtime = timeit.Timer( 'f.jacobian(x)', 'from __main__ import f,x').timeit(number=reps) / reps
def create_sensitivity_tape(device, parList, inVec): """ Create sensitivity AD tape Inputs: device: device with nodal attributes parList: list of parameters to calculate sensitivities inVec: input vector including parameters and perhaps nonlinear control voltages at the end of the vector Side effects: operating point attributes lost in internal terminals """ # Should store some parameter information for safety # # Create AD tape ----------------------------------------------------- # a_inVec = ad.independent(inVec) # set adouble attributes: a_inVec may be longer than parList but # zip() truncates to the shortest list for item, a_val in zip(parList, a_inVec): setattr(device, item[0], a_val) # Re-calculate coductances / parameters linVec = np.empty(shape=0) device.process_params() if device.linearVCCS: gList = [] for vccs in device.linearVCCS: # Linear output current g = vccs[2] # Kludge: Make sure all elements are of the correct type if type(g) != ad.cppad_.a_float: g = ad.cppad_.a_float(g) gList.append(g) # Overwrite output vector linVec = np.array(gList) #import pdb; pdb.set_trace() # Later add for time- and frequency- domain if device.isDCSource: val = device.get_DCsource() # Kludge: Make sure all elements are of the correct type if type(val) != ad.cppad_.a_float: val = ad.cppad_.a_float(val) valVec = np.array([val]) sourceVec = np.concatenate((linVec, valVec), axis=0) else: sourceVec = linVec if device.isNonlinear: # calculate nonlinear currents and concatenate to output (iVec, qVec) = device.eval_cqs(a_inVec[-device.nD_nxin:]) # # Kludge: Make sure all elements are of the correct type (not needed?) # for k,i in enumerate(iVec): # if type(i) != ad.cppad_.a_float: # iVec[k] = ad.cppad_.a_float(i) a_outVec = np.concatenate((sourceVec, iVec), axis=0) else: # Just copy whatever we have a_outVec = sourceVec # tape stored in f f = ad.adfun(a_inVec, a_outVec) f.optimize() # Restore element to original state device.clean_attributes() device.set_attributes() device.process_params() nd.restore_RCnumbers(device) # End of crate tape ------------------------------------------------ return f