def test_Collocation_2(): # Full 2PBVP test problem # This is calculating the 4th eigenvalue of Mathieu's Equation # This problem contains an adjustable parameter. def odefun(t, X, p, const): return (X[1], -(p[0] - 2 * 5 * np.cos(2 * t)) * X[0]) def bcfun(t0, X0, q0, tf, Xf, qf, p, ndp, aux): return (X0[1], Xf[1], X0[0] - 1) algo = Collocation(odefun, None, bcfun) solinit = Solution() solinit.t = np.linspace(0, np.pi, 30) solinit.y = np.vstack( (np.cos(4 * solinit.t), -4 * np.sin(4 * solinit.t))).T solinit.dynamical_parameters = np.array([15]) out = algo.solve(solinit) assert abs(out.t[-1] - np.pi) < tol assert abs(out.y[0][0] - 1) < tol assert abs(out.y[0][1]) < tol assert abs(out.y[-1][0] - 1) < tol assert abs(out.y[-1][1]) < tol assert abs(out.dynamical_parameters[0] - 17.09646175) < tol
def guess_mapper(sol): n_c = len(constants_of_motion) if n_c == 0: return sol sol_out = Solution() sol_out.t = copy.copy(sol.t) sol_out.y = np.array([[fn(*sol.y[0]) for fn in states_2_states_fn]]) sol_out.q = sol.q if len(quads) > 0: sol_out.q = -0.0 * np.array([np.ones((len(quads)))]) sol_out.dynamical_parameters = sol.dynamical_parameters sol_out.dynamical_parameters[-n_c:] = np.array( [fn(*sol.y[0]) for fn in states_2_constants_fn]) sol_out.nondynamical_parameters = sol.nondynamical_parameters sol_out.aux = sol.aux return sol_out
def test_Shooting_3(): # This problem contains a parameter, but it is not explicit in the BCs. # Since time is buried in the ODEs, this tests if the BVP solver calculates # sensitivities with respect to parameters. def odefun(t, X, p, const): return 1 * p[0] def bcfun(t0, X0, q0, tf, Xf, qf, p, ndp, aux): return (X0[0] - 0, Xf[0] - 2) algo = Shooting(odefun, None, bcfun) solinit = Solution() solinit.t = np.linspace(0, 1, 2) solinit.y = np.array([[0], [0]]) solinit.dynamical_parameters = np.array([1]) out = algo.solve(solinit) assert abs(out.dynamical_parameters - 2) < tol
def test_Shooting_1(): # Full 2PBVP test problem # This is the simplest BVP def odefun(t, X, p, const): return (X[1], -abs(X[0])) def bcfun(t0, X0, q0, tf, Xf, qf, p, ndp, aux): return (X0[0], Xf[0]+2) algo = Shooting(odefun, None, bcfun) solinit = Solution() solinit.t = np.linspace(0,4,2) solinit.y = np.array([[0,1],[0,1]]) out = algo.solve(solinit) assert out.y[0][0] < tol assert out.y[0][1] - 2.06641646 < tol assert out.y[-1][0] + 2 < tol assert out.y[-1][1] + 2.87588998 < tol assert out.t[-1] - 4 < tol assert abs(out.y[0][1] - solinit.y[0][1]) > tol assert abs(out.y[-1][0] - solinit.y[-1][0]) - 2 < tol
def test_Shooting_4(): # This problem contains a quad and tests if the bvp solver correctly # integrates the quadfun. def odefun(t, x, p, const): return -x[1], x[0] def quadfun(t, x, p, const): return x[0] def bcfun(t0, X0, q0, tf, Xf, qf, params, ndp, aux): return X0[0], X0[1] - 1, qf[0] - 1.0 algo = Shooting(odefun, quadfun, bcfun) solinit = Solution() solinit.t = np.linspace(0, np.pi / 2, 2) solinit.y = np.array([[1, 0], [1, 0]]) solinit.q = np.array([[0], [0]]) out = algo.solve(solinit) assert (out.y[0,0] - 0) < tol assert (out.y[0,1] - 1) < tol assert (out.q[0,0] - 2) < tol assert (out.q[-1,0] - 1) < tol
def load(self): """ Loads solution data using dill if not already loaded """ if not self.is_loaded: logging.info("Loading datafile " + self.filename + "...") out = loadmat(self.filename) if 'output' in out: out = out['output']['result'][0][0][0][0] soldata = out['solution']['phase'][0][0][0][0] # if 'solution' not in self._data: # self.is_loaded = False # logging.error("Solution missing in data file :"+self.filename) # raise RuntimeError("Solution missing in data file :"+self.filename) # if 'problem_data' not in self._data: # self.is_loaded = False # logging.error("Problem data missing in data file :"+self.filename) # raise RuntimeError("Problem data missing in data file :"+self.filename) # _sol = Solution() tf = max(soldata['time']) _sol.x = soldata['time'][:, 0] / tf _sol.y = np.r_[soldata['state'].T, soldata['costate'].T, np.ones_like(soldata['time']).T * tf] _sol.u = soldata['control'].T if 'tf' not in self.problem_data['state_list']: self.problem_data['state_list'] = tuple( self.problem_data['state_list']) + ('tf', ) _sol.arcs = ((0, len(_sol.x) - 1), ) if self._const is not None: _sol.aux = {'const': self._const} self._sol = [[_sol]] logging.info('Loaded solution from data file') self.is_loaded = True
def solve(ocp, method, bvp_algorithm, steps, guess_generator): """ Solves the OCP using specified method """ output_file = config['output_file'] logging.info("Computing the necessary conditions of optimality") if method.lower() == 'traditional' or method.lower() == 'brysonho': ocp_ws = BH_ocp_to_bvp(ocp, guess_generator) elif method.lower() == 'icrm': ocp_ws = ICRM_ocp_to_bvp(ocp, guess_generator) else: raise NotImplementedError ocp_ws['problem'] = ocp ocp_ws['guess'] = guess_generator ocp_ws['problem_data']['custom_functions'] = ocp.custom_functions() solinit = Solution() solinit.aux['const'] = OrderedDict( (str(const.name), float(const.value)) for const in ocp_ws['constants']) solinit.aux['dynamical_parameters'] = [ str(p) for p in ocp_ws['problem_data']['dynamical_parameters'] ] solinit.aux['nondynamical_parameters'] = [ str(p) for p in ocp_ws['problem_data']['nondynamical_parameters'] ] bvp = preprocess(ocp_ws['problem_data']) solinit = ocp_ws['guess'].generate(bvp, solinit) state_names = ocp_ws['problem_data']['state_list'] initial_states = solinit.y[0, :] terminal_states = solinit.y[-1, :] initial_bc = dict(zip(state_names, initial_states)) terminal_bc = dict(zip(state_names, terminal_states)) solinit.aux['initial'] = initial_bc solinit.aux['terminal'] = terminal_bc tic() # TODO: Start from specific step for restart capability # TODO: Make class to store result from continuation set? out = dict() out['problem_data'] = ocp_ws['problem_data'] ocp._scaling.initialize(ocp_ws) ocp_ws['scaling'] = ocp._scaling out['solution'] = run_continuation_set(ocp_ws, bvp_algorithm, steps, solinit, bvp) total_time = toc() logging.info('Continuation process completed in %0.4f seconds.\n' % total_time) bvp_algorithm.close() # Final time is appended as a parameter, so scale the output x variables to show the correct time for continuation_set in out['solution']: for sol in continuation_set: tf_ind = [ i for i, s in enumerate(out['problem_data'] ['dynamical_parameters']) if str(s) is 'tf' ][0] tf = sol.dynamical_parameters[tf_ind] sol.t = sol.t * tf # Save data # del out['problem_data']['s_list'] del out['problem_data']['states'] del out['problem_data']['costates'] qvars = out['problem_data']['quantity_vars'] qvars = {str(k): str(v) for k, v in qvars.items()} out['problem_data']['quantity_vars'] = qvars with open(output_file, 'wb') as outfile: pickle.dump(out, outfile) return out['solution'][-1][-1]
def solve(ocp, method, bvp_algorithm, steps, guess_generator, output_file='data.dill'): """ Solves the OCP using specified method """ # Initialize necessary conditions of optimality object # print("Computing the necessary conditions of optimality") logging.info("Computing the necessary conditions of optimality") from beluga.optimlib import methods # TODO: Load oc method by name wf = methods[method] # wf = brysonho.BrysonHo # workspace = brysonho.init_workspace(ocp) # ocp_ws = wf(workspace) ocp_ws = wf({'problem': ocp, 'guess': guess_generator}) solinit = Solution() solinit.aux['const'] = dict((str(const.name),float(const.value)) for const in ocp_ws['constants']) solinit.aux['parameters'] = ocp_ws['problem_data']['parameter_list'] # For path constraints solinit.aux['constraint'] = cl.defaultdict(float) solinit.aux['constraints'] = dict((s['name'], {'unit':str(s['unit']), 'expr':str(s['expr']), 'direction': s['direction'], 'arc_type': i, 'pi_list':[str(_) for _ in s['pi_list']]}) for i, s in enumerate(ocp_ws['problem_data']['s_list'],1)) solinit.aux['arc_seq'] = (0,) solinit.aux['pi_seq'] = (None,) bvp_fn = bvp_algorithm.preprocess(ocp_ws['problem_data']) # The initial guess is automatically stored in the bvp object # solinit is just a reference to it solinit = ocp_ws['guess'].generate(bvp_fn, solinit) # # includes costates state_names = ocp_ws['problem_data']['state_list'] initial_states = solinit.y[:,0] # First column terminal_states = solinit.y[:,-1] # Last column initial_bc = dict(zip(state_names,initial_states)) terminal_bc = dict(zip(state_names,terminal_states)) solinit.aux['initial'] = initial_bc solinit.aux['terminal'] = terminal_bc solinit2= copy.deepcopy(solinit) tic() # TODO: Start from specific step for restart capability # TODO: Make class to store result from continuation set? out = {}; out['problem_data'] = ocp_ws['problem_data']; ocp._scaling.initialize(ocp_ws) ocp_ws['scaling'] = ocp._scaling out['solution'] = run_continuation_set(ocp_ws, bvp_algorithm, steps, bvp_fn, solinit) total_time = toc() # tic() # out['solution'] = run_continuation_set(ocp_ws, bvp_algorithm, steps, bvp_fn, solinit2) # total_time = toc() logging.info('Continuation process completed in %0.4f seconds.\n' % total_time) bvp_algorithm.close() # Save data # del out['problem_data']['s_list'] del out['problem_data']['states'] del out['problem_data']['costates'] qvars = out['problem_data']['quantity_vars'] qvars = {str(k):str(v) for k,v in qvars.items()} out['problem_data']['quantity_vars'] = qvars with open(output_file, 'wb') as outfile: dill.settings['recurse'] = True dill.dump(out, outfile) # Dill Beluga object only
def solve(ocp, method, bvp_algorithm, steps, guess_generator, **kwargs): """ Solves the OCP using specified method +------------------------+-----------------+-----------------+ | Valid kwargs | Default Value | Valid Values | +========================+=================+=================+ | autoscale | True | bool | +------------------------+-----------------+-----------------+ | n_cpus | 1 | integer | +------------------------+-----------------+-----------------+ """ autoscale = kwargs.get('autoscale', True) n_cpus = int(kwargs.get('n_cpus', 1)) if n_cpus < 1: raise ValueError('Number of cpus must be greater than 1.') if n_cpus > 1: logging.debug('Starting processing pool with ' + str(n_cpus) + 'cpus... ') pool = pathos.multiprocessing.Pool(processes=n_cpus) logging.debug('Done.') else: pool = None output_file = config['output_file'] logging.info("Computing the necessary conditions of optimality") if method.lower() == 'traditional' or method.lower() == 'brysonho': bvp_ws, guess_mapper = BH_ocp_to_bvp(ocp) elif method.lower() == 'icrm': bvp_ws, guess_mapper = ICRM_ocp_to_bvp(ocp) elif method.lower() == 'diffyg': bvp_ws, guess_mapper = DIFFYG_ocp_to_bvp(ocp) else: raise NotImplementedError logging.debug('Resulting BVP problem:') for key in bvp_ws.keys(): logging.debug(str(key) + ': ' + str(bvp_ws[key])) bvp_ws['problem'] = ocp bvp_ws['guess'] = guess_generator bvp_ws['custom_functions'] = ocp.custom_functions() solinit = Solution() solinit.aux['const'] = OrderedDict( (const, val) for const, val in zip(bvp_ws['constants'], bvp_ws['constants_values'])) for const in bvp_ws['constants']: if not str(const) in solinit.aux['const'].keys(): solinit.aux['const'][str(const)] = 0 solinit.aux['dynamical_parameters'] = bvp_ws['dynamical_parameters'] solinit.aux['nondynamical_parameters'] = bvp_ws['nondynamical_parameters'] bvp = preprocess(bvp_ws) solinit = bvp_ws['guess'].generate(bvp, solinit, guess_mapper) state_names = bvp_ws['states'] initial_states = solinit.y[0, :] terminal_states = solinit.y[-1, :] initial_bc = dict(zip(state_names, initial_states)) terminal_bc = dict(zip(state_names, terminal_states)) for ii in initial_bc: if ii + '_0' in solinit.aux['const'].keys(): solinit.aux['const'][ii + '_0'] = initial_bc[ii] for ii in terminal_bc: if ii + '_f' in solinit.aux['const'].keys(): solinit.aux['const'][ii + '_f'] = terminal_bc[ii] quad_names = bvp_ws['quads'] n_quads = len(quad_names) if n_quads > 0: initial_quads = solinit.q[0, :] terminal_quads = solinit.q[-1, :] initial_bc = dict(zip(quad_names, initial_quads)) terminal_bc = dict(zip(quad_names, terminal_quads)) for ii in initial_bc: if ii + '_0' in solinit.aux['const'].keys(): solinit.aux['const'][ii + '_0'] = initial_bc[ii] for ii in terminal_bc: if ii + '_f' in solinit.aux['const'].keys(): solinit.aux['const'][ii + '_f'] = terminal_bc[ii] time0 = time.time() out = dict() out['problem_data'] = bvp_ws ocp._scaling.initialize(bvp_ws) bvp_ws['scaling'] = ocp._scaling out['solution'] = run_continuation_set(bvp_ws, bvp_algorithm, steps, solinit, bvp, pool, autoscale) total_time = time.time() - time0 logging.info('Continuation process completed in %0.4f seconds.\n' % total_time) bvp_algorithm.close() # Final time is appended as a parameter, so scale the output x variables to show the correct time for continuation_set in out['solution']: for sol in continuation_set: tf_ind = [ i for i, s in enumerate(out['problem_data'] ['dynamical_parameters']) if str(s) is 'tf' ][0] tf = sol.dynamical_parameters[tf_ind] sol.t = sol.t * tf if pool is not None: pool.close() # Save data with open(output_file, 'wb') as outfile: pickle.dump(out, outfile) return out['solution'][-1][-1]