From d16ae5eceab36b262a466ed6434d5e6f61edc83f Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 14 Nov 2023 09:42:43 -0500 Subject: [PATCH 1/5] Removed save/load features --- bioptim/examples/__main__.py | 1 - .../getting_started/example_save_and_load.py | 161 ------------- .../getting_started/example_simulation.py | 2 +- bioptim/examples/getting_started/pendulum.py | 1 + bioptim/gui/graph.py | 2 +- bioptim/models/biorbd/biorbd_model.py | 5 + bioptim/models/protocols/biomodel.py | 5 + .../optimization/optimal_control_program.py | 224 +----------------- .../stochastic_optimal_control_program.py | 10 - tests/shard1/test__global_plots.py | 26 +- tests/shard1/test_all_examples.py | 38 --- tests/shard1/test_global_align.py | 6 - tests/shard1/test_global_fatigue.py | 33 --- .../test_global_minimize_marker_velocity.py | 12 - tests/shard2/test_global_muscle_driven_ocp.py | 3 - .../test_global_muscle_tracking_0_False.py | 3 - .../test_global_muscle_tracking_0_True.py | 3 - tests/shard2/test_global_muscle_tracking_1.py | 3 - tests/shard2/test_global_muscle_tracking_2.py | 6 - tests/shard2/test_global_optimal_time.py | 18 -- .../test_global_optimal_time_mayer_min.py | 6 - tests/shard2/test_global_sqp.py | 3 - tests/shard3/test_global_getting_started.py | 222 ----------------- ...st_global_symmetrical_torque_driven_ocp.py | 6 - tests/shard3/test_global_torque_driven_ocp.py | 24 -- ...t_global_torque_driven_with_contact_ocp.py | 6 - .../shard3/test_muscle_driven_ocp_implicit.py | 3 - tests/utils.py | 26 -- 28 files changed, 17 insertions(+), 841 deletions(-) delete mode 100644 bioptim/examples/getting_started/example_save_and_load.py diff --git a/bioptim/examples/__main__.py b/bioptim/examples/__main__.py index b640019c4..54fdfbfea 100644 --- a/bioptim/examples/__main__.py +++ b/bioptim/examples/__main__.py @@ -50,7 +50,6 @@ ("Example mapping", "example_mapping.py"), ("Example multiphase", "example_multiphase.py"), ("Example optimal time", "example_optimal_time.py"), - ("Example save and load", "example_save_and_load.py"), ("Example simulation", "example_simulation.py"), ("Example Implicit Dynamics", "example_implicit_dynamics.py"), ("Pendulum", "pendulum.py"), diff --git a/bioptim/examples/getting_started/example_save_and_load.py b/bioptim/examples/getting_started/example_save_and_load.py deleted file mode 100644 index 4b6974f05..000000000 --- a/bioptim/examples/getting_started/example_save_and_load.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -This is a clone of the getting_started/pendulum.py example. - -It is designed to show how to create and solve a problem, and afterward, save it to the hard drive and reload it. - -It shows an example of *.bo method and how to use the stand_alone boolean parameter of the save function. If set -to True, the variable dictionaries (states, controls and parameters) are saved instead of the full Solution class -itself. This allows to load the saved file into a setting where bioptim is not installed using the pickle package, but -prevents from using the class methods Solution offers after loading the file. -""" - -import pickle - -import numpy as np -from casadi import MX -from bioptim import ( - BiorbdModel, - OptimalControlProgram, - DynamicsFcn, - Dynamics, - BoundsList, - ObjectiveFcn, - Objective, - OdeSolver, - OdeSolverBase, - PhaseDynamics, -) - - -def custom_plot_callback(x: MX, q_to_plot: list) -> MX: - """ - Create a used defined plot function with extra_parameters - - Parameters - ---------- - x: MX - The current states of the optimization - q_to_plot: list - The slice indices to plot - - Returns - ------- - The value to plot - """ - - return x[q_to_plot, :] - - -def prepare_ocp( - biorbd_model_path: str, - final_time: float, - n_shooting: int, - n_threads: int, - use_sx: bool = False, - ode_solver: OdeSolverBase = OdeSolver.RK4(), - phase_dynamics: PhaseDynamics = PhaseDynamics.SHARED_DURING_THE_PHASE, - expand_dynamics: bool = True, -) -> OptimalControlProgram: - """ - Prepare the program - - Parameters - ---------- - biorbd_model_path: str - The path of the biorbd model - final_time: float - The time at the final node - n_shooting: int - The number of shooting points - n_threads: int - The number of threads to use while using multithreading - ode_solver: OdeSolverBase - The type of ode solver used - use_sx: bool - If the program should be constructed using SX instead of MX (longer to create the CasADi graph, faster to solve) - phase_dynamics: PhaseDynamics - If the dynamics equation within a phase is unique or changes at each node. - PhaseDynamics.SHARED_DURING_THE_PHASE is much faster, but lacks the capability to have changing dynamics within - a phase. A good example of when PhaseDynamics.ONE_PER_NODE should be used is when different external forces - are applied at each node - expand_dynamics: bool - If the dynamics function should be expanded. Please note, this will solve the problem faster, but will slow down - the declaration of the OCP, so it is a trade-off. Also depending on the solver, it may or may not work - (for instance IRK is not compatible with expanded dynamics) - - Returns - ------- - The ocp ready to be solved - """ - - bio_model = BiorbdModel(biorbd_model_path) - - # Add objective functions - objective_functions = Objective(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="tau", derivative=True) - - # Dynamics - dynamics = Dynamics(DynamicsFcn.TORQUE_DRIVEN, expand_dynamics=expand_dynamics, phase_dynamics=phase_dynamics) - - # Path constraint - x_bounds = BoundsList() - x_bounds["q"] = bio_model.bounds_from_ranges("q") - x_bounds["q"][:, [0, -1]] = 0 # Start and end at 0... - x_bounds["q"][1, -1] = 3.14 # ...but end with pendulum 180 degrees rotated - x_bounds["qdot"] = bio_model.bounds_from_ranges("qdot") - x_bounds["qdot"][:, [0, -1]] = 0 # Start and end without any velocity - - # Define control path constraint - n_tau = bio_model.nb_tau - u_bounds = BoundsList() - u_bounds["tau"] = [-100] * n_tau, [100] * n_tau # Limit the strength of the pendulum to (-100 to 100)... - u_bounds["tau"][1, :] = 0 # ...but remove the capability to actively rotate - - return OptimalControlProgram( - bio_model, - dynamics, - n_shooting, - final_time, - x_bounds, - u_bounds, - objective_functions=objective_functions, - n_threads=n_threads, - use_sx=use_sx, - ode_solver=ode_solver, - ) - - -def main(): - """ - Create and solve a program. Then it saves it using the .bo method, and then using te stand_alone option. - """ - - ocp = prepare_ocp(biorbd_model_path="models/pendulum.bioMod", final_time=1, n_shooting=100, n_threads=4) - - # --- Solve the program --- # - sol = ocp.solve() - print(f"Time to solve : {sol.real_time_to_optimize}sec") - - # --- Print objective cost --- # - print(f"Final objective value : {np.nansum(sol.cost)} \n") - sol.print_cost() - - # --- Save the optimal control program and the solution with stand_alone = False --- # - ocp.save(sol, "pendulum.bo") # you don't have to specify the extension ".bo" - - # --- Load the optimal control program and the solution --- # - ocp_load, sol_load = OptimalControlProgram.load("pendulum.bo") - - # --- Show results --- # - sol_load.animate() - sol_load.graphs() - - # --- Save the optimal control program and the solution with stand_alone = True --- # - ocp.save(sol, f"pendulum_sa.bo", stand_alone=True) - - # --- Load the solution saved with stand_alone = True --- # - with open(f"pendulum_sa.bo", "rb") as file: - states, controls, parameters = pickle.load(file) - - -if __name__ == "__main__": - main() diff --git a/bioptim/examples/getting_started/example_simulation.py b/bioptim/examples/getting_started/example_simulation.py index 330d78f42..469677615 100644 --- a/bioptim/examples/getting_started/example_simulation.py +++ b/bioptim/examples/getting_started/example_simulation.py @@ -6,7 +6,7 @@ The second part of the example is to actually solve the program and then simulate the results from this solution. The main goal of this kind of simulation, especially in single shooting (that is not resetting the states at each node) is to validate the dynamics of multiple shooting. If they both are equal, it usually means that a great confidence -can be held in the solution. Another goal would be to reload fast a previously saved optimized solution +can be held in the solution. """ from bioptim import InitialGuess, Solution, Shooting, InterpolationType, SolutionIntegrator diff --git a/bioptim/examples/getting_started/pendulum.py b/bioptim/examples/getting_started/pendulum.py index b57aae1e1..00b96b777 100644 --- a/bioptim/examples/getting_started/pendulum.py +++ b/bioptim/examples/getting_started/pendulum.py @@ -8,6 +8,7 @@ During the optimization process, the graphs are updated real-time (even though it is a bit too fast and short to really appreciate it). Finally, once it finished optimizing, it animates the model using the optimal solution """ + import platform from bioptim import ( diff --git a/bioptim/gui/graph.py b/bioptim/gui/graph.py index c95159d47..fd73a7f41 100644 --- a/bioptim/gui/graph.py +++ b/bioptim/gui/graph.py @@ -320,7 +320,7 @@ def print(self): print("") print("") print(f"**********") - print(f"MODEL: {self.ocp.original_values['bio_model'][phase_idx]}") + print(f"MODEL: {self.ocp.nlp[phase_idx].model.name}") if isinstance(self.ocp.nlp[phase_idx].tf, (int, float)): print(f"PHASE DURATION: {round(self.ocp.nlp[phase_idx].tf, 2)} s") else: diff --git a/bioptim/models/biorbd/biorbd_model.py b/bioptim/models/biorbd/biorbd_model.py index bf91ed8aa..f9fba265f 100644 --- a/bioptim/models/biorbd/biorbd_model.py +++ b/bioptim/models/biorbd/biorbd_model.py @@ -29,6 +29,11 @@ def __init__(self, bio_model: str | biorbd.Model, friction_coefficients: np.ndar self.model = biorbd.Model(bio_model) if isinstance(bio_model, str) else bio_model self._friction_coefficients = friction_coefficients + @property + def name(self) -> str: + # parse the path and split to get the .bioMod name + return self.model.path().absolutePath().to_string().split("/")[-1] + @property def path(self) -> str: return self.model.path().relativePath().to_string() diff --git a/bioptim/models/protocols/biomodel.py b/bioptim/models/protocols/biomodel.py index 985fddd09..2e0180438 100644 --- a/bioptim/models/protocols/biomodel.py +++ b/bioptim/models/protocols/biomodel.py @@ -13,6 +13,11 @@ class BioModel(Protocol): As a reminder for developers: only necessary attributes and methods should appear here. """ + @property + def name(self) -> str: + """Get the name of the model""" + return "" + def copy(self): """copy the model by reloading one""" diff --git a/bioptim/optimization/optimal_control_program.py b/bioptim/optimization/optimal_control_program.py index 6689410bc..2a494f2eb 100644 --- a/bioptim/optimization/optimal_control_program.py +++ b/bioptim/optimization/optimal_control_program.py @@ -1,7 +1,4 @@ from typing import Callable, Any -import os -import sys -import pickle from copy import deepcopy from math import inf @@ -92,8 +89,6 @@ class OptimalControlProgram: The number of thread to use if using multithreading original_phase_time: list[float] The time vector as sent by the user - original_values: dict - A copy of the ocp as it is after defining everything phase_transitions: list[PhaseTransition] The list of transition constraint between phases ocp_solver: SolverInterface @@ -128,30 +123,19 @@ class OptimalControlProgram: Create all the plots associated with the OCP solve(self, solver: Solver, show_online_optim: bool, solver_options: dict) -> Solution Call the solver to actually solve the ocp - save(self, sol: Solution, file_path: str, stand_alone: bool = False) - Save the ocp and solution structure to the hard drive. It automatically create the required - folder if it does not exists. Please note that biorbd is required to load back this structure. - @staticmethod - load(file_path: str) -> list - Reload a previous optimization (*.bo) saved using save _define_time(self, phase_time: float | tuple, objective_functions: ObjectiveList, constraints: ConstraintList) Declare the phase_time vector in v. If objective_functions or constraints defined a time optimization, a sanity check is perform and the values of initial guess and bounds for these particular phases __modify_penalty(self, new_penalty: PenaltyOption | Parameter) - The internal function to modify a penalty. It is also stored in the original_values, meaning that if one - overrides an objective only the latter is preserved when saved + The internal function to modify a penalty. __set_nlp_is_stochastic(self) Set the nlp as stochastic if any of the phases is stochastic __set_stochastic_internal_stochastic_variables(self) Set the internal stochastic variables (s_init, s_bounds, s_scaling) if any of the phases is stochastic - _set_stochastic_variables_to_original_values(self, s_init, s_bounds, s_scaling) - Set the original_values with the stochastic variables (s_init, s_bounds, s_scaling) if any of the phases is - stochastic _check_quaternions_hasattr(self, bio_model) Check if the bio_model has quaternions and set the flag accordingly _check_and_prepare_dynamics(self, dynamics) Check if the dynamics is a Dynamics or a DynamicsList and set the flag accordingly - _set_original_values( """ def __init__( @@ -253,37 +237,7 @@ def __init__( # Placed here because of MHE self._check_and_prepare_dynamics(dynamics) - - self._set_original_values( - bio_model, - n_shooting, - phase_time, - x_init, - u_init, - x_bounds, - u_bounds, - x_scaling, - xdot_scaling, - u_scaling, - ode_solver, - control_type, - variable_mappings, - time_phase_mapping, - node_mappings, - plot_mappings, - phase_transitions, - multinode_constraints, - multinode_objectives, - parameter_bounds, - parameter_init, - parameter_constraints, - parameter_objectives, - n_threads, - use_sx, - integrated_value_functions, - ) s_init, s_bounds, s_scaling = self._set_stochastic_internal_stochastic_variables() - self._set_stochastic_variables_to_original_values(s_init, s_bounds, s_scaling) self._check_and_set_threads(n_threads) self._check_and_set_shooting_points(n_shooting) @@ -394,69 +348,6 @@ def _check_and_prepare_dynamics(self, dynamics): elif not isinstance(dynamics, DynamicsList): raise RuntimeError("dynamics should be a Dynamics or a DynamicsList") - def _set_original_values( - self, - bio_model, - n_shooting, - phase_time, - x_init, - u_init, - x_bounds, - u_bounds, - x_scaling, - xdot_scaling, - u_scaling, - ode_solver, - control_type, - variable_mappings, - time_phase_mapping, - node_mappings, - plot_mappings, - phase_transitions, - multinode_constraints, - multinode_objectives, - parameter_bounds, - parameter_init, - parameter_constraints, - parameter_objectives, - n_threads, - use_sx, - integrated_value_functions, - ): - self.original_values = { - "bio_model": [m.serialize() for m in bio_model], - "dynamics": self.dynamics, - "n_shooting": n_shooting, - "phase_time": phase_time, - "x_init": x_init, - "u_init": u_init, - "x_bounds": x_bounds, - "u_bounds": u_bounds, - "x_scaling": x_scaling, - "xdot_scaling": xdot_scaling, - "u_scaling": u_scaling, - "objective_functions": ObjectiveList(), - "constraints": ConstraintList(), - "parameters": ParameterList(), - "ode_solver": ode_solver, - "control_type": control_type, - "variable_mappings": variable_mappings, - "time_phase_mapping": time_phase_mapping, - "node_mappings": node_mappings, - "plot_mappings": plot_mappings, - "phase_transitions": phase_transitions, - "multinode_constraints": multinode_constraints, - "multinode_objectives": multinode_objectives, - "parameter_bounds": parameter_bounds, - "parameter_init": parameter_init, - "parameter_objectives": parameter_objectives, - "parameter_constraints": parameter_constraints, - "n_threads": n_threads, - "use_sx": use_sx, - "integrated_value_functions": integrated_value_functions, - } - return - def _check_and_set_threads(self, n_threads): if not isinstance(n_threads, int) or isinstance(n_threads, bool) or n_threads < 1: raise RuntimeError("n_threads should be a positive integer greater or equal than 1") @@ -1636,90 +1527,6 @@ def set_warm_start(self, sol: Solution): self.is_warm_starting = True - def save(self, sol: Solution, file_path: str, stand_alone: bool = False): - """ - Save the ocp and solution structure to the hard drive. It automatically creates the required - folder if it does not exist. Please note that biorbd is required to load back this structure. - - IMPORTANT NOTICE: Please note that this is dependent on the bioptim version used to create the .bo file - and retrocompatibility is NOT enforced. This means that an optimized solution from a previous version will - probably NOT load on a newer bioptim version. To save the solution in a way which is independent of the - version of bioptim, one may use the stand_alone flag to True. - - Parameters - ---------- - sol: Solution - The solution structure to save - file_path: str - The path to solve the structure. It creates a .bo (BiOptim file) - stand_alone: bool - If set to True, the variable dictionaries (states, controls and parameters) are saved instead of the full - Solution class itself. This allows to load the saved file into a setting where bioptim is not installed - using the pickle package, but prevents from using the class methods Solution offers after loading the file - """ - - _, ext = os.path.splitext(file_path) - if ext == "": - file_path = file_path + ".bo" - elif ext != ".bo": - raise RuntimeError(f"Incorrect extension({ext}), it should be (.bo) or (.bob) if you use save_get_data.") - - if stand_alone: - # TODO check if this file is loaded when load is used, and raise an error - data_to_save = sol.states, sol.controls, sol.parameters - else: - sol_copy = sol.copy() - sol_copy.ocp = None # Ocp is not pickable - data_to_save = {"ocp_initializer": self.original_values, "sol": sol_copy, "versions": self.version} - - # Create folder if necessary - directory, _ = os.path.split(file_path) - if directory != "" and not os.path.isdir(directory): - os.makedirs(directory) - - with open(file_path, "wb") as file: - pickle.dump(data_to_save, file) - - @staticmethod - def load(file_path: str) -> list: - """ - Reload a previous optimization (*.bo) saved using save - - Parameters - ---------- - file_path: str - The path to the *.bo file - - Returns - ------- - The ocp and sol structure. If it was saved, the iterations are also loaded - """ - - with open(file_path, "rb") as file: - try: - data = pickle.load(file) - except BaseException as error_message: - raise ValueError( - f"The file '{file_path}' cannot be loaded, maybe the version of bioptim (version {__version__})\n" - f"is not the same as the one that created the file (version unknown). For more information\n" - "please refer to the original error message below\n\n" - f"{type(error_message).__name__}: {error_message}" - ) - ocp = OptimalControlProgram.from_loaded_data(data["ocp_initializer"]) - for key in data["versions"].keys(): - key_module = "biorbd_casadi" if key == "biorbd" else key - try: - check_version(sys.modules[key_module], data["versions"][key], ocp.version[key], exclude_max=False) - except ImportError: - raise ImportError( - f"Version of {key} from file ({data['versions'][key]}) is not the same as the " - f"installed version ({ocp.version[key]})" - ) - sol = data["sol"] - sol.ocp = SimplifiedOCP(ocp) - out = [ocp, sol] - return out - def print( self, to_console: bool = True, @@ -1866,8 +1673,7 @@ def define_parameters_phase_time( def __modify_penalty(self, new_penalty: PenaltyOption | Parameter): """ - The internal function to modify a penalty. It is also stored in the original_values, meaning that if one - overrides an objective only the latter is preserved when saved + The internal function to modify a penalty. Parameters ---------- @@ -1878,18 +1684,13 @@ def __modify_penalty(self, new_penalty: PenaltyOption | Parameter): if not new_penalty: return phase_idx = new_penalty.phase - - # Copy to self.original_values so it can be save/load - pen = new_penalty.type.get_type() - self.original_values[pen.penalty_nature()].add(deepcopy(new_penalty)) new_penalty.add_or_replace_to_penalty_pool(self, self.nlp[phase_idx]) self.program_changed = True def __modify_parameter_penalty(self, new_penalty: PenaltyOption | Parameter): """ - The internal function to modify a parameter penalty. It is also stored in the original_values, meaning that if one - overrides an objective only the latter is preserved when saved + The internal function to modify a parameter penalty. Parameters ---------- @@ -1900,11 +1701,7 @@ def __modify_parameter_penalty(self, new_penalty: PenaltyOption | Parameter): if not new_penalty: return - # Copy to self.original_values so it can be save/load - pen = new_penalty.type.get_type() - self.original_values[pen.penalty_nature()].add(deepcopy(new_penalty)) self.parameters[0].add_or_replace_to_penalty_pool(self, new_penalty) - self.program_changed = True def node_time(self, phase_idx: int, node_idx: int): @@ -1939,21 +1736,6 @@ def _set_default_ode_solver(self): """ return OdeSolver.RK4() - def _set_stochastic_variables_to_original_values( - self, - s_init: InitialGuessList, - s_bounds: BoundsList, - s_scaling: VariableScalingList, - ): - """ - Set the stochastic variables to their original values - - Note - ---- - This method is overrided in StochasticOptimalControlProgram - """ - pass - def _set_stochastic_internal_stochastic_variables(self): """ Set the stochastic variables to their internal values diff --git a/bioptim/optimization/stochastic_optimal_control_program.py b/bioptim/optimization/stochastic_optimal_control_program.py index d80d20952..cb6f7fb03 100644 --- a/bioptim/optimization/stochastic_optimal_control_program.py +++ b/bioptim/optimization/stochastic_optimal_control_program.py @@ -261,16 +261,6 @@ def _prepare_stochastic_dynamics_collocation(self, constraints, phase_transition if len(self.nlp) > 1 and i_phase < len(self.nlp) - 1: phase_transition.add(PhaseTransitionFcn.COVARIANCE_CONTINUOUS, phase_pre_idx=i_phase) - def _set_stochastic_variables_to_original_values( - self, - s_init: InitialGuessList, - s_bounds: BoundsList, - s_scaling: VariableScalingList, - ): - self.original_values["s_init"] = s_init - self.original_values["s_bounds"] = s_bounds - self.original_values["s_scaling"] = s_scaling - @staticmethod def load(file_path: str) -> list: """ diff --git a/tests/shard1/test__global_plots.py b/tests/shard1/test__global_plots.py index 10012ac35..76c0714b2 100644 --- a/tests/shard1/test__global_plots.py +++ b/tests/shard1/test__global_plots.py @@ -8,7 +8,7 @@ from casadi import Function, MX import numpy as np -from bioptim import OptimalControlProgram, CostType, OdeSolver, Solver, RigidBodyDynamics, BiorbdModel, PhaseDynamics +from bioptim import CostType, OdeSolver, Solver, RigidBodyDynamics, BiorbdModel, PhaseDynamics from bioptim.limits.penalty import PenaltyOption import matplotlib @@ -124,38 +124,14 @@ def test_add_new_plot(phase_dynamics): solver.set_maximum_iterations(1) sol = ocp.solve(solver) - # Saving/loading files reset the plot settings to normal - save_name = "test_plot.bo" - ocp.save(sol, save_name) - # Test 1 - Working plot ocp.add_plot("My New Plot", lambda t, x, u, p, s: x[0:2, :]) sol.graphs(automatically_organize=False) - # Test 2 - Combine using combine_to is not allowed - ocp, sol = OptimalControlProgram.load(save_name) - with pytest.raises(RuntimeError): - ocp.add_plot("My New Plot", lambda t, x, u, p, s: x[0:2, :], combine_to="NotAllowed") - - # Test 3 - Create a completely new plot - ocp, sol = OptimalControlProgram.load(save_name) - ocp.add_plot("My New Plot", lambda t, x, u, p, s: x[0:2, :]) - ocp.add_plot("My Second New Plot", lambda t, x, p, u, s: x[0:2, :]) - sol.graphs(automatically_organize=False) - - # Test 4 - Combine to the first using fig_name - ocp, sol = OptimalControlProgram.load(save_name) - ocp.add_plot("My New Plot", lambda t, x, u, p, s: x[0:2, :]) - ocp.add_plot("My New Plot", lambda t, x, u, p, s: x[0:2, :]) - sol.graphs(automatically_organize=False) - # Add the plot of objectives and constraints to this mess ocp.add_plot_penalty(CostType.ALL) sol.graphs(automatically_organize=False) - # Delete the saved file - os.remove(save_name) - @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @pytest.mark.parametrize( diff --git a/tests/shard1/test_all_examples.py b/tests/shard1/test_all_examples.py index eb3ac4425..d750620c6 100644 --- a/tests/shard1/test_all_examples.py +++ b/tests/shard1/test_all_examples.py @@ -263,44 +263,6 @@ def test__getting_started__example_optimal_time(): from bioptim.examples.getting_started import example_optimal_time as ocp_module -@pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) -def test__getting_started__example_save_and_load(phase_dynamics): - from bioptim.examples.getting_started import example_save_and_load as ocp_module - - bioptim_folder = os.path.dirname(ocp_module.__file__) - - if phase_dynamics == PhaseDynamics.ONE_PER_NODE: - with pytest.raises( - RuntimeError, match="Multiprocessing is not supported with phase_dynamics=PhaseDynamics.ONE_PER_NODE" - ): - ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=3, - n_shooting=100, - n_threads=4, - phase_dynamics=phase_dynamics, - expand_dynamics=False, - ) - else: - ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=3, - n_shooting=100, - n_threads=4, - phase_dynamics=phase_dynamics, - expand_dynamics=False, - ) - - ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=3, - n_shooting=100, - n_threads=1, - phase_dynamics=PhaseDynamics.SHARED_DURING_THE_PHASE, - expand_dynamics=False, - ) - - def test__getting_started__example_simulation(): from bioptim.examples.getting_started import example_optimal_time as ocp_module diff --git a/tests/shard1/test_global_align.py b/tests/shard1/test_global_align.py index fd341852c..c07841220 100644 --- a/tests/shard1/test_global_align.py +++ b/tests/shard1/test_global_align.py @@ -50,9 +50,6 @@ def test_track_segment_on_rt(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array([0, 9.81, 66.98666900582079, 66.98666424580644])) np.testing.assert_almost_equal(tau[:, -2], np.array([-0, 9.81, -66.98666900582079, -66.98666424580644])) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -98,9 +95,6 @@ def test_track_marker_on_segment(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array([23.6216587, 12.2590703, 31.520697, 12.9472294])) np.testing.assert_almost_equal(tau[:, -2], np.array([-16.659525, 14.5872277, -36.1009998, 4.417834])) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) diff --git a/tests/shard1/test_global_fatigue.py b/tests/shard1/test_global_fatigue.py index 0a81b50b9..85a8b4602 100644 --- a/tests/shard1/test_global_fatigue.py +++ b/tests/shard1/test_global_fatigue.py @@ -79,9 +79,6 @@ def test_xia_fatigable_muscles(phase_dynamics): np.array((8.86069119e-03, 1.17337666e-08, 1.28715148e-08, 2.02340603e-02, 2.02340603e-02, 2.16517945e-088)), ) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -158,9 +155,6 @@ def test_xia_stabilized_fatigable_muscles(phase_dynamics): np.array((8.86069119e-03, 1.17337666e-08, 1.28715148e-08, 2.02340603e-02, 2.02340603e-02, 2.16517945e-08)), ) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -198,9 +192,6 @@ def test_michaud_fatigable_muscles(phase_dynamics): # Check some of the results # TODO: add tests - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -268,9 +259,6 @@ def test_effort_fatigable_muscles(phase_dynamics): np.array((3.86483818e-02, 1.10050313e-09, 2.74222702e-09, 4.25097771e-02, 4.25097771e-02, 6.56233597e-09)), ) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -311,9 +299,6 @@ def test_fatigable_xia_torque_non_split(phase_dynamics): # Check some of the results # TODO: add tests - # save and load - TestUtils.save_and_load(sol, ocp, False) - @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_fatigable_xia_torque_split(phase_dynamics): @@ -376,9 +361,6 @@ def test_fatigable_xia_torque_split(phase_dynamics): np.testing.assert_almost_equal(tau_plus[:, 0], np.array((5.2893453, 0))) np.testing.assert_almost_equal(tau_plus[:, -2], np.array((0, 0))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -448,9 +430,6 @@ def test_fatigable_xia_stabilized_torque_split(phase_dynamics): np.testing.assert_almost_equal(tau_plus[:, 0], np.array((5.2893453, 0))) np.testing.assert_almost_equal(tau_plus[:, -2], np.array((0, 0))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -491,9 +470,6 @@ def test_fatigable_michaud_torque_non_split(phase_dynamics): # Check some of the results # TODO: add some tests - # save and load - TestUtils.save_and_load(sol, ocp, False) - @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_fatigable_michaud_torque_split(phase_dynamics): @@ -561,9 +537,6 @@ def test_fatigable_michaud_torque_split(phase_dynamics): np.testing.assert_almost_equal(tau_plus[:, 0], np.array((5.03417919, 0)), decimal=5) np.testing.assert_almost_equal(tau_plus[:, -2], np.array((0, 0))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -604,9 +577,6 @@ def test_fatigable_effort_torque_non_split(phase_dynamics): # Check some of the results # TODO: add some tests - # save and load - TestUtils.save_and_load(sol, ocp, False) - @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_fatigable_effort_torque_split(phase_dynamics): @@ -665,8 +635,5 @@ def test_fatigable_effort_torque_split(phase_dynamics): np.testing.assert_almost_equal(tau_plus[:, 0], np.array((5.85068579, 0))) np.testing.assert_almost_equal(tau_plus[:, -2], np.array((0, 0))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) diff --git a/tests/shard2/test_global_minimize_marker_velocity.py b/tests/shard2/test_global_minimize_marker_velocity.py index 4e21ca4a4..fb571695d 100644 --- a/tests/shard2/test_global_minimize_marker_velocity.py +++ b/tests/shard2/test_global_minimize_marker_velocity.py @@ -172,9 +172,6 @@ def test_track_and_minimize_marker_displacement_global(ode_solver, phase_dynamic tau[:, -2], np.array([4.42976253e-02, 1.40077846e00, -7.28864793e-13, 9.24667396e01]), decimal=2 ) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -221,9 +218,6 @@ def test_track_and_minimize_marker_displacement_RT(ode_solver, phase_dynamics): tau[:, -2], np.array([-1.97981112e01, -9.89876772e-02, 4.34033234e-08, 2.61513636e-08]) ) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -268,9 +262,6 @@ def test_track_and_minimize_marker_velocity(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array([-4.52216174e-02, 9.25170010e-01, 0, 0])) np.testing.assert_almost_equal(tau[:, -2], np.array([4.4260355e-02, 1.4004583, 0, 0])) - # # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -326,8 +317,5 @@ def test_track_and_minimize_marker_velocity_linear_controls(ode_solver, phase_dy np.testing.assert_almost_equal(tau[2:, 0], np.array([-8.495542, 0]), decimal=5) np.testing.assert_almost_equal(tau[2:, -1], np.array([8.495541, 0]), decimal=5) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) diff --git a/tests/shard2/test_global_muscle_driven_ocp.py b/tests/shard2/test_global_muscle_driven_ocp.py index eb6f8315f..11ee36e87 100644 --- a/tests/shard2/test_global_muscle_driven_ocp.py +++ b/tests/shard2/test_global_muscle_driven_ocp.py @@ -139,8 +139,5 @@ def test_muscle_driven_ocp(ode_solver, phase_dynamics): else: raise ValueError("Test not ready") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) diff --git a/tests/shard2/test_global_muscle_tracking_0_False.py b/tests/shard2/test_global_muscle_tracking_0_False.py index 31462c85f..0007bbdcd 100644 --- a/tests/shard2/test_global_muscle_tracking_0_False.py +++ b/tests/shard2/test_global_muscle_tracking_0_False.py @@ -147,8 +147,5 @@ def test_muscle_activations_and_states_tracking(ode_solver, n_threads, phase_dyn else: raise ValueError("Test not implemented") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) diff --git a/tests/shard2/test_global_muscle_tracking_0_True.py b/tests/shard2/test_global_muscle_tracking_0_True.py index fc9a2c348..6188cdddd 100644 --- a/tests/shard2/test_global_muscle_tracking_0_True.py +++ b/tests/shard2/test_global_muscle_tracking_0_True.py @@ -147,8 +147,5 @@ def test_muscle_activations_and_states_tracking(ode_solver, n_threads, phase_dyn else: raise ValueError("Test not implemented") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) diff --git a/tests/shard2/test_global_muscle_tracking_1.py b/tests/shard2/test_global_muscle_tracking_1.py index 45d27755a..5b49f6416 100644 --- a/tests/shard2/test_global_muscle_tracking_1.py +++ b/tests/shard2/test_global_muscle_tracking_1.py @@ -128,8 +128,5 @@ def test_muscle_activation_no_residual_torque_and_markers_tracking(ode_solver, p else: raise ValueError("Test not ready") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) diff --git a/tests/shard2/test_global_muscle_tracking_2.py b/tests/shard2/test_global_muscle_tracking_2.py index 27d6442ac..60ef29407 100644 --- a/tests/shard2/test_global_muscle_tracking_2.py +++ b/tests/shard2/test_global_muscle_tracking_2.py @@ -145,9 +145,6 @@ def test_muscle_excitation_with_torque_and_markers_tracking(ode_solver): else: raise ValueError("Test not ready") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -280,8 +277,5 @@ def test_muscle_excitation_no_residual_torque_and_markers_tracking(ode_solver): else: raise ValueError("Test not implemented") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) diff --git a/tests/shard2/test_global_optimal_time.py b/tests/shard2/test_global_optimal_time.py index e0d28a155..5f1d8e9f5 100644 --- a/tests/shard2/test_global_optimal_time.py +++ b/tests/shard2/test_global_optimal_time.py @@ -109,9 +109,6 @@ def test_pendulum_max_time_mayer_constrained(ode_solver, phase_dynamics): # optimized time np.testing.assert_almost_equal(tf, max_ft) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -216,9 +213,6 @@ def test_pendulum_min_time_lagrange(ode_solver, phase_dynamics): else: raise ValueError("Test not implemented") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) @@ -385,9 +379,6 @@ def test_time_constraint(ode_solver, phase_dynamics): else: raise ValueError("Test not ready") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -449,9 +440,6 @@ def test_monophase_time_constraint(ode_solver, phase_dynamics): # optimized time np.testing.assert_almost_equal(tf, 1.0) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -521,9 +509,6 @@ def test_multiphase_time_constraint(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tf_all.T, [[1.0, 3, 0.8]]) np.testing.assert_almost_equal(tf, np.sum(tf_all)) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -600,9 +585,6 @@ def test_multiphase_time_constraint_with_phase_time_equality(ode_solver, phase_d np.testing.assert_almost_equal(tf_all.T, [[0.95655144, 3]]) np.testing.assert_almost_equal(tf, np.sum(tf_all) + tf_all[0, 0]) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) diff --git a/tests/shard2/test_global_optimal_time_mayer_min.py b/tests/shard2/test_global_optimal_time_mayer_min.py index fb85358d6..a5778399f 100644 --- a/tests/shard2/test_global_optimal_time_mayer_min.py +++ b/tests/shard2/test_global_optimal_time_mayer_min.py @@ -95,9 +95,6 @@ def test_pendulum_min_time_mayer(ode_solver, phase_dynamics): else: raise ValueError("Test not implemented") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) @@ -180,8 +177,5 @@ def test_pendulum_min_time_mayer_constrained(ode_solver, phase_dynamics): else: np.testing.assert_almost_equal(tf, min_ft) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) diff --git a/tests/shard2/test_global_sqp.py b/tests/shard2/test_global_sqp.py index eca218bf0..f6648e7aa 100644 --- a/tests/shard2/test_global_sqp.py +++ b/tests/shard2/test_global_sqp.py @@ -54,6 +54,3 @@ def test_pendulum(phase_dynamics): # initial and final controls np.testing.assert_almost_equal(tau[:, 0], np.array((11.75634204, 0))) np.testing.assert_almost_equal(tau[:, -2], np.array((-16.60785771, 0))) - - # save and load - TestUtils.save_and_load(sol, ocp, test_solve_of_loaded=True, solver=solver) diff --git a/tests/shard3/test_global_getting_started.py b/tests/shard3/test_global_getting_started.py index 13d303c8b..0058faf0d 100644 --- a/tests/shard3/test_global_getting_started.py +++ b/tests/shard3/test_global_getting_started.py @@ -222,195 +222,11 @@ def test_pendulum(ode_solver, use_sx, n_threads, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((6.01549798, 0.0))) np.testing.assert_almost_equal(tau[:, -2], np.array((-13.68877181, 0.0))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) return -@pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) -@pytest.mark.parametrize("n_threads", [1, 2]) -@pytest.mark.parametrize("use_sx", [False, True]) -@pytest.mark.parametrize("ode_solver", [OdeSolver.RK4, OdeSolver.IRK, OdeSolver.COLLOCATION]) -def test_pendulum_save_and_load_no_rk8(n_threads, use_sx, ode_solver, phase_dynamics): - from bioptim.examples.getting_started import example_save_and_load as ocp_module - - if platform.system() == "Windows": - # This is a long test and CI is already long for Windows - return - - # For reducing time phase_dynamics=PhaseDynamics.ONE_PER_NODE is skipped for redundant tests - if n_threads > 1 and phase_dynamics == PhaseDynamics.ONE_PER_NODE: - return - - bioptim_folder = os.path.dirname(ocp_module.__file__) - - ode_solver_orig = ode_solver - if ode_solver == OdeSolver.IRK: - ode_solver = ode_solver() - if use_sx: - with pytest.raises(RuntimeError, match="use_sx=True and OdeSolver.IRK are not yet compatible"): - ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=1, - n_shooting=30, - n_threads=n_threads, - use_sx=use_sx, - ode_solver=ode_solver, - phase_dynamics=phase_dynamics, - expand_dynamics=ode_solver_orig != OdeSolver.IRK, - ) - else: - ocp = ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=1, - n_shooting=30, - n_threads=n_threads, - use_sx=use_sx, - ode_solver=ode_solver, - phase_dynamics=phase_dynamics, - expand_dynamics=ode_solver_orig != OdeSolver.IRK, - ) - sol = ocp.solve() - - # Check objective function value - f = np.array(sol.cost) - np.testing.assert_equal(f.shape, (1, 1)) - - # Check constraints - g = np.array(sol.constraints) - np.testing.assert_equal(g.shape, (120, 1)) - np.testing.assert_almost_equal(g, np.zeros((120, 1))) - - # Check some of the results - q, qdot, tau = (sol.states["q"], sol.states["qdot"], sol.controls["tau"]) - - # initial and final position - np.testing.assert_almost_equal(q[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(q[:, -1], np.array((0, 3.14))) - - # initial and final velocities - np.testing.assert_almost_equal(qdot[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(qdot[:, -1], np.array((0, 0))) - - # save and load - TestUtils.save_and_load(sol, ocp, False) - - # simulate - TestUtils.simulate(sol) - else: - ode_solver = ode_solver() - ocp = ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=1, - n_shooting=30, - n_threads=n_threads, - use_sx=use_sx, - ode_solver=ode_solver, - phase_dynamics=phase_dynamics, - expand_dynamics=ode_solver_orig != OdeSolver.IRK, - ) - sol = ocp.solve() - - # Check objective function value - is_collocation = isinstance(ode_solver, OdeSolver.COLLOCATION) and not isinstance(ode_solver, OdeSolver.IRK) - f = np.array(sol.cost) - np.testing.assert_equal(f.shape, (1, 1)) - if isinstance(ode_solver, OdeSolver.RK8): - np.testing.assert_almost_equal(f[0, 0], 9.821989132327003) - elif is_collocation: - pass - else: - np.testing.assert_almost_equal(f[0, 0], 9.834017207589055) - - # Check constraints - g = np.array(sol.constraints) - if is_collocation: - np.testing.assert_equal(g.shape, (600, 1)) - np.testing.assert_almost_equal(g, np.zeros((600, 1))) - else: - np.testing.assert_equal(g.shape, (120, 1)) - np.testing.assert_almost_equal(g, np.zeros((120, 1))) - - # Check some of the results - q, qdot, tau = (sol.states["q"], sol.states["qdot"], sol.controls["tau"]) - - # initial and final position - np.testing.assert_almost_equal(q[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(q[:, -1], np.array((0, 3.14))) - - # initial and final velocities - np.testing.assert_almost_equal(qdot[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(qdot[:, -1], np.array((0, 0))) - - # initial and final controls - if isinstance(ode_solver, OdeSolver.RK8): - np.testing.assert_almost_equal(tau[:, 0], np.array((5.67291529, 0))) - np.testing.assert_almost_equal(tau[:, -2], np.array((-11.71262836, 0))) - elif is_collocation: - pass - else: - np.testing.assert_almost_equal(tau[:, 0], np.array((5.72227268, 0))) - np.testing.assert_almost_equal(tau[:, -2], np.array((-11.62799294, 0))) - - # save and load - TestUtils.save_and_load(sol, ocp, False) - - # simulate - TestUtils.simulate(sol) - - -@pytest.mark.parametrize("use_sx", [False, True]) -def test_pendulum_save_and_load_rk8(use_sx): - from bioptim.examples.getting_started import example_save_and_load as ocp_module - - bioptim_folder = os.path.dirname(ocp_module.__file__) - - ocp = ocp_module.prepare_ocp( - biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", - final_time=1, - n_shooting=10, - n_threads=1, - use_sx=use_sx, - ode_solver=OdeSolver.RK8(), - expand_dynamics=True, - ) - sol = ocp.solve() - - # Check objective function value - f = np.array(sol.cost) - np.testing.assert_equal(f.shape, (1, 1)) - np.testing.assert_almost_equal(f[0, 0], 1134.4262872942047) - - # Check constraints - g = np.array(sol.constraints) - np.testing.assert_equal(g.shape, (40, 1)) - np.testing.assert_almost_equal(g, np.zeros((40, 1))) - - # Check some of the results - q, qdot, tau = (sol.states["q"], sol.states["qdot"], sol.controls["tau"]) - - # initial and final position - np.testing.assert_almost_equal(q[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(q[:, -1], np.array((0, 3.14))) - - # initial and final velocities - np.testing.assert_almost_equal(qdot[:, 0], np.array((0, 0))) - np.testing.assert_almost_equal(qdot[:, -1], np.array((0, 0))) - - # initial and final controls - np.testing.assert_almost_equal(tau[:, 0], np.array((4.18966502, 0))) - np.testing.assert_almost_equal(tau[:, -2], np.array((-17.59767942, 0))) - - # save and load - TestUtils.save_and_load(sol, ocp, False) - - # simulate - TestUtils.simulate(sol) - - @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @pytest.mark.parametrize("ode_solver", [OdeSolver.RK4, OdeSolver.RK8, OdeSolver.IRK]) def test_custom_constraint_track_markers(ode_solver, phase_dynamics): @@ -536,13 +352,6 @@ def test_initial_guesses(ode_solver, interpolation, random_init, phase_dynamics) np.testing.assert_almost_equal(tau[:, 0], np.array([5.0, 9.81, 7.85])) np.testing.assert_almost_equal(tau[:, -2], np.array([-5.0, 9.81, -7.85])) - # save and load - if interpolation == InterpolationType.CUSTOM and not random_init: - with pytest.raises(AttributeError, match="'PathCondition' object has no attribute 'custom_function'"): - TestUtils.save_and_load(sol, ocp, False) - else: - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -594,9 +403,6 @@ def test_cyclic_objective(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array([9.89210954, 9.39362112, -15.53061197])) np.testing.assert_almost_equal(tau[:, -2], np.array([17.16370432, 9.78643138, -26.94701577])) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -649,9 +455,6 @@ def test_cyclic_constraint(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array([20.0, 9.81, -31.4])) np.testing.assert_almost_equal(tau[:, -2], np.array([20.0, 9.81, -31.4])) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -708,9 +511,6 @@ def test_phase_transitions(ode_solver, phase_dynamics): np.testing.assert_almost_equal(controls[0]["tau"][:, 0], np.array((0.73170732, 12.71705188, -0.0928732))) np.testing.assert_almost_equal(controls[-1]["tau"][:, -2], np.array((0.11614402, 8.70686126, 1.05599166))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate with pytest.raises( RuntimeError, @@ -833,10 +633,6 @@ def test_parameter_optimization(ode_solver, phase_dynamics): cost_values_all = np.sum(sol.detailed_cost[i]["cost_value_weighted"] for i in range(len(sol.detailed_cost))) np.testing.assert_almost_equal(cost_values_all, f[0, 0]) - # TODO: fix save and load - # # save and load - # TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -955,9 +751,6 @@ def test_example_external_forces(ode_solver): # detailed cost values np.testing.assert_almost_equal(sol.detailed_cost[0]["cost_value_weighted"], 7067.851604540213) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -1027,9 +820,6 @@ def test_example_multiphase(ode_solver_type, phase_dynamics): np.testing.assert_almost_equal(controls[2]["tau"][:, 0], np.array((0.3078264, 9.81, 0.34001243))) np.testing.assert_almost_equal(controls[2]["tau"][:, -2], np.array((-0.36233407, 9.81, -0.58394606))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -1151,9 +941,6 @@ def test_contact_forces_inequality_greater_than_constraint(ode_solver, phase_dyn np.testing.assert_almost_equal(tau[:, 0], np.array((-33.50557304))) np.testing.assert_almost_equal(tau[:, -2], np.array((-29.43209257))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -1204,9 +991,6 @@ def test_contact_forces_inequality_lesser_than_constraint(ode_solver): np.testing.assert_almost_equal(tau[:, 0], np.array((-24.36593641))) np.testing.assert_almost_equal(tau[:, -2], np.array((-24.36125297))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -1432,9 +1216,6 @@ def test_multinode_constraints(ode_solver, phase_dynamics): np.testing.assert_almost_equal(controls[0]["tau"][:, 0], np.array([1.32977862, 9.81, 0.0])) np.testing.assert_almost_equal(controls[-1]["tau"][:, -2], np.array([-1.2, 9.81, -1.884])) - # save and load - TestUtils.save_and_load(sol, ocp, False) - def test_multistart(): from bioptim.examples.getting_started import example_multistart as ocp_module @@ -1660,6 +1441,3 @@ def test_example_variable_scaling(phase_dynamics): # initial and final controls np.testing.assert_almost_equal(tau[:, 0], np.array([-1000.00000999, 0.0])) np.testing.assert_almost_equal(tau[:, -2], np.array([-1000.00000999, 0.0])) - - # save and load - TestUtils.save_and_load(sol, ocp, False) diff --git a/tests/shard3/test_global_symmetrical_torque_driven_ocp.py b/tests/shard3/test_global_symmetrical_torque_driven_ocp.py index 095991723..0b8dcb0ce 100644 --- a/tests/shard3/test_global_symmetrical_torque_driven_ocp.py +++ b/tests/shard3/test_global_symmetrical_torque_driven_ocp.py @@ -56,9 +56,6 @@ def test_symmetry_by_mapping(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((1.16129033, 1.16129033, -0.58458751))) np.testing.assert_almost_equal(tau[:, -2], np.array((-1.16129033, -1.16129033, 0.58458751))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -110,8 +107,5 @@ def test_symmetry_by_constraint(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((1.16129033, 1.16129033, 0, -0.58458751, 0.58458751))) np.testing.assert_almost_equal(tau[:, -2], np.array((-1.16129033, -1.16129033, 0, 0.58458751, -0.58458751))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) diff --git a/tests/shard3/test_global_torque_driven_ocp.py b/tests/shard3/test_global_torque_driven_ocp.py index 0335a0abc..0d9f06a36 100644 --- a/tests/shard3/test_global_torque_driven_ocp.py +++ b/tests/shard3/test_global_torque_driven_ocp.py @@ -58,9 +58,6 @@ def test_track_markers(ode_solver, actuator_type, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((1.4516128810214546, 9.81, 2.2790322540381487))) np.testing.assert_almost_equal(tau[:, -2], np.array((-1.4516128810214546, 9.81, -2.2790322540381487))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -118,9 +115,6 @@ def test_track_markers_changing_constraints(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((4.2641129, 9.81, 2.27903226))) np.testing.assert_almost_equal(tau[:, -2], np.array((1.36088709, 9.81, -2.27903226))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -158,9 +152,6 @@ def test_track_markers_changing_constraints(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((-5.625, 21.06, 2.2790323))) np.testing.assert_almost_equal(tau[:, -2], np.array((-5.625, 21.06, -2.27903226))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -211,9 +202,6 @@ def test_track_markers_with_actuators(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((0.2140175, 0.981, 0.3360075))) np.testing.assert_almost_equal(tau[:, -2], np.array((-0.2196496, 0.981, -0.3448498))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -307,9 +295,6 @@ def test_track_marker_2D_pendulum(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((6.93890241, -12.76433504))) np.testing.assert_almost_equal(tau[:, -2], np.array((0.13156876, 0.93749913))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -404,9 +389,6 @@ def test_track_marker_2D_pendulum(ode_solver, defects_type, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((6.8532419, -12.1810791))) np.testing.assert_almost_equal(tau[:, -2], np.array((0.1290981, 0.9345706))) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -529,9 +511,6 @@ def test_trampo_quaternions(phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.zeros((12,)), decimal=6) np.testing.assert_almost_equal(tau[:, -2], np.zeros((12,)), decimal=6) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=6) @@ -668,9 +647,6 @@ def test_torque_activation_driven(ode_solver, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((-0.2256539, 0.0681475)), decimal=3) np.testing.assert_almost_equal(tau[:, -2], np.array((-0.0019898, -0.0238914)), decimal=3) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=4) diff --git a/tests/shard3/test_global_torque_driven_with_contact_ocp.py b/tests/shard3/test_global_torque_driven_with_contact_ocp.py index c4154081f..83921241a 100644 --- a/tests/shard3/test_global_torque_driven_with_contact_ocp.py +++ b/tests/shard3/test_global_torque_driven_with_contact_ocp.py @@ -85,9 +85,6 @@ def test_maximize_predicted_height_CoM(objective_name, phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((-18.7970058))) np.testing.assert_almost_equal(tau[:, -2], np.array(-0.1918057)) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol) @@ -134,9 +131,6 @@ def test_maximize_predicted_height_CoM_with_actuators(phase_dynamics): np.testing.assert_almost_equal(tau[:, 0], np.array((-0.550905))) np.testing.assert_almost_equal(tau[:, -2], np.array(-0.0050623)) - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) diff --git a/tests/shard3/test_muscle_driven_ocp_implicit.py b/tests/shard3/test_muscle_driven_ocp_implicit.py index 29558e990..9701bc70c 100644 --- a/tests/shard3/test_muscle_driven_ocp_implicit.py +++ b/tests/shard3/test_muscle_driven_ocp_implicit.py @@ -90,8 +90,5 @@ def test_muscle_driven_ocp_implicit(ode_solver, phase_dynamics): else: raise ValueError("Test not ready") - # save and load - TestUtils.save_and_load(sol, ocp, False) - # simulate TestUtils.simulate(sol, decimal_value=5) diff --git a/tests/utils.py b/tests/utils.py index 4c9eed1e6..855a36c1c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -37,32 +37,6 @@ def load_module(path: str) -> Any: spec.loader.exec_module(module) return module - @staticmethod - def save_and_load(sol, ocp, test_solve_of_loaded=False, solver=None): - file_path = "test" - ocp.save(sol, f"{file_path}.bo") - ocp_load, sol_load = OptimalControlProgram.load(f"{file_path}.bo") - - TestUtils.deep_assert(sol, sol_load) - TestUtils.deep_assert(sol_load, sol) - if test_solve_of_loaded: - sol_from_load = ocp_load.solve(solver) - TestUtils.deep_assert(sol, sol_from_load) - TestUtils.deep_assert(sol_from_load, sol) - - TestUtils.deep_assert(ocp_load, ocp) - TestUtils.deep_assert(ocp, ocp_load) - - ocp.save(sol, f"{file_path}_sa.bo", stand_alone=True) - with open(f"{file_path}_sa.bo", "rb") as file: - states, controls, parameters = pickle.load(file) - TestUtils.deep_assert(states, sol.states) - TestUtils.deep_assert(controls, sol.controls) - TestUtils.deep_assert(parameters, sol.parameters) - - os.remove(f"{file_path}.bo") - os.remove(f"{file_path}_sa.bo") - @staticmethod def deep_assert(first_elem, second_elem): if isinstance(first_elem, dict): From 81fd8c6d0a2c41e91c617c43cebba0a5f9a71977 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 14 Nov 2023 11:08:12 -0500 Subject: [PATCH 2/5] Fixed MHE --- .../moving_horizon_estimation/cyclic_nmpc.py | 2 +- .../examples/moving_horizon_estimation/mhe.py | 2 +- .../optimization/optimal_control_program.py | 15 +---- .../receding_horizon_optimization.py | 65 ++++++++++++------- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/bioptim/examples/moving_horizon_estimation/cyclic_nmpc.py b/bioptim/examples/moving_horizon_estimation/cyclic_nmpc.py index ca70ff8aa..d0b7fdce0 100644 --- a/bioptim/examples/moving_horizon_estimation/cyclic_nmpc.py +++ b/bioptim/examples/moving_horizon_estimation/cyclic_nmpc.py @@ -69,7 +69,7 @@ def prepare_nmpc( dynamics, cycle_len, cycle_duration, - objective_functions=new_objectives, + common_objective_functions=new_objectives, constraints=constraints, x_bounds=x_bound, u_bounds=u_bound, diff --git a/bioptim/examples/moving_horizon_estimation/mhe.py b/bioptim/examples/moving_horizon_estimation/mhe.py index 2deebf845..c4fbb17dd 100644 --- a/bioptim/examples/moving_horizon_estimation/mhe.py +++ b/bioptim/examples/moving_horizon_estimation/mhe.py @@ -155,7 +155,7 @@ def prepare_mhe( Dynamics(DynamicsFcn.TORQUE_DRIVEN, expand_dynamics=expand_dynamics, phase_dynamics=phase_dynamics), window_len, window_duration, - objective_functions=new_objectives, + common_objective_functions=new_objectives, x_bounds=x_bounds, u_bounds=u_bounds, x_init=x_init_list, diff --git a/bioptim/optimization/optimal_control_program.py b/bioptim/optimization/optimal_control_program.py index 2a494f2eb..830f40e98 100644 --- a/bioptim/optimization/optimal_control_program.py +++ b/bioptim/optimization/optimal_control_program.py @@ -134,8 +134,6 @@ class OptimalControlProgram: Set the internal stochastic variables (s_init, s_bounds, s_scaling) if any of the phases is stochastic _check_quaternions_hasattr(self, bio_model) Check if the bio_model has quaternions and set the flag accordingly - _check_and_prepare_dynamics(self, dynamics) - Check if the dynamics is a Dynamics or a DynamicsList and set the flag accordingly """ def __init__( @@ -236,7 +234,6 @@ def __init__( bio_model = self._initialize_model(bio_model) # Placed here because of MHE - self._check_and_prepare_dynamics(dynamics) s_init, s_bounds, s_scaling = self._set_stochastic_internal_stochastic_variables() self._check_and_set_threads(n_threads) @@ -337,17 +334,7 @@ def _initialize_model(self, bio_model): bio_model = self._check_quaternions_hasattr(bio_model) self.n_phases = len(bio_model) return bio_model - - def _check_and_prepare_dynamics(self, dynamics): - if isinstance(dynamics, Dynamics): - dynamics_type_tp = DynamicsList() - dynamics_type_tp.add(dynamics) - self.dynamics = dynamics_type_tp - elif isinstance(dynamics, DynamicsList): - self.dynamics = dynamics - elif not isinstance(dynamics, DynamicsList): - raise RuntimeError("dynamics should be a Dynamics or a DynamicsList") - + def _check_and_set_threads(self, n_threads): if not isinstance(n_threads, int) or isinstance(n_threads, bool) or n_threads < 1: raise RuntimeError("n_threads should be a positive integer greater or equal than 1") diff --git a/bioptim/optimization/receding_horizon_optimization.py b/bioptim/optimization/receding_horizon_optimization.py index ed2a97971..912865704 100644 --- a/bioptim/optimization/receding_horizon_optimization.py +++ b/bioptim/optimization/receding_horizon_optimization.py @@ -3,6 +3,7 @@ from typing import Callable from time import perf_counter +from casadi import SX import numpy as np from .optimal_control_program import OptimalControlProgram @@ -35,6 +36,7 @@ def __init__( dynamics: Dynamics | DynamicsList, window_len: int | list | tuple, window_duration: int | float | list | tuple, + common_objective_functions: ObjectiveList = None, use_sx=True, **kwargs, ): @@ -49,6 +51,8 @@ def __init__( The length of the sliding window. It is translated into n_shooting in each individual optimization program window_duration The time in second of the sliding window + common_objective_functions + The objective functions that carries through all the individual optimization program use_sx Same as OCP, but has True as default value """ @@ -56,12 +60,21 @@ def __init__( if isinstance(bio_model, (list, tuple)) and len(bio_model) > 1: raise ValueError("Receding horizon optimization must be defined using only one bio_model") + if "objective_functions" in kwargs: + raise ValueError( + "'objective_functions' should be defined via 'common_objective_functions' for the objectives that are shared between the windows " + "or via 'update_objectives' for the objective that is specific to each window" + ) + + self.common_objective_functions = deepcopy(common_objective_functions) + super(RecedingHorizonOptimization, self).__init__( bio_model=bio_model, dynamics=dynamics, n_shooting=window_len, phase_time=window_duration, use_sx=use_sx, + objective_functions=self.common_objective_functions, **kwargs, ) self.total_optimization_run = 0 @@ -217,15 +230,16 @@ def _initialize_solution(self, states: list, controls: list): for key in self.nlp[0].controls.keys(): controls_tp = np.concatenate([control[key] for control in controls], axis=1) u_init_for_solution.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME) - if self.original_values["control_type"] == ControlType.CONSTANT: + if self.nlp[0].control_type == ControlType.CONSTANT: controls_tp = controls_tp[:, :-1] u_init.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME) - model_class = self.original_values["bio_model"][0][0] - model_initializer = self.original_values["bio_model"][0][1] + model_serialized = self.nlp[0].model.serialize() + model_class = model_serialized[0] + model_initializer = model_serialized[1] solution_ocp = OptimalControlProgram( bio_model=model_class(**model_initializer), - dynamics=self.original_values["dynamics"][0], + dynamics=self.nlp[0].dynamics_type, ode_solver=self.nlp[0].ode_solver, n_shooting=self.total_optimization_run - 1, phase_time=self.total_optimization_run * self.nlp[0].dt, @@ -233,7 +247,7 @@ def _initialize_solution(self, states: list, controls: list): u_bounds=self.nlp[0].u_bounds, x_init=x_init, u_init=u_init, - use_sx=self.original_values["use_sx"], + use_sx=self.cx == SX, ) s_init = InitialGuessList() p_init = InitialGuessList() @@ -439,21 +453,23 @@ def _initialize_solution(self, states: list, controls: list): for key in self.nlp[0].controls.keys(): controls_tp = np.concatenate([control[key] for control in controls], axis=1) u_init_for_solution.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME) - if self.original_values["control_type"] == ControlType.CONSTANT: + if self.nlp[0].control_type == ControlType.CONSTANT: controls_tp = controls_tp[:, :-1] u_init.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME) - model_class = self.original_values["bio_model"][0][0] - model_initializer = self.original_values["bio_model"][0][1] + + model_serialized = self.nlp[0].model.serialize() + model_class = model_serialized[0] + model_initializer = model_serialized[1] solution_ocp = OptimalControlProgram( bio_model=model_class(**model_initializer), - dynamics=self.original_values["dynamics"][0], + dynamics=self.nlp[0].dynamics_type, n_shooting=self.total_optimization_run * self.nlp[0].ns - 1, phase_time=self.total_optimization_run * self.nlp[0].ns * self.nlp[0].dt, x_bounds=self.nlp[0].x_bounds, u_bounds=self.nlp[0].u_bounds, x_init=x_init, u_init=u_init, - use_sx=self.original_values["use_sx"], + use_sx=self.cx == SX, ) s_init = InitialGuessList() p_init = InitialGuessList() @@ -673,22 +689,23 @@ def _initialize_solution(self, states: list, controls: list): for key in self.nlp[0].controls.keys(): controls_tp = np.concatenate([control[key] for control in controls], axis=1) u_init_for_solution.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME, phase=0) - if self.original_values["control_type"] == ControlType.CONSTANT: + if self.nlp[0].control_type == ControlType.CONSTANT: controls_tp = controls_tp[:, :-1] u_init.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME, phase=0) - model_class = self.original_values["bio_model"][0][0] - model_initializer = self.original_values["bio_model"][0][1] + model_serialized = self.nlp[0].model.serialize() + model_class = model_serialized[0] + model_initializer = model_serialized[1] solution_ocp = OptimalControlProgram( bio_model=model_class(**model_initializer), - dynamics=self.original_values["dynamics"][0], + dynamics=self.nlp[0].dynamics_type, ode_solver=self.nlp[0].ode_solver, - objective_functions=deepcopy(self.original_values["objective_functions"]), + objective_functions=deepcopy(self.common_objective_functions), n_shooting=self.cycle_len * self.total_optimization_run - 1, phase_time=(self.cycle_len * self.total_optimization_run - 1) * self.nlp[0].dt, x_init=x_init, u_init=u_init, - use_sx=self.original_values["use_sx"], + use_sx=self.cx == SX, ) s_init = InitialGuessList() p_init = InitialGuessList() @@ -710,25 +727,23 @@ def _initialize_one_cycle(self, states: np.ndarray, controls: np.ndarray): for key in self.nlp[0].controls.keys(): controls_tp = controls[key] u_init_for_solution.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME, phase=0) - if self.original_values["control_type"] == ControlType.CONSTANT: + if self.nlp[0].control_type == ControlType.CONSTANT: controls_tp = controls_tp[:, :-1] u_init.add(key, controls_tp, interpolation=InterpolationType.EACH_FRAME, phase=0) - original_values = self.original_values - - model_class = original_values["bio_model"][0][0] - model_initializer = original_values["bio_model"][0][1] - + model_serialized = self.nlp[0].model.serialize() + model_class = model_serialized[0] + model_initializer = model_serialized[1] solution_ocp = OptimalControlProgram( bio_model=model_class(**model_initializer), - dynamics=original_values["dynamics"][0], - objective_functions=deepcopy(original_values["objective_functions"]), + dynamics=self.nlp[0].dynamics_type, + objective_functions=deepcopy(self.common_objective_functions), ode_solver=self.nlp[0].ode_solver, n_shooting=self.cycle_len, phase_time=self.cycle_len * self.nlp[0].dt, x_init=x_init, u_init=u_init, - use_sx=original_values["use_sx"], + use_sx=self.cx == SX, ) s_init = InitialGuessList() p_init = InitialGuessList() From 5d41ad4797a8ab9b76f9622aa1fd3450278ff3fe Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 14 Nov 2023 13:14:55 -0500 Subject: [PATCH 3/5] same --- bioptim/examples/moving_horizon_estimation/multi_cyclic_nmpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bioptim/examples/moving_horizon_estimation/multi_cyclic_nmpc.py b/bioptim/examples/moving_horizon_estimation/multi_cyclic_nmpc.py index 282ed742a..800ecb2cc 100644 --- a/bioptim/examples/moving_horizon_estimation/multi_cyclic_nmpc.py +++ b/bioptim/examples/moving_horizon_estimation/multi_cyclic_nmpc.py @@ -86,7 +86,7 @@ def prepare_nmpc( cycle_duration=cycle_duration, n_cycles_simultaneous=n_cycles_simultaneous, n_cycles_to_advance=n_cycles_to_advance, - objective_functions=new_objectives, + common_objective_functions=new_objectives, constraints=constraints, x_bounds=x_bounds, u_bounds=u_bounds, From 07827d66063dc68bcb0f9e8bccafbf64f7ebcf40 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 14 Nov 2023 16:15:29 -0500 Subject: [PATCH 4/5] Final non passing tests fix --- tests/shard3/test_initial_condition.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/shard3/test_initial_condition.py b/tests/shard3/test_initial_condition.py index 050735398..46bb14161 100644 --- a/tests/shard3/test_initial_condition.py +++ b/tests/shard3/test_initial_condition.py @@ -198,7 +198,7 @@ def custom_bound_func(current_shooting, val, total_shooting): @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_simulate_from_initial_multiple_shoot(phase_dynamics): - from bioptim.examples.getting_started import example_save_and_load as ocp_module + from bioptim.examples.getting_started import pendulum as ocp_module bioptim_folder = os.path.dirname(ocp_module.__file__) @@ -244,7 +244,7 @@ def test_simulate_from_initial_multiple_shoot(phase_dynamics): @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_simulate_from_initial_single_shoot(phase_dynamics): # Load pendulum - from bioptim.examples.getting_started import example_save_and_load as ocp_module + from bioptim.examples.getting_started import pendulum as ocp_module bioptim_folder = os.path.dirname(ocp_module.__file__) From 305ec5046ec280d2b943c63448f2767e96a77188 Mon Sep 17 00:00:00 2001 From: Pariterre Date: Tue, 14 Nov 2023 17:00:51 -0500 Subject: [PATCH 5/5] Fix other tests --- .../obstacle_avoidance_direct_collocation.py | 3 +- .../torque_driven_ocp/trampo_quaternions.py | 2 +- .../receding_horizon_optimization.py | 2 +- .../test_global_stochastic_collocation.py | 114 +++++------------- 4 files changed, 30 insertions(+), 91 deletions(-) diff --git a/bioptim/examples/stochastic_optimal_control/obstacle_avoidance_direct_collocation.py b/bioptim/examples/stochastic_optimal_control/obstacle_avoidance_direct_collocation.py index ad5a5aa3d..fb25e2120 100644 --- a/bioptim/examples/stochastic_optimal_control/obstacle_avoidance_direct_collocation.py +++ b/bioptim/examples/stochastic_optimal_control/obstacle_avoidance_direct_collocation.py @@ -184,10 +184,9 @@ def prepare_socp( objective_functions = ObjectiveList() objective_functions.add(ObjectiveFcn.Mayer.MINIMIZE_TIME, weight=1) objective_functions.add( - ObjectiveFcn.Mayer.MINIMIZE_CONTROL, + ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="u", weight=1e-2 / (2 * n_shooting), - node=Node.ALL_SHOOTING, quadratic=True, ) diff --git a/bioptim/examples/torque_driven_ocp/trampo_quaternions.py b/bioptim/examples/torque_driven_ocp/trampo_quaternions.py index 1e38f264c..e5ab5f8db 100644 --- a/bioptim/examples/torque_driven_ocp/trampo_quaternions.py +++ b/bioptim/examples/torque_driven_ocp/trampo_quaternions.py @@ -86,7 +86,7 @@ def prepare_ocp( # Add objective functions objective_functions = ObjectiveList() objective_functions.add(ObjectiveFcn.Mayer.MINIMIZE_MARKERS, marker_index=1, weight=-1) - objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="tau", node=Node.ALL_SHOOTING, weight=100) + objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="tau", weight=100) # Dynamics dynamics = DynamicsList() diff --git a/bioptim/optimization/receding_horizon_optimization.py b/bioptim/optimization/receding_horizon_optimization.py index 912865704..686067090 100644 --- a/bioptim/optimization/receding_horizon_optimization.py +++ b/bioptim/optimization/receding_horizon_optimization.py @@ -74,7 +74,7 @@ def __init__( n_shooting=window_len, phase_time=window_duration, use_sx=use_sx, - objective_functions=self.common_objective_functions, + objective_functions=common_objective_functions, **kwargs, ) self.total_optimization_run = 0 diff --git a/tests/shard5/test_global_stochastic_collocation.py b/tests/shard5/test_global_stochastic_collocation.py index 69b64eeb3..4ec8f49ee 100644 --- a/tests/shard5/test_global_stochastic_collocation.py +++ b/tests/shard5/test_global_stochastic_collocation.py @@ -212,7 +212,7 @@ def test_obstacle_avoidance_direct_collocation(): # Check objective function value f = np.array(sol.cost) np.testing.assert_equal(f.shape, (1, 1)) - np.testing.assert_almost_equal(f[0, 0], 4.587065067031554) + np.testing.assert_almost_equal(f[0, 0], 4.099146411209181) # Check constraints g = np.array(sol.constraints) @@ -232,82 +232,34 @@ def test_obstacle_avoidance_direct_collocation(): ) # initial and final position - np.testing.assert_almost_equal(q[:, 0], np.array([0.0, 2.91660270e00])) - np.testing.assert_almost_equal(q[:, -1], np.array([0.0, 2.91660270e00])) - np.testing.assert_almost_equal(qdot[:, 0], np.array([4.59876163, 0.33406115])) - np.testing.assert_almost_equal(qdot[:, -1], np.array([4.59876163, 0.33406115])) + np.testing.assert_almost_equal(q[:, 0], np.array([0.0, 2.97814416e00])) + np.testing.assert_almost_equal(q[:, -1], np.array([0.0, 2.97814416e00])) + np.testing.assert_almost_equal(qdot[:, 0], np.array([4.28153262, 0.36568711])) + np.testing.assert_almost_equal(qdot[:, -1], np.array([4.28153262, 0.36568711])) - np.testing.assert_almost_equal(u[:, 0], np.array([3.94130314, 0.50752995])) - np.testing.assert_almost_equal(u[:, -2], np.array([1.37640701, 2.78054156])) + np.testing.assert_almost_equal(u[:, 0], np.array([2.62449641, 1.42518093])) + np.testing.assert_almost_equal(u[:, -2], np.array([0.98869976, 2.83323732])) np.testing.assert_almost_equal( m[:, 0], np.array( [ - 1.00000000e00, - -1.05389293e-23, - -9.29903240e-24, - 1.00382361e-23, - -1.64466833e-23, - 1.00000000e00, - 1.21492152e-24, - -3.15104115e-23, - -6.68416587e-25, - -6.00029062e-24, - 1.00000000e00, - 1.99489733e-23, - -1.16322274e-24, - -2.03253417e-24, - -3.00499207e-24, - 1.00000000e00, - 2.19527862e-01, - -1.88588087e-02, - -2.00283989e-01, - -8.03404360e-02, - -1.99327784e-02, - 2.02962627e-01, - -8.39758964e-02, - -2.49822789e-01, - 1.76793622e-02, - 5.30096916e-03, - -6.35628572e-03, - -1.01527618e-02, - 6.21147642e-03, - 2.87692596e-02, - -1.06499714e-02, - -1.48244735e-02, - 4.01184050e-01, - -1.20760665e-02, - -3.47575458e-01, - -1.01031369e-01, - -1.22801502e-02, - 3.94781689e-01, - -1.03912381e-01, - -4.08950331e-01, - 3.31437788e-02, - 9.65931210e-03, - 1.64098610e-03, - 3.61379227e-02, - 9.94099379e-03, - 4.10555191e-02, - 3.89631730e-02, - 2.71848362e-02, - 2.74709609e-01, - -6.03467730e-05, - -1.00613832e-01, - -1.27941917e-02, - -9.52485792e-05, - 2.74478998e-01, - -1.23522568e-02, - -1.07746467e-01, - 1.00776666e-02, - 1.25778066e-03, - 1.65876475e-01, - 2.50629520e-02, - 1.28718848e-03, - 1.07109173e-02, - 2.48728130e-02, - 1.81242999e-01, + 1.00000000e+00, 4.18671255e-23, 4.28371222e-21, -2.09918267e-19, + 3.44229928e-22, 1.00000000e+00, -4.66804386e-20, -5.14324329e-20, + -2.06675909e-23, -1.61247050e-22, 1.00000000e+00, -7.04413401e-20, + -3.27139223e-22, -2.61268483e-22, 4.41399057e-20, 1.00000000e+00, + 2.11891384e-01, -1.58680379e-02, -3.07585749e-01, -9.08757981e-02, + -1.72161795e-02, 1.95250052e-01, -9.69940564e-02, -3.86911301e-01, + 2.81495095e-02, 4.94627743e-03, -1.06536334e-02, -3.39095322e-03, + 6.01146660e-03, 4.34835191e-02, -2.32542690e-03, -7.95425413e-03, + 4.08499325e-01, -9.64710094e-03, -4.26676607e-01, -9.74461605e-02, + -9.88390650e-03, 4.05552654e-01, -1.00910193e-01, -4.92316087e-01, + 4.39486929e-02, 8.74347038e-03, 4.72289379e-02, 3.98745524e-02, + 9.07610650e-03, 5.27435499e-02, 4.32026417e-02, 8.63948277e-02, + 2.77396392e-01, 3.38212145e-05, -9.93589312e-02, -1.01929042e-02, + -8.60843770e-06, 2.77166036e-01, -9.77442621e-03, -1.02836238e-01, + 9.71202833e-03, 1.00344034e-03, 1.90048454e-01, 2.17086631e-02, + 1.03402088e-03, 9.94432755e-03, 2.14855478e-02, 2.02784050e-01, ] ), decimal=6, @@ -317,22 +269,10 @@ def test_obstacle_avoidance_direct_collocation(): cov[:, -2], np.array( [ - 0.00440214, - -0.00021687, - 0.00470812, - -0.00133034, - -0.00021687, - 0.00214526, - -0.00098746, - 0.00142654, - 0.00470812, - -0.00098746, - 0.02155766, - -0.00941652, - -0.00133034, - 0.00142654, - -0.00941652, - 0.00335482, + 0.00373282, 0.00024041, 0.00319094, -0.00109769, 0.00024041, + 0.00075171, -0.00102995, 0.00112714, 0.00319094, -0.00102995, + 0.03139494, -0.01650263, -0.00109769, 0.00112714, -0.01650263, + 0.01354738, ] ), decimal=6,