diff --git a/.github/workflows/run_tests_linux.yml b/.github/workflows/run_tests_linux.yml index 5cce358dc..fd47123e7 100644 --- a/.github/workflows/run_tests_linux.yml +++ b/.github/workflows/run_tests_linux.yml @@ -59,18 +59,17 @@ jobs: if: matrix.shard == 1 - name: Run tests with code coverage - run: pytest -v --color=yes --cov-report term-missing --cov=bioptim --cov-report=xml:coverage.xml tests/shard${{ matrix.shard }} + run: pytest -v --color=yes --cov-report term-missing --cov=bioptim tests/shard${{ matrix.shard }} if: matrix.os == 'ubuntu-latest' - + - name: Archive coverage report id: archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage${{ matrix.shard }} - path: | - coverage.xml - .coverage - + name: coverage-${{ matrix.shard }} + path: .coverage + if-no-files-found: error + include-hidden-files: true merge-coverage: needs: build @@ -84,59 +83,37 @@ jobs: run: echo "PREFIX=${{ env.PREFIX_LINUX }}" >> $GITHUB_ENV - - name: Checkout code uses: actions/checkout@v3 - - name: Setup environment - uses: conda-incubator/setup-miniconda@v2 - with: - miniforge-version: latest - use-mamba: true - activate-environment: bioptim - environment-file: environment.yml - - - name: Print mamba info - run: | - conda info -# mamba list - - name: Install extra dependencies run: | - sudo apt-get install -y python3-pip + sudo apt install -y python3-pip pip3 install coverage - name: Download all workflow run artifacts id: download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 + with: + pattern: coverage-* - name: Rename coverage files run: | for shard in {1,2,3,4,5,6}; do - mv coverage${shard}/coverage.xml coverage${shard}.xml - mv coverage${shard}/.coverage* .coverage${shard} + mv coverage-${shard}/.coverage .coverage${shard} done - - name: Show current dir content - run: pwd; ls -a -l - - name: Merge coverage reports - run: coverage combine .coverage1 .coverage2 .coverage3 .coverage4 - - - name: Show current dir content with new .coverage file - run: pwd; ls -a + run: coverage combine .coverage1 .coverage2 .coverage3 .coverage4 .coverage5 .coverage6 - name: Generate XML report run: | coverage xml coverage report -m - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + - uses: codecov/codecov-action@v5 with: - #token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml - #files: ./coverage1.xml, ./coverage2.xml, ./coverage3.xml, ./coverage4.xml + files: ./coverage.xml flags: unittests fail_ci_if_error: true - verbose: true \ No newline at end of file + verbose: true diff --git a/bioptim/examples/__main__.py b/bioptim/examples/__main__.py index 42bb246aa..1ae22adb8 100644 --- a/bioptim/examples/__main__.py +++ b/bioptim/examples/__main__.py @@ -3,13 +3,14 @@ import keyword import os import re -import sys import subprocess +import sys -import pyqtgraph as pg -from functools import lru_cache from collections import OrderedDict -from PyQt5 import QtCore, QtGui, QtWidgets +from functools import lru_cache + +import pyqtgraph as pg +from pyqtgraph.Qt import QT_LIB, QtCore, QtGui, QtWidgets try: import acados @@ -137,6 +138,8 @@ ] ) +app = pg.mkQApp() + class Ui_Form(object): def setupUi(self, Form): @@ -206,7 +209,6 @@ def retranslateUi(self, Form): path = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, path) -app = pg.mkQApp() QRegularExpression = QtCore.QRegularExpression @@ -740,7 +742,7 @@ def main(): app = pg.mkQApp() loader = ExampleLoader() loader.ui.exampleTree.setCurrentIndex(loader.ui.exampleTree.model().index(0, 0)) - app.exec() + pg.exec() if __name__ == "__main__": diff --git a/bioptim/interfaces/acados_interface.py b/bioptim/interfaces/acados_interface.py index c3d4ea2aa..49dc5dd83 100644 --- a/bioptim/interfaces/acados_interface.py +++ b/bioptim/interfaces/acados_interface.py @@ -163,7 +163,7 @@ def __acados_export_model(self, ocp): x_sym = ocp.nlp[0].states.scaled.cx_start u = ocp.nlp[0].controls.cx_start u_sym = ocp.nlp[0].controls.scaled.cx_start - p = ocp.nlp[0].parameters.cx + p = ocp.nlp[0].parameters.scaled.cx p_sym = ocp.nlp[0].parameters.scaled.cx a = ocp.nlp[0].algebraic_states.cx_start a_sym = ocp.nlp[0].algebraic_states.scaled.cx_start @@ -294,7 +294,7 @@ def __set_constraints(self, ocp): dt = nlp.dt x = nlp.states.cx_start u = nlp.controls.cx_start - p = nlp.parameters.cx + p = nlp.parameters.scaled.cx a = nlp.algebraic_states.cx_start d = nlp.numerical_timeseries.cx diff --git a/bioptim/interfaces/interface_utils.py b/bioptim/interfaces/interface_utils.py index 83962aa9f..5b61e3ef9 100644 --- a/bioptim/interfaces/interface_utils.py +++ b/bioptim/interfaces/interface_utils.py @@ -79,19 +79,19 @@ def generic_solve(interface, expand_during_shake_tree=False) -> dict: interface.online_optim(interface.ocp, interface.opts.show_options) # Thread here on (f and all_g) instead of individually for each function? - interface.sqp_nlp = {"x": v, "f": sum1(all_objectives), "g": all_g} + interface.nlp = {"x": v, "f": sum1(all_objectives), "g": all_g} interface.c_compile = interface.opts.c_compile options = interface.opts.as_dict(interface) if interface.c_compile: if not interface.ocp_solver or interface.ocp.program_changed: - nlpsol("nlpsol", interface.solver_name.lower(), interface.sqp_nlp, options).generate_dependencies("nlp.c") + nlpsol("nlpsol", interface.solver_name.lower(), interface.nlp, options).generate_dependencies("nlp.c") interface.ocp_solver = nlpsol("nlpsol", interface.solver_name, Importer("nlp.c", "shell"), options) interface.ocp.program_changed = False else: - interface.ocp_solver = nlpsol("solver", interface.solver_name.lower(), interface.sqp_nlp, options) + interface.ocp_solver = nlpsol("solver", interface.solver_name.lower(), interface.nlp, options) - interface.sqp_limits = { + interface.limits = { "lbx": v_bounds[0], "ubx": v_bounds[1], "lbg": all_g_bounds.min, @@ -100,13 +100,13 @@ def generic_solve(interface, expand_during_shake_tree=False) -> dict: } if interface.lam_g is not None: - interface.sqp_limits["lam_g0"] = interface.lam_g + interface.limits["lam_g0"] = interface.lam_g if interface.lam_x is not None: - interface.sqp_limits["lam_x0"] = interface.lam_x + interface.limits["lam_x0"] = interface.lam_x # Solve the problem tic = perf_counter() - interface.out = {"sol": interface.ocp_solver.call(interface.sqp_limits)} + interface.out = {"sol": interface.ocp_solver.call(interface.limits)} interface.out["sol"]["solver_time_to_optimize"] = interface.ocp_solver.stats()["t_wall_total"] interface.out["sol"]["real_time_to_optimize"] = perf_counter() - tic interface.out["sol"]["iter"] = interface.ocp_solver.stats()["iter_count"] @@ -172,7 +172,7 @@ def _shake_tree_for_penalties(ocp, penalties_cx, v, v_bounds, expand): try: penalty = penalty.expand() except RuntimeError: - # This happens mostly when there is a Newton decent in the penalty + # This happens mostly when, for instance, there is a Newton decent in the penalty pass return penalty(vertcat(*dt, v[len(dt) :])) diff --git a/bioptim/interfaces/ipopt_interface.py b/bioptim/interfaces/ipopt_interface.py index 754d1be47..85a55da5e 100644 --- a/bioptim/interfaces/ipopt_interface.py +++ b/bioptim/interfaces/ipopt_interface.py @@ -10,11 +10,11 @@ ) from .solver_interface import SolverInterface from ..interfaces import Solver -from bioptim.optimization.solution.solution import Solution -from ..optimization.non_linear_program import NonLinearProgram from ..misc.enums import ( SolverType, ) +from ..optimization.non_linear_program import NonLinearProgram +from ..optimization.solution.solution import Solution class IpoptInterface(SolverInterface): @@ -27,9 +27,9 @@ class IpoptInterface(SolverInterface): Options irrelevant of a specific ocp opts: IPOPT Options of the current ocp - ipopt_nlp: dict + nlp: dict The declaration of the variables Ipopt-friendly - ipopt_limits: dict + limits: dict The declaration of the bound Ipopt-friendly lam_g: np.ndarray The lagrange multiplier of the constraints to initialize the solver @@ -64,8 +64,8 @@ def __init__(self, ocp): self.opts = Solver.IPOPT() self.solver_name = SolverType.IPOPT.value - self.ipopt_nlp = {} - self.ipopt_limits = {} + self.nlp = {} + self.limits = {} self.ocp_solver = None self.c_compile = False diff --git a/bioptim/interfaces/sqp_interface.py b/bioptim/interfaces/sqp_interface.py index f5baf9993..12583aed9 100644 --- a/bioptim/interfaces/sqp_interface.py +++ b/bioptim/interfaces/sqp_interface.py @@ -1,5 +1,6 @@ import numpy as np +from bioptim.optimization.solution.solution import Solution from .interface_utils import ( generic_online_optim, generic_solve, @@ -9,11 +10,10 @@ ) from .solver_interface import SolverInterface from ..interfaces import Solver -from bioptim.optimization.solution.solution import Solution -from ..optimization.non_linear_program import NonLinearProgram from ..misc.enums import ( SolverType, ) +from ..optimization.non_linear_program import NonLinearProgram class SQPInterface(SolverInterface): @@ -26,9 +26,9 @@ class SQPInterface(SolverInterface): Options irrelevant of a specific ocp opts: SQP Options of the current ocp - sqp_nlp: dict + nlp: dict The declaration of the variables SQP-friendly - sqp_limits: dict + limits: dict The declaration of the bound SQP-friendly lam_g: np.ndarray The lagrange multiplier of the constraints to initialize the solver @@ -63,8 +63,8 @@ def __init__(self, ocp): self.opts = Solver.SQP_METHOD() self.solver_name = SolverType.SQP.value - self.sqp_nlp = {} - self.sqp_limits = {} + self.nlp = {} + self.limits = {} self.ocp_solver = None self.c_compile = False diff --git a/bioptim/models/biorbd/biorbd_model.py b/bioptim/models/biorbd/biorbd_model.py index 201652325..e1c30c7fc 100644 --- a/bioptim/models/biorbd/biorbd_model.py +++ b/bioptim/models/biorbd/biorbd_model.py @@ -1,3 +1,5 @@ +import os +from pathlib import Path from typing import Callable import biorbd_casadi as biorbd @@ -89,7 +91,7 @@ def name(self) -> str: @property def path(self) -> str: - return self.model.path().relativePath().to_string() + return self.model.path().absolutePath().to_string() def copy(self): return BiorbdModel(self.path) diff --git a/bioptim/models/biorbd/multi_biorbd_model.py b/bioptim/models/biorbd/multi_biorbd_model.py index 711942317..18cca53ee 100644 --- a/bioptim/models/biorbd/multi_biorbd_model.py +++ b/bioptim/models/biorbd/multi_biorbd_model.py @@ -90,11 +90,12 @@ def deep_copy(self, *args): raise NotImplementedError("Deep copy is not implemented yet for MultiBiorbdModel class") @property - def path(self) -> (list[str], list[str]): + def path(self) -> tuple[list[str], list[str]]: return [model.path for model in self.models], [model.path for model in self.extra_models] def copy(self): - return MultiBiorbdModel(tuple(self.path[0]), tuple(self.path[1])) + all_paths = self.path + return MultiBiorbdModel(tuple(all_paths[0]), tuple(all_paths[1])) def serialize(self) -> tuple[Callable, dict]: return MultiBiorbdModel, dict(bio_model=tuple(self.path[0]), extra_bio_models=tuple(self.path[1])) diff --git a/bioptim/models/protocols/stochastic_biomodel.py b/bioptim/models/protocols/stochastic_biomodel.py index 82bebb4c3..bb14ec055 100644 --- a/bioptim/models/protocols/stochastic_biomodel.py +++ b/bioptim/models/protocols/stochastic_biomodel.py @@ -38,6 +38,10 @@ def sensory_reference(self, time, states, controls, parameters, algebraic_states def reshape_to_matrix(var, shape): """ Restore the matrix form of the variables + + See Also + -------- + reshape_to_vector """ if var.shape[0] != shape[0] * shape[1]: @@ -83,6 +87,10 @@ def reshape_to_cholesky_matrix(var, shape): def reshape_to_vector(matrix): """ Restore the vector form of the matrix + + See Also + -------- + reshape_to_matrix """ shape_0, shape_1 = matrix.shape[0], matrix.shape[1] if isinstance(matrix, np.ndarray): diff --git a/resources/plotting_server.py b/resources/plotting_server.py index b709a0cf8..fcc103e3c 100644 --- a/resources/plotting_server.py +++ b/resources/plotting_server.py @@ -1,7 +1,7 @@ """ -This file is an example of how to run a bioptim Online plotting server. Apart on Macos, this is usually not the way to run a -bioptim server as it is easier to run it as an automatic multiprocess. This is achieved by setting -`Solver.IPOPT(online_optim=OnlineOptim.MULTIPROCESS_SERVER)`. +This file is an example of how to run a bioptim Online plotting server. Apart on Macos, this is usually not the way +to run a bioptim server as it is easier to run it as an automatic multiprocess. This is achieved by setting +`Solver.IPOPT(online_optim=OnlineOptim.MULTIPROCESS_SERVER)`. If set to OnlineOptim.SERVER, then the plotting server is mandatory. Since the server runs usings sockets, it is possible to run the server on a different machine than the one running the diff --git a/tests/shard1/test__global_plots.py b/tests/shard1/test__global_plots.py index abba43461..3992ddddf 100644 --- a/tests/shard1/test__global_plots.py +++ b/tests/shard1/test__global_plots.py @@ -3,15 +3,17 @@ """ import io -import matplotlib -import numpy as np import os -import pytest import sys -from casadi import Function, MX from bioptim import CostType, OdeSolver, Solver, BiorbdModel, PhaseDynamics from bioptim.limits.penalty import PenaltyOption +from casadi import Function, MX +import matplotlib +import numpy as np +import pytest + +from ..utils import TestUtils matplotlib.use("Agg") @@ -21,7 +23,7 @@ def test_plot_graphs_one_phase(phase_dynamics): # Load graphs_one_phase from bioptim.examples.torque_driven_ocp import track_markers_with_torque_actuators as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -40,7 +42,7 @@ def test_plot_check_conditioning(phase_dynamics): # Load graphs check conditioning from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -58,7 +60,7 @@ def test_plot_check_conditioning_live(phase_dynamics): # Load graphs check conditioning from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -73,7 +75,7 @@ def test_plot_check_conditioning_live(phase_dynamics): def test_plot_ipopt_output_live(phase_dynamics): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -87,7 +89,7 @@ def test_plot_ipopt_output_live(phase_dynamics): def test_save_ipopt_output(): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -107,7 +109,7 @@ def test_plot_merged_graphs(phase_dynamics): # Load graphs_one_phase from bioptim.examples.muscle_driven_ocp import muscle_excitations_tracker as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" @@ -146,7 +148,7 @@ def test_plot_graphs_multi_phases(phase_dynamics): # Load graphs_one_phase from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -162,7 +164,7 @@ def test_add_new_plot(phase_dynamics): # Load graphs_one_phase from bioptim.examples.torque_driven_ocp import track_markers_with_torque_actuators as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -189,7 +191,7 @@ def test_console_objective_functions(phase_dynamics): # Load graphs_one_phase from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", diff --git a/tests/shard1/test_acados_interface.py b/tests/shard1/test_acados_interface.py index 5d2e54b47..5a5421e6f 100644 --- a/tests/shard1/test_acados_interface.py +++ b/tests/shard1/test_acados_interface.py @@ -6,11 +6,12 @@ import os import shutil -import pytest from sys import platform import numpy as np import numpy.testing as npt +import pytest + from bioptim import ( BiorbdModel, Axis, @@ -23,14 +24,12 @@ MovingHorizonEstimator, Dynamics, DynamicsFcn, - InitialGuessList, InterpolationType, Solver, BoundsList, PhaseDynamics, SolutionMerge, ) - from tests.utils import TestUtils @@ -41,7 +40,7 @@ def test_acados_no_obj(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -65,7 +64,7 @@ def test_acados_one_mayer(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", n_shooting=10, tf=2, expand_dynamics=True @@ -95,7 +94,7 @@ def test_acados_mayer_first_node(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -131,7 +130,7 @@ def test_acados_several_mayer(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -168,7 +167,7 @@ def test_acados_one_lagrange(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 target = np.expand_dims(np.arange(0, n_shooting + 1), axis=0) @@ -213,7 +212,7 @@ def test_acados_one_lagrange_and_one_mayer(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 target = np.expand_dims(np.arange(0, n_shooting + 1), axis=0) @@ -261,7 +260,7 @@ def test_acados_control_lagrange_and_state_mayer(cost_type): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 target = np.array([[2]]) @@ -300,7 +299,7 @@ def test_acados_options(cost_type): from bioptim.examples.acados import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -334,7 +333,7 @@ def test_acados_fail_external(): from bioptim.examples.acados import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -357,7 +356,7 @@ def test_acados_fail_lls(): from bioptim.examples.acados import static_arm as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/arm26.bioMod", @@ -384,7 +383,7 @@ def test_acados_custom_dynamics(problem_type_custom): from bioptim.examples.getting_started import custom_dynamics as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -423,7 +422,7 @@ def test_acados_one_parameter(): from bioptim.examples.getting_started import custom_parameters as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) target_g = np.zeros((3, 1)) target_g[2] = -9.81 @@ -495,7 +494,7 @@ def test_acados_several_parameter(): from bioptim.examples.getting_started import custom_parameters as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) target_g = np.zeros((3, 1)) target_g[2] = -9.81 @@ -572,7 +571,7 @@ def test_acados_one_end_constraints(): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -625,7 +624,7 @@ def test_acados_constraints_all(): from bioptim.examples.track import track_marker_on_segment as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -669,7 +668,7 @@ def test_acados_constraints_end_all(): from bioptim.examples.track import track_marker_on_segment as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -714,7 +713,7 @@ def test_acados_phase_dynamics_reject(): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", diff --git a/tests/shard1/test_biorbd_model.py b/tests/shard1/test_biorbd_model.py index 68d4be3fa..3bf2169d3 100644 --- a/tests/shard1/test_biorbd_model.py +++ b/tests/shard1/test_biorbd_model.py @@ -4,17 +4,20 @@ """ import os + +import biorbd_casadi as biorbd +from bioptim import BiorbdModel import pytest import numpy as np import numpy.testing as npt -import biorbd_casadi as biorbd -from bioptim import BiorbdModel + +from ..utils import TestUtils def test_biorbd_model_import(): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = "/models/pendulum.bioMod" BiorbdModel(bioptim_folder + model_path) @@ -53,7 +56,7 @@ def test_bounds_from_ranges(my_keys): x_max_qdot = [[31.41592654, 31.41592654, 31.41592654], [31.41592654, 31.41592654, 31.41592654]] x_max_qddot = [[314.15926536, 314.15926536, 314.15926536], [314.15926536, 314.15926536, 314.15926536]] - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = "/models/pendulum.bioMod" bio_model = BiorbdModel(bioptim_folder + model_path) diff --git a/tests/shard1/test_biorbd_model_holonomic.py b/tests/shard1/test_biorbd_model_holonomic.py index 8df79fc43..616665cc0 100644 --- a/tests/shard1/test_biorbd_model_holonomic.py +++ b/tests/shard1/test_biorbd_model_holonomic.py @@ -1,18 +1,18 @@ import os +from bioptim import HolonomicBiorbdModel, HolonomicConstraintsFcn, HolonomicConstraintsList, Solver, SolutionMerge +from casadi import DM, MX import numpy as np import numpy.testing as npt import pytest -from casadi import DM, MX -from bioptim import HolonomicBiorbdModel, HolonomicConstraintsFcn, HolonomicConstraintsList, Solver, SolutionMerge -from tests.utils import TestUtils +from ..utils import TestUtils def test_model_holonomic(): from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = bioptim_folder + "/models/triple_pendulum.bioMod" model = HolonomicBiorbdModel(biorbd_model_path) @@ -162,7 +162,7 @@ def test_example_two_pendulums(): """Test the holonomic_constraints/two_pendulums example""" from bioptim.examples.holonomic_constraints import two_pendulums - bioptim_folder = os.path.dirname(two_pendulums.__file__) + bioptim_folder = TestUtils.module_folder(two_pendulums) # --- Prepare the ocp --- # ocp, model = two_pendulums.prepare_ocp( diff --git a/tests/shard1/test_biorbd_multi_model.py b/tests/shard1/test_biorbd_multi_model.py index 3e8b8cc1d..f8c7b61be 100644 --- a/tests/shard1/test_biorbd_multi_model.py +++ b/tests/shard1/test_biorbd_multi_model.py @@ -1,21 +1,19 @@ import os -import pytest + +from bioptim import MultiBiorbdModel, BiMappingList, BoundsList +import biorbd_casadi as biorbd import numpy as np import numpy.testing as npt from casadi import DM -import biorbd_casadi as biorbd -from bioptim import ( - MultiBiorbdModel, - BiMappingList, - BoundsList, -) +import pytest + from ..utils import TestUtils def test_biorbd_model_import(): from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = "/models/triple_pendulum.bioMod" biorbd_model_path_modified_inertia = "/models/triple_pendulum_modified_inertia.bioMod" MultiBiorbdModel((bioptim_folder + biorbd_model_path, bioptim_folder + biorbd_model_path_modified_inertia)) @@ -37,7 +35,7 @@ def test_biorbd_model_import(): def test_biorbd_model(): from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = "/models/triple_pendulum.bioMod" biorbd_model_path_modified_inertia = "/models/triple_pendulum_modified_inertia.bioMod" models = MultiBiorbdModel( diff --git a/tests/shard1/test_continuity_linear_continuous.py b/tests/shard1/test_continuity_linear_continuous.py index 96d078b5c..6f7f5a8c2 100644 --- a/tests/shard1/test_continuity_linear_continuous.py +++ b/tests/shard1/test_continuity_linear_continuous.py @@ -1,9 +1,9 @@ import os -import numpy as np +from bioptim import PhaseDynamics, ControlType, QuadratureRule, Solver import numpy.testing as npt -from bioptim import PhaseDynamics, ControlType, QuadratureRule, Solver +from ..utils import TestUtils def test_continuity_linear_continuous_global(): @@ -13,7 +13,7 @@ def test_continuity_linear_continuous_global(): """ from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", diff --git a/tests/shard1/test_dynamics.py b/tests/shard1/test_dynamics.py index 1910cca7a..1df5e2264 100644 --- a/tests/shard1/test_dynamics.py +++ b/tests/shard1/test_dynamics.py @@ -1,4 +1,3 @@ -import os import re import numpy as np @@ -22,7 +21,8 @@ PhaseDynamics, ExternalForceSetTimeSeries, ) -from tests.utils import TestUtils + +from ..utils import TestUtils class OptimalControlProgram: @@ -1289,7 +1289,7 @@ def test_with_contact_error(dynamics_fcn, phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module from bioptim import BoundsList, ObjectiveList, OdeSolver, OptimalControlProgram - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) bio_model = BiorbdModel(bioptim_folder + "/models/pendulum.bioMod") diff --git a/tests/shard1/test_global_align.py b/tests/shard1/test_global_align.py index e48600995..a2099d618 100644 --- a/tests/shard1/test_global_align.py +++ b/tests/shard1/test_global_align.py @@ -2,12 +2,12 @@ Test for file IO """ +import platform import pytest -import os +from bioptim import OdeSolver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, PhaseDynamics, SolutionMerge from tests.utils import TestUtils @@ -17,7 +17,7 @@ def test_track_segment_on_rt(ode_solver, phase_dynamics): from bioptim.examples.track import track_segment_on_rt as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -63,7 +63,7 @@ def test_track_segment_on_rt(ode_solver, phase_dynamics): def test_track_marker_on_segment(ode_solver, phase_dynamics): from bioptim.examples.track import track_marker_on_segment as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -74,6 +74,17 @@ def test_track_marker_on_segment(ode_solver, phase_dynamics): phase_dynamics=phase_dynamics, expand_dynamics=ode_solver != OdeSolver.IRK, ) + + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(105, 1), + expected_v_f_g=[49.41640708093857, 380.05556849200684, 18.43946477408692], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value diff --git a/tests/shard1/test_global_fatigue.py b/tests/shard1/test_global_fatigue.py index e4f4f2a16..b16506516 100644 --- a/tests/shard1/test_global_fatigue.py +++ b/tests/shard1/test_global_fatigue.py @@ -1,19 +1,18 @@ import platform -import os -import pytest -import numpy as np -import numpy.testing as npt from bioptim import OdeSolver, Solver, PhaseDynamics, SolutionMerge +import numpy.testing as npt +import numpy as np +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_xia_fatigable_muscles(phase_dynamics): from bioptim.examples.fatigue import static_arm_with_fatigue as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/arm26_constant.bioMod" ocp = ocp_module.prepare_ocp( @@ -27,6 +26,17 @@ def test_xia_fatigable_muscles(phase_dynamics): n_threads=1, phase_dynamics=phase_dynamics, ) + + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(613, 1), + expected_v_f_g=[306.3365222501875, 3457.8474074260107, 244.8780101727087], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value @@ -89,7 +99,7 @@ def test_xia_fatigable_muscles(phase_dynamics): def test_xia_stabilized_fatigable_muscles(phase_dynamics): from bioptim.examples.fatigue import static_arm_with_fatigue as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/arm26_constant.bioMod" ocp = ocp_module.prepare_ocp( @@ -103,6 +113,17 @@ def test_xia_stabilized_fatigable_muscles(phase_dynamics): n_threads=8 if phase_dynamics == PhaseDynamics.SHARED_DURING_THE_PHASE else 1, expand_dynamics=True, ) + + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(613, 1), + expected_v_f_g=[306.3365222501875, 3457.8474074260107, 486.75997079938367], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value @@ -166,7 +187,7 @@ def test_xia_stabilized_fatigable_muscles(phase_dynamics): def test_michaud_fatigable_muscles(phase_dynamics): from bioptim.examples.fatigue import static_arm_with_fatigue as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/arm26_constant.bioMod" ocp = ocp_module.prepare_ocp( @@ -203,7 +224,7 @@ def test_michaud_fatigable_muscles(phase_dynamics): def test_effort_fatigable_muscles(phase_dynamics): from bioptim.examples.fatigue import static_arm_with_fatigue as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/arm26_constant.bioMod" ocp = ocp_module.prepare_ocp( @@ -217,6 +238,17 @@ def test_effort_fatigable_muscles(phase_dynamics): n_threads=8 if phase_dynamics == PhaseDynamics.SHARED_DURING_THE_PHASE else 1, expand_dynamics=True, ) + + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(301, 1), + expected_v_f_g=[148.61306144921627, 2777.429584653532, -15.071606578311815], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value @@ -275,7 +307,7 @@ def test_fatigable_xia_torque_non_split(phase_dynamics): if platform.system() == "Darwin": return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -308,7 +340,7 @@ def test_fatigable_xia_torque_non_split(phase_dynamics): def test_fatigable_xia_torque_split(phase_dynamics): from bioptim.examples.fatigue import pendulum_with_fatigue as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -321,6 +353,17 @@ def test_fatigable_xia_torque_split(phase_dynamics): phase_dynamics=phase_dynamics, expand_dynamics=True, ) + + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.ones((217, 1)) / 10, # Random generates nan in the g vector + expected_v_f_g=[21.7, 0.04, 0.8256265085043029], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value @@ -378,7 +421,7 @@ def test_fatigable_xia_stabilized_torque_split(phase_dynamics): # This is a long test and CI is already long for Windows return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -448,7 +491,7 @@ def test_fatigable_michaud_torque_non_split(phase_dynamics): # This is a long test and CI is already long for Windows return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -485,7 +528,7 @@ def test_fatigable_michaud_torque_split(phase_dynamics): # This tst fails on the CI return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -556,7 +599,7 @@ def test_fatigable_effort_torque_non_split(phase_dynamics): # This tst fails on the CI return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( @@ -593,7 +636,7 @@ def test_fatigable_effort_torque_split(phase_dynamics): # This tst fails on the CI return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = f"{bioptim_folder}/models/pendulum.bioMod" ocp = ocp_module.prepare_ocp( diff --git a/tests/shard1/test_global_mhe.py b/tests/shard1/test_global_mhe.py index 1f42eb910..4a8dff046 100644 --- a/tests/shard1/test_global_mhe.py +++ b/tests/shard1/test_global_mhe.py @@ -2,11 +2,12 @@ Test for file IO """ -import os +from bioptim import Solver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt import pytest -from bioptim import Solver, PhaseDynamics, SolutionMerge + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -16,7 +17,7 @@ def update_functions(_nmpc, cycle_idx, _sol): from bioptim.examples.moving_horizon_estimation import cyclic_nmpc as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_cycles = 3 cycle_len = 20 diff --git a/tests/shard1/test_mhe.py b/tests/shard1/test_mhe.py index 5af77578b..1394ff924 100644 --- a/tests/shard1/test_mhe.py +++ b/tests/shard1/test_mhe.py @@ -3,8 +3,7 @@ import shutil from sys import platform -import numpy as np -import numpy.testing as npt +from bioptim.misc.enums import SolverType from bioptim import ( BiorbdModel, Solver, @@ -16,9 +15,10 @@ PhaseDynamics, SolutionMerge, ) +import numpy as np +import numpy.testing as npt -from tests.utils import TestUtils -from bioptim.misc.enums import SolverType +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -34,7 +34,7 @@ def test_mhe(solver, phase_dynamics): from bioptim.examples.moving_horizon_estimation import mhe as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) bio_model = BiorbdModel(bioptim_folder + "/models/cart_pendulum.bioMod") nq = bio_model.nb_q diff --git a/tests/shard1/test_prepare_all_examples.py b/tests/shard1/test_prepare_all_examples.py index 8193cc7fb..b0b518370 100644 --- a/tests/shard1/test_prepare_all_examples.py +++ b/tests/shard1/test_prepare_all_examples.py @@ -1,15 +1,17 @@ -import numpy as np import os -import pytest from bioptim import InterpolationType, PhaseDynamics +import numpy as np +import pytest + +from ..utils import TestUtils ## examples/acados def test__acados__cube(): from bioptim.examples.acados import cube as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -22,7 +24,7 @@ def test__acados__cube(): def test__acados__pendulum(): from bioptim.examples.acados import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -35,7 +37,7 @@ def test__acados__pendulum(): def test__acados__static_arm(): from bioptim.examples.acados import static_arm as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/arm26.bioMod", @@ -52,7 +54,7 @@ def test__acados__static_arm(): def test__getting_started__custom_bounds(): from bioptim.examples.getting_started import custom_bounds as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -66,7 +68,7 @@ def test__getting_started__custom_bounds(): def test__getting_started__custom_constraints(): from bioptim.examples.getting_started import custom_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -78,7 +80,7 @@ def test__getting_started__custom_constraints(): def test__getting_started__custom_dynamics(): from bioptim.examples.getting_started import custom_dynamics as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -92,7 +94,7 @@ def test__getting_started__custom_dynamics(): def test__getting_started__custom_initial_guess(interpolation, random): from bioptim.examples.getting_started import custom_initial_guess as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( bioptim_folder + "/models/cube.bioMod", @@ -108,7 +110,7 @@ def test__getting_started__custom_initial_guess(interpolation, random): def test__getting_started__custom_objectives(): from bioptim.examples.getting_started import custom_objectives as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -120,7 +122,7 @@ def test__getting_started__custom_objectives(): def test__getting_started__custom_parameters(): from bioptim.examples.getting_started import custom_parameters as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) target_g = np.zeros((3, 1)) target_g[2] = -9.81 @@ -144,7 +146,7 @@ def test__getting_started__custom_parameters(): def test__getting_started__custom_phase_transitions(): from bioptim.examples.getting_started import custom_phase_transitions as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -156,7 +158,7 @@ def test__getting_started__custom_phase_transitions(): def test__getting_started__custom_plotting(): from bioptim.examples.getting_started import custom_plotting as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -170,7 +172,7 @@ def test__getting_started__custom_plotting(): def test__getting_started__example_continuity_as_objective(): from bioptim.examples.getting_started import example_continuity_as_objective as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp_first_pass( biorbd_model_path=bioptim_folder + "/models/pendulum_maze.bioMod", @@ -183,7 +185,7 @@ def test__getting_started__example_continuity_as_objective(): def test__getting_started__example_cyclic_movement(): from bioptim.examples.getting_started import example_cyclic_movement as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -198,7 +200,7 @@ def test__getting_started__example_cyclic_movement(): def test__getting_started__example_external_forces(): from bioptim.examples.getting_started import example_external_forces as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_with_forces.bioMod", @@ -211,7 +213,7 @@ def test__getting_started__example_inequality_constraint(): example_inequality_constraint as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/../torque_driven_ocp/models/2segments_4dof_2contacts.bioMod", @@ -237,7 +239,7 @@ def test__getting_started__example_multinode_constraints(): example_multinode_constraints as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -252,7 +254,7 @@ def test__getting_started__example_multinode_objective(): example_multinode_objective as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -279,7 +281,7 @@ def test__getting_started__example_multinode_objective(): def test__getting_started__example_multiphase(): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -306,7 +308,7 @@ def test__getting_started__example_simulation(): def test__getting_started__pendulum(): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -322,7 +324,7 @@ def test__getting_started__pendulum_constrained_states_controls(): pendulum_constrained_states_controls as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -349,7 +351,7 @@ def test__muscle_driven_ocp__muscle_excitations_tracker(): def test__muscle_driven_ocp__static_arm(): from bioptim.examples.muscle_driven_ocp import static_arm as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/arm26.bioMod", @@ -370,7 +372,7 @@ def test__optimal_time_ocp__multiphase_time_constraint(): multiphase_time_constraint as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) final_time = (2, 5, 4) time_min = (1, 3, 0.1) @@ -390,7 +392,7 @@ def test__optimal_time_ocp__multiphase_time_constraint(): def test__optimal_time_ocp__pendulum_min_time_Mayer(): from bioptim.examples.optimal_time_ocp import pendulum_min_time_Mayer as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -404,7 +406,7 @@ def test__optimal_time_ocp__pendulum_min_time_Mayer(): def test__optimal_time_ocp__time_constraint(): from bioptim.examples.optimal_time_ocp import time_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -422,7 +424,7 @@ def test__symmetrical_torque_driven_ocp__symmetry_by_constraint(): symmetry_by_constraint as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cubeSym.bioMod", @@ -434,7 +436,7 @@ def test__symmetrical_torque_driven_ocp__symmetry_by_constraint(): def test__symmetrical_torque_driven_ocp__symmetry_by_mapping(): from bioptim.examples.symmetrical_torque_driven_ocp import symmetry_by_mapping as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cubeSym.bioMod", @@ -448,7 +450,7 @@ def test__torque_driven_ocp__maximize_predicted_height_CoM(): maximize_predicted_height_CoM as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_4dof_2contacts.bioMod", @@ -467,7 +469,7 @@ def test__torque_driven_ocp__multi_biorbd_model(): example_multi_biorbd_model as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/triple_pendulum.bioMod", @@ -483,7 +485,7 @@ def test__torque_driven_ocp__phase_transition_uneven_variable_number_by_mapping( phase_transition_uneven_variable_number_by_mapping as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/double_pendulum.bioMod", @@ -499,7 +501,7 @@ def test__torque_driven_ocp__phase_transition_uneven_variable_number_by_bounds() phase_transition_uneven_variable_number_by_bounds as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path_with_translations=bioptim_folder + "/models/double_pendulum_with_translations.bioMod", @@ -512,7 +514,7 @@ def test__torque_driven_ocp__phase_transition_uneven_variable_number_by_bounds() def test__torque_driven_ocp__spring_load(): from bioptim.examples.torque_driven_ocp import spring_load as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) for scenario in range(8): ocp_module.prepare_ocp( @@ -525,7 +527,7 @@ def test__torque_driven_ocp__spring_load(): def test__track__optimal_estimation(): from bioptim.examples.track import optimal_estimation as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) markers_ref = np.array( [ @@ -1347,7 +1349,7 @@ def test__torque_driven_ocp__track_markers_with_torque_actuators(): track_markers_with_torque_actuators as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -1362,7 +1364,7 @@ def test__torque_driven_ocp__track_markers_with_torque_actuators(): def test__torque_driven_ocp__example_quaternions(): from bioptim.examples.torque_driven_ocp import example_quaternions as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/trunk_and_2arm_quaternion.bioMod", @@ -1377,7 +1379,7 @@ def test__torque_driven_ocp__minimize_segment_velocity(): example_minimize_segment_velocity as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/triple_pendulum.bioMod", @@ -1390,7 +1392,7 @@ def test__torque_driven_ocp__minimize_segment_velocity(): def test__track__track_marker_on_segment(): from bioptim.examples.track import track_marker_on_segment as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -1405,7 +1407,7 @@ def test__track__track_marker_on_segment(): def test__track__track_segment_on_rt(): from bioptim.examples.track import track_segment_on_rt as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_and_line.bioMod", @@ -1419,7 +1421,7 @@ def test__track__track_segment_on_rt(): def test__getting_started__example_variable_scaling(): from bioptim.examples.getting_started import example_variable_scaling as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -1435,7 +1437,7 @@ def test__torque_driven_ocp__torque_activation_driven(): torque_activation_driven as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_2dof_2contacts.bioMod", @@ -1451,7 +1453,7 @@ def test__inverse_optimal_control__double_pendulum_torque_driven_IOCP(): double_pendulum_torque_driven_IOCP as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( weights=[0.4, 0.3, 0.3], @@ -1466,7 +1468,7 @@ def test__contact_and_muscle_forces_example(): contact_forces_inequality_constraint_muscle as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_4dof_2contacts_1muscle.bioMod", @@ -1483,7 +1485,7 @@ def test__contact_and_muscle_forces_example_excitation(): contact_forces_inequality_constraint_muscle_excitations as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_4dof_2contacts_1muscle.bioMod", @@ -1499,7 +1501,7 @@ def test_min_max_example(): minimize_maximum_torque_by_extra_parameter as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp_module.prepare_ocp( bio_model_path=bioptim_folder + "/models/double_pendulum.bioMod", diff --git a/tests/shard2/test_cost_function_integration.py b/tests/shard2/test_cost_function_integration.py index 022d1b5f9..797d1b80d 100644 --- a/tests/shard2/test_cost_function_integration.py +++ b/tests/shard2/test_cost_function_integration.py @@ -3,13 +3,8 @@ """ import io -import os import sys -import numpy as np -import numpy.testing as npt -import pytest - from bioptim import ( BiorbdModel, OdeSolver, @@ -26,6 +21,11 @@ PhaseDynamics, SolutionMerge, ) +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils def prepare_ocp( @@ -145,7 +145,7 @@ def sum_cost_function_output(sol): def test_pendulum(control_type, integration_rule, objective, phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -282,7 +282,7 @@ def test_pendulum(control_type, integration_rule, objective, phase_dynamics): def test_pendulum_collocation(control_type, integration_rule, objective, phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if integration_rule not in ( QuadratureRule.RECTANGLE_LEFT, @@ -359,7 +359,7 @@ def test_pendulum_collocation(control_type, integration_rule, objective, phase_d def test_pendulum_target(control_type, integration_rule, objective, phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if objective == "qdot": target = np.array( @@ -601,7 +601,7 @@ def test_pendulum_target(control_type, integration_rule, objective, phase_dynami def test_error_mayer_trapz(integration_rule, phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) with pytest.raises( ValueError, diff --git a/tests/shard2/test_global_inverse_optimal_control.py b/tests/shard2/test_global_inverse_optimal_control.py index 2451d62dd..c2d55f8fb 100644 --- a/tests/shard2/test_global_inverse_optimal_control.py +++ b/tests/shard2/test_global_inverse_optimal_control.py @@ -2,20 +2,20 @@ Test for file IO """ -import os - from bioptim import PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt import pytest +from ..utils import TestUtils + @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_double_pendulum_torque_driven_IOCP(phase_dynamics): # Load double pendulum ocp from bioptim.examples.inverse_optimal_control import double_pendulum_torque_driven_IOCP as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = bioptim_folder + "/models/double_pendulum.bioMod" ocp = ocp_module.prepare_ocp( diff --git a/tests/shard2/test_global_minimize_marker_velocity.py b/tests/shard2/test_global_minimize_marker_velocity.py index dd79739fc..d2a1f46ee 100644 --- a/tests/shard2/test_global_minimize_marker_velocity.py +++ b/tests/shard2/test_global_minimize_marker_velocity.py @@ -2,9 +2,8 @@ Test for file IO """ -import pytest -import numpy as np -import numpy.testing as npt +import platform + from bioptim import ( BiorbdModel, OptimalControlProgram, @@ -21,6 +20,10 @@ PhaseDynamics, SolutionMerge, ) +from casadi import Function +import numpy as np +import numpy.testing as npt +import pytest from tests.utils import TestUtils @@ -196,6 +199,18 @@ def test_track_and_minimize_marker_displacement_RT(ode_solver, phase_dynamics): ode_solver=ode_solver, phase_dynamics=phase_dynamics, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(69, 1), + expected_v_f_g=[31.736865760272735, 735.9774884772594, 14.88489768158775], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value @@ -242,6 +257,18 @@ def test_track_and_minimize_marker_velocity(ode_solver, phase_dynamics): ode_solver=ode_solver, phase_dynamics=phase_dynamics, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(69, 1), + expected_v_f_g=[31.736865760272735, 90.85915472423895, 14.88489768158775], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value diff --git a/tests/shard2/test_global_muscle_driven_ocp.py b/tests/shard2/test_global_muscle_driven_ocp.py index 4d2a73bc8..712dab4bf 100644 --- a/tests/shard2/test_global_muscle_driven_ocp.py +++ b/tests/shard2/test_global_muscle_driven_ocp.py @@ -2,14 +2,13 @@ Test for file IO """ -import os import pytest import numpy as np import numpy.testing as npt from bioptim import OdeSolver, ControlType, PhaseDynamics, SolutionMerge -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -25,7 +24,7 @@ def test_muscle_driven_ocp(ode_solver, phase_dynamics): else: control_type = ControlType.CONSTANT - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( bioptim_folder + "/models/arm26.bioMod", diff --git a/tests/shard2/test_global_muscle_tracking_0_False.py b/tests/shard2/test_global_muscle_tracking_0_False.py index 96dec1592..a97bb4502 100644 --- a/tests/shard2/test_global_muscle_tracking_0_False.py +++ b/tests/shard2/test_global_muscle_tracking_0_False.py @@ -2,15 +2,14 @@ Test for file IO """ -import os import pytest -import numpy.testing as npt import platform -import numpy as np from bioptim import OdeSolver, Solver, BiorbdModel, PhaseDynamics, SolutionMerge +import numpy as np +import numpy.testing as npt -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.ONE_PER_NODE]) @@ -34,7 +33,7 @@ def test_muscle_activations_and_states_tracking(ode_solver, n_threads, phase_dyn if n_threads > 1 and phase_dynamics == PhaseDynamics.ONE_PER_NODE: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" diff --git a/tests/shard2/test_global_muscle_tracking_0_True.py b/tests/shard2/test_global_muscle_tracking_0_True.py index 196a40018..5a90ef221 100644 --- a/tests/shard2/test_global_muscle_tracking_0_True.py +++ b/tests/shard2/test_global_muscle_tracking_0_True.py @@ -2,15 +2,14 @@ Test for file IO """ -import os -import pytest import platform +from bioptim import OdeSolver, Solver, BiorbdModel, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, Solver, BiorbdModel, PhaseDynamics, SolutionMerge +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE]) @@ -20,21 +19,13 @@ def test_muscle_activations_and_states_tracking(ode_solver, n_threads, phase_dyn # Load muscle_activations_tracker from bioptim.examples.muscle_driven_ocp import muscle_activations_tracker as ocp_module - if ( - platform.system() == "Windows" - and phase_dynamics == PhaseDynamics.SHARED_DURING_THE_PHASE - and ode_solver == OdeSolver.RK4 - ): - # This one fails on CI - return - # For reducing time phase_dynamics=PhaseDynamics.ONE_PER_NODE is skipped for redundant tests if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: - return + pytest.skip("Redundant test") if n_threads > 1 and phase_dynamics == PhaseDynamics.ONE_PER_NODE: - return + pytest.skip("Redundant test") - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" @@ -64,6 +55,31 @@ def test_muscle_activations_and_states_tracking(ode_solver, n_threads, phase_dyn phase_dynamics=phase_dynamics, expand_dynamics=ode_solver != OdeSolver.IRK, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + match ode_solver: + case OdeSolver.RK4: + v_len = 65 + expected = [30.176497827705912, 216.82271917709082, 41.92662823234213] + case OdeSolver.COLLOCATION: + v_len = 145 + expected = [69.45381468487611, 200.49863362309583, 1010.2302612756722] + case OdeSolver.IRK: + v_len = 65 + expected = [30.176497827705912, 216.82271917709082, 63.204797772662936] + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(v_len, 1), + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + solver = Solver.IPOPT() # solver.set_maximum_iterations(10) sol = ocp.solve(solver) diff --git a/tests/shard2/test_global_muscle_tracking_1.py b/tests/shard2/test_global_muscle_tracking_1.py index ee7dec7e0..82850a441 100644 --- a/tests/shard2/test_global_muscle_tracking_1.py +++ b/tests/shard2/test_global_muscle_tracking_1.py @@ -2,15 +2,14 @@ Test for file IO """ -import os -import pytest import platform +from bioptim import OdeSolver, PhaseDynamics, BiorbdModel, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, PhaseDynamics, BiorbdModel, SolutionMerge +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -19,16 +18,12 @@ def test_muscle_activation_no_residual_torque_and_markers_tracking(ode_solver, p # Load muscle_activations_tracker from bioptim.examples.muscle_driven_ocp import muscle_activations_tracker as ocp_module - if platform.system() == "Windows" and phase_dynamics == PhaseDynamics.ONE_PER_NODE: - # This is a long test and CI is already long for Windows - return - # For reducing time phase_dynamics=False is skipped for redundant tests # and because test fails on CI if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver in (OdeSolver.RK4, OdeSolver.COLLOCATION): - return + pytest.skip("Redundant test") - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" @@ -57,6 +52,31 @@ def test_muscle_activation_no_residual_torque_and_markers_tracking(ode_solver, p phase_dynamics=phase_dynamics, expand_dynamics=ode_solver != OdeSolver.IRK, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + match ode_solver: + case OdeSolver.RK4: + v_len = 55 + expected = [26.473138941541652, 215.04636610774946, 10.574774769800726] + case OdeSolver.COLLOCATION: + v_len = 135 + expected = [64.27619358626245, 199.0608093817752, -497.2117949234156] + case OdeSolver.IRK: + v_len = 55 + expected = [26.473138941541652, 215.04636610774946, -35.9551582488577] + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(v_len, 1), + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value diff --git a/tests/shard2/test_global_muscle_tracking_2.py b/tests/shard2/test_global_muscle_tracking_2.py index 35a18aa1d..c9c8be961 100644 --- a/tests/shard2/test_global_muscle_tracking_2.py +++ b/tests/shard2/test_global_muscle_tracking_2.py @@ -2,14 +2,12 @@ Test for file IO """ -import os -import pytest - +from bioptim import OdeSolver, BiorbdModel, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, BiorbdModel, SolutionMerge +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("ode_solver", [OdeSolver.RK4, OdeSolver.COLLOCATION, OdeSolver.IRK]) @@ -17,7 +15,7 @@ def test_muscle_excitation_with_torque_and_markers_tracking(ode_solver): # Load muscle_excitations_tracker from bioptim.examples.muscle_driven_ocp import muscle_excitations_tracker as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" @@ -153,7 +151,7 @@ def test_muscle_excitation_no_residual_torque_and_markers_tracking(ode_solver): # Load muscle_excitations_tracker from bioptim.examples.muscle_driven_ocp import muscle_excitations_tracker as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/arm26.bioMod" diff --git a/tests/shard2/test_global_nmpc_final.py b/tests/shard2/test_global_nmpc_final.py index 342f8ba3f..367d3cd56 100644 --- a/tests/shard2/test_global_nmpc_final.py +++ b/tests/shard2/test_global_nmpc_final.py @@ -2,13 +2,14 @@ Test for file IO """ -import os import platform -import pytest +from bioptim import Solver, MultiCyclicCycleSolutions, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import Solver, MultiCyclicCycleSolutions, PhaseDynamics, SolutionMerge +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -18,7 +19,7 @@ def update_functions(_nmpc, cycle_idx, _sol): from bioptim.examples.moving_horizon_estimation import multi_cyclic_nmpc as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_cycles_simultaneous = 2 n_cycles_to_advance = 1 @@ -109,7 +110,7 @@ def update_functions(_nmpc, cycle_idx, _sol): from bioptim.examples.moving_horizon_estimation import multi_cyclic_nmpc as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_cycles_simultaneous = 2 n_cycles_to_advance = 1 @@ -147,7 +148,7 @@ def update_functions(_nmpc, cycle_idx, _sol): from bioptim.examples.moving_horizon_estimation import multi_cyclic_nmpc_with_parameters as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_cycles_simultaneous = 2 n_cycles_to_advance = 1 diff --git a/tests/shard2/test_global_optimal_time.py b/tests/shard2/test_global_optimal_time.py index 797f5b8cd..0e2e106cb 100644 --- a/tests/shard2/test_global_optimal_time.py +++ b/tests/shard2/test_global_optimal_time.py @@ -2,11 +2,6 @@ Test for file IO """ -import os -import pytest - -import numpy as np -import numpy.testing as npt from bioptim import ( BiorbdModel, ConstraintList, @@ -24,8 +19,11 @@ PhaseDynamics, SolutionMerge, ) +import numpy as np +import numpy.testing as npt +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -34,7 +32,7 @@ def test_pendulum_max_time_mayer_constrained(ode_solver, phase_dynamics): # Load pendulum_min_time_Mayer from bioptim.examples.optimal_time_ocp import pendulum_min_time_Mayer as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ns = 30 tf = 1 @@ -98,7 +96,7 @@ def test_time_constraint(ode_solver, phase_dynamics): # Load time_constraint from bioptim.examples.optimal_time_ocp import time_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if ode_solver == OdeSolver.IRK: ft = 2 @@ -196,7 +194,7 @@ def test_monophase_time_constraint(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -261,7 +259,7 @@ def test_multiphase_time_constraint(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -332,7 +330,7 @@ def test_multiphase_time_constraint_with_phase_time_equality(ode_solver, phase_d if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", diff --git a/tests/shard2/test_global_optimal_time_mayer_min.py b/tests/shard2/test_global_optimal_time_mayer_min.py index 7299cfdd9..3550cae78 100644 --- a/tests/shard2/test_global_optimal_time_mayer_min.py +++ b/tests/shard2/test_global_optimal_time_mayer_min.py @@ -2,14 +2,14 @@ Test for file IO """ -import os -import pytest +import platform +from bioptim import OdeSolver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, PhaseDynamics, SolutionMerge +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -18,7 +18,7 @@ def test_pendulum_min_time_mayer(ode_solver, phase_dynamics): # Load pendulum_min_time_Mayer from bioptim.examples.optimal_time_ocp import pendulum_min_time_Mayer as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if ode_solver == OdeSolver.IRK: ft = 2 @@ -104,7 +104,7 @@ def test_pendulum_min_time_mayer_constrained(ode_solver, phase_dynamics): # Load pendulum_min_time_Mayer from bioptim.examples.optimal_time_ocp import pendulum_min_time_Mayer as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) tf = 1 ns = 30 @@ -118,6 +118,31 @@ def test_pendulum_min_time_mayer_constrained(ode_solver, phase_dynamics): phase_dynamics=phase_dynamics, expand_dynamics=ode_solver != OdeSolver.IRK, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + match ode_solver: + case OdeSolver.RK4: + v_len = 185 + expected = [87.49523141142917, 11.236203565420874, -0.005115857843225768] + case OdeSolver.COLLOCATION: + v_len = 665 + expected = [329.58704584455836, 11.236203565420874, 32.40020240692716] + case OdeSolver.IRK: + v_len = 185 + expected = [87.49523141142917, 11.236203565420874, 4027.416142481593] + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(v_len, 1), + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check constraints diff --git a/tests/shard2/test_global_sqp.py b/tests/shard2/test_global_sqp.py index b1bba2cc6..0ab51465d 100644 --- a/tests/shard2/test_global_sqp.py +++ b/tests/shard2/test_global_sqp.py @@ -2,18 +2,19 @@ Tests for SQP interface. """ -import os +from bioptim import Solver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import Solver, PhaseDynamics, SolutionMerge import pytest +from ..utils import TestUtils + @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_pendulum(phase_dynamics): from bioptim.examples.sqp_method import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", diff --git a/tests/shard3/test_external_force_class.py b/tests/shard3/test_external_force_class.py index 3bd8ae382..962b565f4 100644 --- a/tests/shard3/test_external_force_class.py +++ b/tests/shard3/test_external_force_class.py @@ -1,10 +1,10 @@ -import os import re +from bioptim import ExternalForceSetTimeSeries, BiorbdModel import numpy as np import pytest -from bioptim import ExternalForceSetTimeSeries, BiorbdModel +from ..utils import TestUtils # Fixture for creating a standard ExternalForceSetTimeSeries instance @@ -140,7 +140,7 @@ def test_fail_within_biomod(external_forces): """Test inserting the external forces in a model.""" from bioptim.examples.getting_started import example_external_forces as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) invalid_segment_name = "segment1" force_array = np.random.rand(6, 10) @@ -172,7 +172,7 @@ def test_success_within_biomod(external_forces): """Test inserting the external forces in a model.""" from bioptim.examples.getting_started import example_external_forces as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) force_array = np.random.rand(6, 10) torque_array = np.random.rand(3, 10) diff --git a/tests/shard3/test_external_forces.py b/tests/shard3/test_external_forces.py index f9bcdad57..7a584110f 100644 --- a/tests/shard3/test_external_forces.py +++ b/tests/shard3/test_external_forces.py @@ -1,9 +1,3 @@ -import os - -import numpy as np -import numpy.testing as npt -import pytest - from bioptim import ( PhaseDynamics, SolutionMerge, @@ -19,6 +13,11 @@ BoundsList, ExternalForceSetTimeSeries, ) +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize( @@ -53,7 +52,7 @@ def test_example_external_forces( ): from bioptim.examples.getting_started import example_external_forces as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_with_forces.bioMod", @@ -239,7 +238,7 @@ def test_example_external_forces_all_at_once(together: bool): from bioptim.examples.getting_started import example_external_forces as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube_with_forces.bioMod", diff --git a/tests/shard3/test_get_time_solution.py b/tests/shard3/test_get_time_solution.py index c0ed317a7..3b183e860 100644 --- a/tests/shard3/test_get_time_solution.py +++ b/tests/shard3/test_get_time_solution.py @@ -1,9 +1,8 @@ -import os - +from bioptim import OdeSolver, Solver, PhaseDynamics, SolutionMerge, TimeAlignment, ControlType, Solution import pytest import numpy.testing as npt -from bioptim import OdeSolver, Solver, PhaseDynamics, SolutionMerge, TimeAlignment, ControlType, Solution +from ..utils import TestUtils def _get_solution( @@ -23,7 +22,7 @@ def _get_solution( if is_multi_phase: from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = bioptim_folder + "/models/cube.bioMod" prepare_args = { "biorbd_model_path": model_path, @@ -34,7 +33,7 @@ def _get_solution( else: from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) model_path = bioptim_folder + "/models/pendulum.bioMod" prepare_args = { "biorbd_model_path": model_path, diff --git a/tests/shard3/test_global_getting_started.py b/tests/shard3/test_global_getting_started.py index 54c8911b9..05ea885dd 100644 --- a/tests/shard3/test_global_getting_started.py +++ b/tests/shard3/test_global_getting_started.py @@ -2,17 +2,11 @@ Test for file IO """ -import os import pickle import platform import re import shutil -import numpy as np -import numpy.testing as npt -import pytest -from casadi import sum1, sum2 - from bioptim import ( InterpolationType, OdeSolver, @@ -23,7 +17,12 @@ PhaseDynamics, SolutionMerge, ) -from tests.utils import TestUtils +from casadi import sum1, sum2 +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -57,7 +56,7 @@ def test_pendulum(ode_solver, use_sx, n_threads, phase_dynamics): if ode_solver == OdeSolver.RK8 and not use_sx: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_obj = ode_solver() @@ -252,7 +251,7 @@ def test_pendulum(ode_solver, use_sx, n_threads, phase_dynamics): def test_custom_constraint_track_markers(ode_solver, phase_dynamics): from bioptim.examples.getting_started import custom_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -314,7 +313,7 @@ def test_custom_constraint_track_markers(ode_solver, phase_dynamics): def test_initial_guesses(ode_solver, interpolation, random_init, phase_dynamics): from bioptim.examples.getting_started import custom_initial_guess as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver = ode_solver() @@ -387,7 +386,7 @@ def test_initial_guesses(ode_solver, interpolation, random_init, phase_dynamics) def test_cyclic_objective(ode_solver, phase_dynamics): from bioptim.examples.getting_started import example_cyclic_movement as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -441,7 +440,7 @@ def test_cyclic_objective(ode_solver, phase_dynamics): def test_cyclic_constraint(ode_solver, phase_dynamics): from bioptim.examples.getting_started import example_cyclic_movement as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -499,7 +498,7 @@ def test_phase_transitions(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -565,7 +564,7 @@ def test_parameter_optimization(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver in (OdeSolver.RK8, OdeSolver.COLLOCATION): return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -674,7 +673,7 @@ def test_custom_problem_type_and_dynamics(problem_type_custom, ode_solver, phase if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -732,7 +731,7 @@ def test_example_external_forces(ode_solver, phase_dynamics, n_threads, use_sx, if n_threads == 2 and phase_dynamics == PhaseDynamics.ONE_PER_NODE: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -846,7 +845,7 @@ def test_example_multiphase(ode_solver_type, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver_type in [OdeSolver.RK8, OdeSolver.COLLOCATION]: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver = ode_solver_type() ocp = ocp_module.prepare_ocp( @@ -922,7 +921,7 @@ def test_example_multiphase(ode_solver_type, phase_dynamics): def test_contact_forces_inequality_greater_than_constraint(ode_solver, phase_dynamics, expand_dynamics): from bioptim.examples.getting_started import example_inequality_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) min_bound = 50 @@ -994,7 +993,7 @@ def test_contact_forces_inequality_greater_than_constraint(ode_solver, phase_dyn def test_contact_forces_inequality_lesser_than_constraint(ode_solver): from bioptim.examples.getting_started import example_inequality_constraint as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) max_bound = 75 ocp = ocp_module.prepare_ocp( @@ -1047,7 +1046,7 @@ def test_contact_forces_inequality_lesser_than_constraint(ode_solver): def test_multinode_objective(ode_solver, phase_dynamics): from bioptim.examples.getting_started import example_multinode_objective as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver = ode_solver() @@ -1179,7 +1178,7 @@ def test_multinode_constraints_wrong_nodes(node): def test_multinode_constraints_too_much_constraints(ode_solver, too_much_constraints, phase_dynamics): from bioptim.examples.getting_started import example_multinode_constraints as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_obj = ode_solver ode_solver = ode_solver() @@ -1218,7 +1217,7 @@ def test_multinode_constraints(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -1265,7 +1264,7 @@ def test_multinode_constraints(ode_solver, phase_dynamics): def test_multistart(): from bioptim.examples.getting_started import example_multistart as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) bio_model_path = [bioptim_folder + "/models/pendulum.bioMod"] final_time = [1] n_shooting = [5, 10] @@ -1452,7 +1451,7 @@ def test_multistart(): def test_example_variable_scaling(phase_dynamics): from bioptim.examples.getting_started import example_variable_scaling as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", diff --git a/tests/shard3/test_global_symmetrical_torque_driven_ocp.py b/tests/shard3/test_global_symmetrical_torque_driven_ocp.py index ae7b07f30..cd3b19401 100644 --- a/tests/shard3/test_global_symmetrical_torque_driven_ocp.py +++ b/tests/shard3/test_global_symmetrical_torque_driven_ocp.py @@ -2,14 +2,12 @@ Test for file IO """ -import os - import pytest import numpy as np import numpy.testing as npt from bioptim import OdeSolver, PhaseDynamics, SolutionMerge -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -21,7 +19,7 @@ def test_symmetry_by_mapping(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cubeSym.bioMod", @@ -73,7 +71,7 @@ def test_symmetry_by_constraint(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cubeSym.bioMod", diff --git a/tests/shard3/test_global_torque_driven_ocp.py b/tests/shard3/test_global_torque_driven_ocp.py index 759345ec1..7e74f26ed 100644 --- a/tests/shard3/test_global_torque_driven_ocp.py +++ b/tests/shard3/test_global_torque_driven_ocp.py @@ -2,10 +2,7 @@ Test for file IO """ -import numpy as np -import numpy.testing as npt -import os -import pytest +import platform from bioptim import ( OdeSolver, @@ -19,7 +16,11 @@ SolutionMerge, ) from bioptim.models.biorbd.viewer_utils import _prepare_tracked_markers_for_animation -from tests.utils import TestUtils +import numpy.testing as npt +import numpy as np +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -33,7 +34,7 @@ def test_track_markers(ode_solver, actuator_type, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -88,7 +89,7 @@ def test_track_markers_changing_constraints(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -186,7 +187,7 @@ def test_track_markers_with_actuators(ode_solver, phase_dynamics): if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: return - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -229,16 +230,16 @@ def test_track_markers_with_actuators(ode_solver, phase_dynamics): @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]) +@pytest.mark.parametrize("ode_solver", [OdeSolver.IRK, OdeSolver.COLLOCATION]) def test_track_marker_2D_pendulum(ode_solver, phase_dynamics): # Load muscle_activations_contact_tracker from bioptim.examples.torque_driven_ocp import track_markers_2D_pendulum as ocp_module # For reducing time phase_dynamics == PhaseDynamics.ONE_PER_NODE is skipped for redundant tests - if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.RK8: - return + if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: + pytest.skip("Redundant test") - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver_orig = ode_solver ode_solver = ode_solver() @@ -268,101 +269,28 @@ def test_track_marker_2D_pendulum(ode_solver, phase_dynamics): phase_dynamics=phase_dynamics, expand_dynamics=ode_solver_orig != OdeSolver.IRK, ) - sol = ocp.solve() - - # Check constraints - g = np.array(sol.constraints) - npt.assert_equal(g.shape, (n_shooting * 4, 1)) - npt.assert_almost_equal(g, np.zeros((n_shooting * 4, 1))) - - # Check some of the results - states = sol.decision_states(to_merge=SolutionMerge.NODES) - controls = sol.decision_controls(to_merge=SolutionMerge.NODES) - q, qdot, tau = states["q"], states["qdot"], controls["tau"] - - if isinstance(ode_solver, OdeSolver.IRK): - # Check objective function value - f = np.array(sol.cost) - npt.assert_equal(f.shape, (1, 1)) - npt.assert_almost_equal(f[0, 0], 290.6751231) - - # initial and final position - npt.assert_almost_equal(q[:, 0], np.array((0, 0))) - npt.assert_almost_equal(q[:, -1], np.array((0.64142484, 2.85371719))) - - # initial and final velocities - npt.assert_almost_equal(qdot[:, 0], np.array((0, 0))) - npt.assert_almost_equal(qdot[:, -1], np.array((3.46921861, 3.24168308))) - - # initial and final controls - npt.assert_almost_equal(tau[:, 0], np.array((9.11770196, -13.83677175))) - npt.assert_almost_equal(tau[:, -1], np.array((1.16836132, 4.77230548))) - - elif isinstance(ode_solver, OdeSolver.RK8): - pass - - else: - # Check objective function value - f = np.array(sol.cost) - npt.assert_equal(f.shape, (1, 1)) - npt.assert_almost_equal(f[0, 0], 281.8560713312711) - - # initial and final position - npt.assert_almost_equal(q[:, 0], np.array((0, 0))) - npt.assert_almost_equal(q[:, -1], np.array((0.8367364, 3.37533055))) - - # initial and final velocities - npt.assert_almost_equal(qdot[:, 0], np.array((0, 0))) - npt.assert_almost_equal(qdot[:, -1], np.array((3.2688391, 3.88242643))) - - # initial and final controls - npt.assert_almost_equal(tau[:, 0], np.array((6.93890241, -12.76433504))) - npt.assert_almost_equal(tau[:, -1], np.array((0.13156876, 0.93749913))) - - # simulate - TestUtils.simulate(sol) - - -@pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) -@pytest.mark.parametrize("ode_solver", [OdeSolver.IRK, OdeSolver.COLLOCATION]) -@pytest.mark.parametrize("defects_type", [DefectType.EXPLICIT, DefectType.IMPLICIT]) -def test_track_marker_2D_pendulum(ode_solver, defects_type, phase_dynamics): - # Load muscle_activations_contact_tracker - from bioptim.examples.torque_driven_ocp import track_markers_2D_pendulum as ocp_module - - # For reducing time phase_dynamics == PhaseDynamics.ONE_PER_NODE is skipped for redundant tests - if phase_dynamics == PhaseDynamics.ONE_PER_NODE and ode_solver == OdeSolver.COLLOCATION: - return - bioptim_folder = os.path.dirname(ocp_module.__file__) - - ode_solver_orig = ode_solver - ode_solver = ode_solver() - - # Define the problem - model_path = bioptim_folder + "/models/pendulum.bioMod" - bio_model = BiorbdModel(model_path) - - final_time = 2 - n_shooting = 30 - - # Generate data to fit + # Check the values which will be sent to the solver np.random.seed(42) - markers_ref = np.random.rand(3, 2, n_shooting + 1) - tau_ref = np.random.rand(2, n_shooting) - - if isinstance(ode_solver, OdeSolver.IRK): - tau_ref = tau_ref * 5 - - ocp = ocp_module.prepare_ocp( - bio_model, - final_time, - n_shooting, - markers_ref, - tau_ref, - ode_solver=ode_solver, - expand_dynamics=ode_solver_orig != OdeSolver.IRK, + match ode_solver_orig: + case OdeSolver.COLLOCATION: + v_len = 665 + expected = [329.58704584455836, 45.86799945455372, 32.40020240692716] + case OdeSolver.IRK: + v_len = 185 + expected = [87.49523141142917, 194.20847154483175, 4027.416142481593] + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(v_len, 1), + expected_v_f_g=expected, + decimal=6, ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check constraints @@ -444,7 +372,7 @@ def test_trampo_quaternions(phase_dynamics): # Load trampo_quaternion from bioptim.examples.torque_driven_ocp import trampo_quaternions as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem model_path = bioptim_folder + "/models/trunk_and_2arm_quaternion.bioMod" @@ -554,7 +482,7 @@ def test_phase_transition_uneven_variable_number_by_bounds(phase_dynamics): # Load phase_transition_uneven_variable_number_by_bounds from bioptim.examples.torque_driven_ocp import phase_transition_uneven_variable_number_by_bounds as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem biorbd_model_path_with_translations = bioptim_folder + "/models/double_pendulum_with_translations.bioMod" @@ -585,7 +513,7 @@ def test_phase_transition_uneven_variable_number_by_mapping(phase_dynamics): # Load phase_transition_uneven_variable_number_by_mapping from bioptim.examples.torque_driven_ocp import phase_transition_uneven_variable_number_by_mapping as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem biorbd_model_path = bioptim_folder + "/models/double_pendulum.bioMod" @@ -642,7 +570,7 @@ def test_torque_activation_driven(ode_solver, phase_dynamics): # Load track_markers from bioptim.examples.torque_driven_ocp import torque_activation_driven as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_2dof_2contacts.bioMod", @@ -688,7 +616,7 @@ def test_example_multi_biorbd_model(phase_dynamics): # Load example_multi_biorbd_model from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = bioptim_folder + "/models/triple_pendulum.bioMod" biorbd_model_path_modified_inertia = bioptim_folder + "/models/triple_pendulum_modified_inertia.bioMod" @@ -699,49 +627,20 @@ def test_example_multi_biorbd_model(phase_dynamics): phase_dynamics=phase_dynamics, expand_dynamics=True, ) - sol = ocp.solve() - # # Check objective function value - # f = np.array(sol.cost) - # npt.assert_equal(f.shape, (1, 1)) - # npt.assert_almost_equal(f[0, 0], 10.697019532108447) - - # # Check constraints - # g = np.array(sol.constraints) - # npt.assert_equal(g.shape, (240, 1)) - # npt.assert_almost_equal(g, np.zeros((240, 1)), decimal=6) - - # # Check some of the results - # states = sol.decision_states(to_merge=SolutionMerge.NODES) - # controls = sol.decision_controls(to_merge=SolutionMerge.NODES) - - # # initial and final position - # npt.assert_almost_equal( - # states["q"][:, 0], np.array([-3.14159265, 0.0, 0.0, -3.14159265, 0.0, 0.0]), decimal=6 - # ) - # npt.assert_almost_equal( - # states["q"][:, -1], np.array([3.05279505, 0.0, 0.0, 3.04159266, 0.0, 0.0]), decimal=6 - # ) - # # initial and final velocities - # npt.assert_almost_equal( - # states["qdot"][:, 0], - # np.array([15.68385811, -31.25068304, 19.2317873, 15.63939216, -31.4159265, 19.91541457]), - # decimal=6, - # ) - # npt.assert_almost_equal( - # states["qdot"][:, -1], - # np.array([15.90689541, -30.54499528, 16.03701393, 15.96682325, -30.89799758, 16.70457477]), - # decimal=6, - # ) - # # initial and final controls - # npt.assert_almost_equal(controls["tau"][:, 0], np.array([-0.48437131, 0.0249894, 0.38051993]), decimal=6) - # npt.assert_almost_equal(controls["tau"][:, -1], np.array([-0.00235227, -0.02192184, -0.00709896]), decimal=6) + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(313, 1), + expected_v_f_g=[154.4724783298145, 21.353112388854846, 60.207690783556835], + decimal=6, + ) def test_example_minimize_segment_velocity(): from bioptim.examples.torque_driven_ocp import example_minimize_segment_velocity as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem @@ -752,6 +651,18 @@ def test_example_minimize_segment_velocity(): n_shooting=5, expand_dynamics=True, ) + + # Check the values which will be sent to the solver + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(52, 1), + expected_v_f_g=[24.04091267073873, 805.0958650566107, 7.321219099616136], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check objective function value 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 486d82a73..e4c9c44a7 100644 --- a/tests/shard3/test_global_torque_driven_with_contact_ocp.py +++ b/tests/shard3/test_global_torque_driven_with_contact_ocp.py @@ -6,14 +6,12 @@ - the non_slipping constraint """ -import os -import pytest - +from bioptim import OdeSolver, Solver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, Solver, PhaseDynamics, SolutionMerge +import pytest -from tests.utils import TestUtils +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -23,7 +21,7 @@ def test_maximize_predicted_height_CoM(objective_name, phase_dynamics): from bioptim.examples.torque_driven_ocp import maximize_predicted_height_CoM as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_4dof_2contacts.bioMod", @@ -97,7 +95,7 @@ def test_maximize_predicted_height_CoM(objective_name, phase_dynamics): def test_maximize_predicted_height_CoM_with_actuators(phase_dynamics): from bioptim.examples.torque_driven_ocp import maximize_predicted_height_CoM as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/2segments_4dof_2contacts.bioMod", @@ -143,7 +141,7 @@ def test_maximize_predicted_height_CoM_with_actuators(phase_dynamics): def test_maximize_predicted_height_CoM_rigidbody_dynamics(phase_dynamics): from bioptim.examples.torque_driven_ocp import maximize_predicted_height_CoM as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver = OdeSolver.RK4() diff --git a/tests/shard3/test_initial_condition.py b/tests/shard3/test_initial_condition.py index b48049152..f67dbc517 100644 --- a/tests/shard3/test_initial_condition.py +++ b/tests/shard3/test_initial_condition.py @@ -1,8 +1,3 @@ -import os -import pytest - -import numpy as np -import numpy.testing as npt from bioptim import ( InterpolationType, Solution, @@ -19,6 +14,11 @@ SolutionMerge, ) from bioptim.limits.path_conditions import InitialGuess +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils # TODO: Add negative test for sizes @@ -141,7 +141,7 @@ def test_initial_guess_update(phase_dynamics): # Load pendulum from bioptim.examples.optimal_time_ocp import pendulum_min_time_Mayer as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -207,7 +207,7 @@ def custom_bound_func(current_shooting, val, total_shooting): def test_simulate_from_initial_multiple_shoot(phase_dynamics): from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) final_time = 2 n_shooting = 10 @@ -255,7 +255,7 @@ def test_simulate_from_initial_single_shoot(phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) final_time = 2 n_shooting = 10 @@ -305,7 +305,7 @@ def test_initial_guess_error_messages(phase_dynamics): """ from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = bioptim_folder + "/models/pendulum.bioMod" # Add objective functions diff --git a/tests/shard3/test_multiphase_noised_initial_guess.py b/tests/shard3/test_multiphase_noised_initial_guess.py index 6e5b2bd0c..022474da4 100644 --- a/tests/shard3/test_multiphase_noised_initial_guess.py +++ b/tests/shard3/test_multiphase_noised_initial_guess.py @@ -1,15 +1,16 @@ -import pytest -import os +from bioptim import BiorbdModel, BoundsList, InitialGuessList, MagnitudeType, PhaseDynamics import numpy as np import numpy.testing as npt -from bioptim import BiorbdModel, BoundsList, InitialGuessList, MagnitudeType, PhaseDynamics +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_noisy_multiphase(phase_dynamics): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=phase_dynamics, @@ -738,7 +739,7 @@ def test_noisy_multiphase(phase_dynamics): def test_add_wrong_magnitude(magnitude, raised_str, phase_dynamics): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=phase_dynamics, @@ -788,7 +789,7 @@ def test_add_wrong_magnitude(magnitude, raised_str, phase_dynamics): def test_add_wrong_bound_push(bound_push, raised_str): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=PhaseDynamics.SHARED_DURING_THE_PHASE, @@ -835,7 +836,7 @@ def test_add_wrong_bound_push(bound_push, raised_str): def test_add_wrong_seed(seed, raised_str): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=PhaseDynamics.SHARED_DURING_THE_PHASE, @@ -875,7 +876,7 @@ def test_add_wrong_seed(seed, raised_str): def test_add_wrong_bounds(): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=PhaseDynamics.SHARED_DURING_THE_PHASE, @@ -925,7 +926,7 @@ def test_add_wrong_bounds(): def test_add_wrong_n_shooting(): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", phase_dynamics=PhaseDynamics.SHARED_DURING_THE_PHASE, diff --git a/tests/shard3/test_passive_torque.py b/tests/shard3/test_passive_torque.py index c606c957d..72e01d84d 100644 --- a/tests/shard3/test_passive_torque.py +++ b/tests/shard3/test_passive_torque.py @@ -1,10 +1,3 @@ -import os - -import numpy as np -import numpy.testing as npt -import pytest -from casadi import MX, SX - from bioptim import ( ConfigureProblem, ControlType, @@ -20,7 +13,12 @@ SolutionMerge, ParameterContainer, ) -from tests.utils import TestUtils +from casadi import MX, SX +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils class OptimalControlProgram: @@ -401,7 +399,7 @@ def test_muscle_driven_with_passive_torque(with_passive_torque, cx, phase_dynami def test_pendulum_passive_torque(with_passive_torque, phase_dynamics): from bioptim.examples.torque_driven_ocp import pendulum_with_passive_torque as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # Define the problem biorbd_model_path = bioptim_folder + "/models/pendulum_with_passive_torque.bioMod" diff --git a/tests/shard4/test_plot_show_bounds.py b/tests/shard4/test_plot_show_bounds.py index 10c86b00b..53f4f1f5f 100644 --- a/tests/shard4/test_plot_show_bounds.py +++ b/tests/shard4/test_plot_show_bounds.py @@ -1,8 +1,8 @@ -import os - from bioptim import PhaseDynamics from bioptim.limits.path_conditions import Bounds +from ..utils import TestUtils + def test_pendulum_show_bounds(): """ @@ -11,7 +11,7 @@ def test_pendulum_show_bounds(): """ from bioptim.examples.sqp_method import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", diff --git a/tests/shard4/test_simulate.py b/tests/shard4/test_simulate.py index 1f736d367..13a6ebf86 100644 --- a/tests/shard4/test_simulate.py +++ b/tests/shard4/test_simulate.py @@ -1,13 +1,14 @@ from sys import platform +from bioptim import Shooting, OdeSolver, SolutionIntegrator, Solver, PhaseDynamics, SolutionMerge, ControlType +from bioptim.models.biorbd.viewer_utils import _check_models_comes_from_same_super_class import numpy as np import numpy.testing as npt import os import pytest import warnings -from bioptim import Shooting, OdeSolver, SolutionIntegrator, Solver, PhaseDynamics, SolutionMerge, ControlType -from bioptim.models.biorbd.viewer_utils import _check_models_comes_from_same_super_class +from ..utils import TestUtils @pytest.mark.parametrize("scaled", [True, False]) @@ -15,7 +16,7 @@ def test_merge_combinations(scaled): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", final_time=2, n_shooting=10 ) @@ -60,7 +61,7 @@ def test_merge_phases_one_phase(phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -87,7 +88,7 @@ def test_merge_phases_multi_phase(phase_dynamics): # Load pendulum from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -113,7 +114,7 @@ def test_interpolate(phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 @@ -157,7 +158,7 @@ def test_interpolate_multiphases(ode_solver, phase_dynamics): # Load pendulum from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -196,7 +197,7 @@ def test_interpolate_multiphases_merge_phase(phase_dynamics): # Load pendulum from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -230,7 +231,7 @@ def test_integrate(integrator, ode_solver, phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 30 if integrator == SolutionIntegrator.SCIPY_RK45 else 10 @@ -281,7 +282,7 @@ def test_integrate_single_shoot(ode_solver, phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 @@ -337,7 +338,7 @@ def test_integrate_single_shoot_use_scipy(ode_solver, phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 @@ -479,7 +480,7 @@ def test_integrate_all_cases(shooting, merge, integrator, ode_solver, phase_dyna # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) n_shooting = 10 if integrator == SolutionIntegrator.OCP else 30 @@ -552,7 +553,7 @@ def test_integrate_multiphase(shooting, integrator, ode_solver, phase_dynamics, # Load pendulum from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -637,7 +638,7 @@ def test_integrate_multiphase(shooting, integrator, ode_solver, phase_dynamics, def test_check_models_comes_from_same_super_class(): from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", @@ -675,7 +676,7 @@ def test_integrate_multiphase_merged(shooting, integrator, ode_solver, phase_dyn # Load pendulum from bioptim.examples.getting_started import example_multiphase as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/cube.bioMod", diff --git a/tests/shard4/test_soft_contacts.py b/tests/shard4/test_soft_contacts.py index d56d18dbf..92da13a8b 100644 --- a/tests/shard4/test_soft_contacts.py +++ b/tests/shard4/test_soft_contacts.py @@ -1,16 +1,16 @@ -import os - +from bioptim import OdeSolver, PhaseDynamics, SolutionMerge import numpy as np import numpy.testing as npt -from bioptim import OdeSolver, PhaseDynamics, SolutionMerge import pytest +from ..utils import TestUtils + @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) def test_soft_contact(phase_dynamics): from bioptim.examples.torque_driven_ocp import example_soft_contact as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ode_solver = OdeSolver.RK8() diff --git a/tests/shard4/test_solution.py b/tests/shard4/test_solution.py index b618861d2..a86985a50 100644 --- a/tests/shard4/test_solution.py +++ b/tests/shard4/test_solution.py @@ -1,9 +1,8 @@ -import os - -import pytest -import numpy as np -import numpy.testing as npt from bioptim import Shooting, OdeSolver, SolutionIntegrator, Solver, ControlType, PhaseDynamics, SolutionMerge +import numpy.testing as npt +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("phase_dynamics", [PhaseDynamics.SHARED_DURING_THE_PHASE, PhaseDynamics.ONE_PER_NODE]) @@ -12,7 +11,7 @@ def test_time(ode_solver, phase_dynamics): # Load pendulum from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", @@ -47,7 +46,7 @@ def test_time_multiphase(ode_solver, phase_dynamics, continuous): # Load slider from bioptim.examples.torque_driven_ocp import slider as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/slider.bioMod", @@ -125,7 +124,7 @@ def test_generate_stepwise_time(ode_solver, merge_phase, phase_dynamics, continu # Load slider from bioptim.examples.torque_driven_ocp import slider as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/slider.bioMod", @@ -221,7 +220,7 @@ def test_generate_decision_time(ode_solver, merge_phase, phase_dynamics, continu # Load slider from bioptim.examples.torque_driven_ocp import slider as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/slider.bioMod", @@ -319,7 +318,7 @@ def test_generate_integrate(ode_solver, merge_phase, shooting_type, integrator, # Load slider from bioptim.examples.torque_driven_ocp import slider as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if ode_solver == OdeSolver.COLLOCATION and control_type == ControlType.LINEAR_CONTINUOUS: with pytest.raises( diff --git a/tests/shard4/test_variational_biorbd_model.py b/tests/shard4/test_variational_biorbd_model.py index 2a832ab70..0dbb9db6f 100644 --- a/tests/shard4/test_variational_biorbd_model.py +++ b/tests/shard4/test_variational_biorbd_model.py @@ -1,12 +1,10 @@ -import os -from casadi import MX, Function - from bioptim import ( ControlType, HolonomicConstraintsFcn, QuadratureRule, VariationalBiorbdModel, ) +from casadi import MX, Function from tests.utils import TestUtils @@ -16,7 +14,7 @@ def test_variational_model(): example_variational_integrator_pendulum as ocp_module, ) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) biorbd_model_path = bioptim_folder + "/models/pendulum.bioMod" model = VariationalBiorbdModel(biorbd_model_path) diff --git a/tests/shard4/test_variational_integrator_examples.py b/tests/shard4/test_variational_integrator_examples.py index 593c81db4..32d0a487c 100644 --- a/tests/shard4/test_variational_integrator_examples.py +++ b/tests/shard4/test_variational_integrator_examples.py @@ -2,11 +2,13 @@ Tests of the examples of the variational integrator. """ -import numpy as np -import numpy.testing as npt import os -import pytest + +import numpy.testing as npt from bioptim import Solver, SolutionMerge +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("use_sx", [False, True]) @@ -14,7 +16,7 @@ def test_variational_pendulum(use_sx): """Test the variational integrator pendulum example""" from bioptim.examples.discrete_mechanics_and_optimal_control import example_variational_integrator_pendulum - bioptim_folder = os.path.dirname(example_variational_integrator_pendulum.__file__) + bioptim_folder = TestUtils.module_folder(example_variational_integrator_pendulum) # --- Prepare the ocp --- # ocp = example_variational_integrator_pendulum.prepare_ocp( @@ -48,7 +50,7 @@ def test_variational_pendulum_with_holonomic_constraints(use_sx): example_variational_integrator_with_holonomic_constraints_pendulum, ) - bioptim_folder = os.path.dirname(example_variational_integrator_with_holonomic_constraints_pendulum.__file__) + bioptim_folder = TestUtils.module_folder(example_variational_integrator_with_holonomic_constraints_pendulum) # --- Prepare the ocp --- # ocp = example_variational_integrator_with_holonomic_constraints_pendulum.prepare_ocp( diff --git a/tests/shard5/test_global_stochastic_collocation.py b/tests/shard5/test_global_stochastic_collocation.py index 2f2f890d2..9ed6fcf73 100644 --- a/tests/shard5/test_global_stochastic_collocation.py +++ b/tests/shard5/test_global_stochastic_collocation.py @@ -1,10 +1,12 @@ -import os -import pytest +import platform +from bioptim import Solver, SocpType, SolutionMerge, PenaltyHelpers, SolutionIntegrator +from casadi import DM, vertcat import numpy as np import numpy.testing as npt -from casadi import DM, vertcat -from bioptim import Solver, SocpType, SolutionMerge, PenaltyHelpers, SolutionIntegrator +import pytest + +from ..utils import TestUtils @pytest.mark.parametrize("use_sx", [False, True]) @@ -24,7 +26,7 @@ def test_arm_reaching_torque_driven_collocations(use_sx: bool): wPqdot_magnitude = DM(np.array([wPqdot_std**2 / dt, wPqdot_std**2 / dt])) sensory_noise_magnitude = vertcat(wPq_magnitude, wPqdot_magnitude) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_socp( biorbd_model_path=bioptim_folder + "/models/LeuvenArmModel.bioMod", @@ -425,6 +427,18 @@ def test_obstacle_avoidance_direct_collocation(use_sx: bool): # Solver parameters solver = Solver.IPOPT() solver.set_maximum_iterations(4) + + # Check the values which will be sent to the solver + np.random.seed(42) + TestUtils.compare_ocp_to_solve( + ocp, + v=np.ones([1107, 1]), # Random values here returns nan for g + expected_v_f_g=[1107.0, 10.01, -170696.19805582374], + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve(solver) # Check objective function value diff --git a/tests/shard5/test_plot_server.py b/tests/shard5/test_plot_server.py index 442726ad5..37d9d8d7f 100644 --- a/tests/shard5/test_plot_server.py +++ b/tests/shard5/test_plot_server.py @@ -1,5 +1,3 @@ -import os - from bioptim.gui.online_callback_server import _serialize_xydata, _deserialize_xydata from bioptim.gui.plot import PlotOcp from bioptim.gui.online_callback_server import _ResponseHeader @@ -7,12 +5,14 @@ from casadi import DM import numpy as np +from ..utils import TestUtils + def test_serialize_deserialize(): # Prepare a set of data to serialize and deserialize from bioptim.examples.getting_started import pendulum as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_ocp( biorbd_model_path=bioptim_folder + "/models/pendulum.bioMod", diff --git a/tests/shard6/test_dt_dependent.py b/tests/shard6/test_dt_dependent.py index a9b635b4c..daed7a46d 100644 --- a/tests/shard6/test_dt_dependent.py +++ b/tests/shard6/test_dt_dependent.py @@ -6,12 +6,6 @@ Note that the final node is not tracked. """ -import os -import pytest -from casadi import MX, SX, vertcat, sin, Function, DM -import numpy as np -import numpy.testing as npt - from bioptim import ( BiorbdModel, BoundsList, @@ -29,6 +23,12 @@ PhaseDynamics, SolutionMerge, ) +from casadi import MX, SX, vertcat, sin, Function, DM +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils def custom_configure( @@ -53,12 +53,10 @@ def dynamics( q = DynamicsFunctions.get(nlp.states["q"], states) qdot = DynamicsFunctions.get(nlp.states["qdot"], states) - tau = DynamicsFunctions.get(nlp.controls["tau"], controls) * ( - sin(nlp.tf_mx - time) * time.ones(nlp.model.nb_tau) * 10 - ) + tau = DynamicsFunctions.get(nlp.controls["tau"], controls) * (sin(nlp.tf - time) * time.ones(nlp.model.nb_tau) * 10) dq = DynamicsFunctions.compute_qdot(nlp, q, qdot) - ddq = nlp.model.forward_dynamics(q, qdot, tau) + ddq = nlp.model.forward_dynamics()(q, qdot, tau, [], parameters) return DynamicsEvaluation(dxdt=vertcat(dq, ddq), defects=None) @@ -164,7 +162,7 @@ def test_dt_dependent_problem(minimize_time, use_sx): from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) # --- Solve the program --- # ocp = prepare_ocp_state_as_time( @@ -216,8 +214,8 @@ def test_dt_dependent_problem(minimize_time, use_sx): tau_dyn = controls_sym * (sin(tf_sym - time_sym) * MX.ones(ocp.nlp[0].model.nb_tau) * 10) out_dyn = vertcat( states_sym[ocp.nlp[0].model.nb_q :], - ocp.nlp[0].model.forward_dynamics( - states_sym[: ocp.nlp[0].model.nb_q], states_sym[ocp.nlp[0].model.nb_q :], tau_dyn + ocp.nlp[0].model.forward_dynamics()( + states_sym[: ocp.nlp[0].model.nb_q], states_sym[ocp.nlp[0].model.nb_q :], tau_dyn, [], [] ), ) diff --git a/tests/shard6/test_global_stochastic_except_collocation.py b/tests/shard6/test_global_stochastic_except_collocation.py index a4cffa67d..30eb8841c 100644 --- a/tests/shard6/test_global_stochastic_except_collocation.py +++ b/tests/shard6/test_global_stochastic_except_collocation.py @@ -1,12 +1,13 @@ -import os +import platform +from bioptim import Solver, SolutionMerge, SolutionIntegrator +from bioptim.examples.stochastic_optimal_control.arm_reaching_torque_driven_implicit import ExampleType +from casadi import DM, vertcat import numpy as np import numpy.testing as npt import pytest -from casadi import DM, vertcat -from bioptim import Solver, SolutionMerge, SolutionIntegrator -from bioptim.examples.stochastic_optimal_control.arm_reaching_torque_driven_implicit import ExampleType +from ..utils import TestUtils # Integrated values should be handled another way @@ -254,7 +255,7 @@ # wPqdot_magnitude = DM(np.array([wPqdot_std**2 / dt, wPqdot_std**2 / dt])) # sensory_noise_magnitude = vertcat(wPq_magnitude, wPqdot_magnitude) # -# bioptim_folder = os.path.dirname(ocp_module.__file__) +# bioptim_folder = TestUtils.module_folder(ocp_module) # # if use_sx: # with pytest.raises( @@ -392,20 +393,13 @@ # ) -@pytest.mark.parametrize("with_cholesky", [True, False]) @pytest.mark.parametrize("with_scaling", [True, False]) @pytest.mark.parametrize("use_sx", [True, False]) -def test_arm_reaching_torque_driven_implicit(with_cholesky, with_scaling, use_sx): +def test_arm_reaching_torque_driven_implicit(with_scaling, use_sx): from bioptim.examples.stochastic_optimal_control import arm_reaching_torque_driven_implicit as ocp_module - if with_cholesky and not with_scaling: - return - if not with_cholesky and not with_scaling and not use_sx: - return - if with_cholesky and with_scaling and use_sx: - return - if with_cholesky and with_scaling and not use_sx: - return + if not with_scaling and not use_sx: + pytest.skip("Redundant test") final_time = 0.8 n_shooting = 4 @@ -420,7 +414,7 @@ def test_arm_reaching_torque_driven_implicit(with_cholesky, with_scaling, use_sx wPqdot_magnitude = DM(np.array([wPqdot_std**2 / dt, wPqdot_std**2 / dt])) sensory_noise_magnitude = vertcat(wPq_magnitude, wPqdot_magnitude) - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) ocp = ocp_module.prepare_socp( biorbd_model_path=bioptim_folder + "/models/LeuvenArmModel.bioMod", @@ -429,7 +423,7 @@ def test_arm_reaching_torque_driven_implicit(with_cholesky, with_scaling, use_sx hand_final_position=hand_final_position, motor_noise_magnitude=motor_noise_magnitude, sensory_noise_magnitude=sensory_noise_magnitude, - with_cholesky=with_cholesky, + with_cholesky=False, with_scaling=with_scaling, use_sx=use_sx, ) @@ -439,6 +433,23 @@ def test_arm_reaching_torque_driven_implicit(with_cholesky, with_scaling, use_sx solver.set_maximum_iterations(4) solver.set_nlp_scaling_method("none") + # Check the values which will be sent to the solver + np.random.seed(42) + expected = ( + [226.2655556486931, 36332536424.841034, 3600.041860608674] + if with_scaling + else [226.2655556486931, 754.7304909444006, 466.9111405169811] + ) + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(457, 1), + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve(solver) # Check objective @@ -457,414 +468,150 @@ def test_arm_reaching_torque_driven_implicit(with_cholesky, with_scaling, use_sx q, qdot = states["q"], states["qdot"] tau = controls["tau"] - if not with_cholesky: - # Check some of the results - k, ref, m, cov, a, c = ( - algebraic_states["k"], - algebraic_states["ref"], - algebraic_states["m"], - algebraic_states["cov"], - algebraic_states["a"], - algebraic_states["c"], + # Check some of the results + k, ref, m, cov, a, c = ( + algebraic_states["k"], + algebraic_states["ref"], + algebraic_states["m"], + algebraic_states["cov"], + algebraic_states["a"], + algebraic_states["c"], + ) + if not with_scaling: + # Check objective function value + npt.assert_almost_equal(f[0, 0], 54.83542936544702) + + # detailed cost values + npt.assert_almost_equal(sol.detailed_cost[0]["cost_value_weighted"], 54.377501174311654) + npt.assert_almost_equal(sol.detailed_cost[1]["cost_value_weighted"], 0.4579281911353716) + npt.assert_almost_equal( + f[0, 0], sum(sol.detailed_cost[i]["cost_value_weighted"] for i in range(len(sol.detailed_cost))) ) - if not with_scaling: - # Check objective function value - npt.assert_almost_equal(f[0, 0], 54.83542936544702) - - # detailed cost values - npt.assert_almost_equal(sol.detailed_cost[0]["cost_value_weighted"], 54.377501174311654) - npt.assert_almost_equal(sol.detailed_cost[1]["cost_value_weighted"], 0.4579281911353716) - npt.assert_almost_equal( - f[0, 0], sum(sol.detailed_cost[i]["cost_value_weighted"] for i in range(len(sol.detailed_cost))) - ) - - # initial and final position - npt.assert_almost_equal(q[:, 0], np.array([0.34906585, 2.24586773])) - npt.assert_almost_equal(q[:, -1], np.array([0.9256103, 1.29037205])) - npt.assert_almost_equal(qdot[:, 0], np.array([0, 0])) - npt.assert_almost_equal(qdot[:, -1], np.array([0, 0])) - - npt.assert_almost_equal(tau[:, 0], np.array([0.4128368, -0.3015095])) - npt.assert_almost_equal(tau[:, -2], np.array([-0.3927315, 0.2511875])) - npt.assert_almost_equal( - k[:, 0], - np.array([-0.0813293, 0.2754021, 0.154386, -0.4924746, 0.0439148, -0.4026935, -0.0874262, 0.1938167]), - ) - npt.assert_almost_equal(ref[:, 0], np.array([2.81907786e-02, 2.84412560e-01, 0, 0])) - npt.assert_almost_equal( - m[:, 0], - np.array( - [ - 1.1112046e00, - -1.5191998e-04, - -8.4126212e-03, - 1.3675755e-02, - 3.8338798e-04, - 1.1107784e00, - -3.4509900e-02, - 2.9958236e-02, - -1.2298549e-02, - -4.7443854e-04, - 1.1068711e00, - 4.2697265e-02, - -5.4747221e-05, - -1.2329282e-02, - 4.9283932e-03, - 1.1096335e00, - ] - ), - ) + # initial and final position + npt.assert_almost_equal(q[:, 0], np.array([0.34906585, 2.24586773])) + npt.assert_almost_equal(q[:, -1], np.array([0.9256103, 1.29037205])) + npt.assert_almost_equal(qdot[:, 0], np.array([0, 0])) + npt.assert_almost_equal(qdot[:, -1], np.array([0, 0])) - npt.assert_almost_equal( - cov[:, -2], - np.array( - [ - -2.1576659e-05, - 4.5807147e-05, - -2.3400401e-04, - 4.9389676e-04, - 4.5807147e-05, - -8.9858345e-05, - 4.0996883e-04, - -8.8392478e-04, - -2.3400401e-04, - 4.0996883e-04, - -4.2538567e-03, - 8.8954301e-03, - 4.9389676e-04, - -8.8392478e-04, - 8.8954301e-03, - -1.9118596e-02, - ] - ), - ) + npt.assert_almost_equal(tau[:, 0], np.array([0.4128368, -0.3015095])) + npt.assert_almost_equal(tau[:, -2], np.array([-0.3927315, 0.2511875])) - npt.assert_almost_equal( - a[:, 3], - np.array( - [ - 1.0000000e00, - -1.6381309e-31, - -2.7975098e-01, - 5.7472138e-01, - 9.5108043e-32, - 1.0000000e00, - -1.0146108e00, - 2.6772428e00, - -1.0000000e-01, - 3.0022057e-31, - 6.1784980e-01, - 1.1194146e00, - 1.9609916e-31, - -1.0000000e-01, - -4.0440906e-02, - 1.1627225e00, - ] - ), - ) - np.set_printoptions(threshold=30) - print(c[:, 2]) - npt.assert_almost_equal( - c[:, 2], - np.array( - [ - 3.74755747e-37, - -1.65536315e-35, - -1.35553765e00, - 1.12565454e00, - -3.76558849e-36, - 1.05336311e-35, - 1.12565454e00, - -3.79193715e00, - 4.51852858e-35, - -1.92552034e-34, - 9.02390584e-02, - -2.29668590e-01, - 2.42659035e-34, - -6.69404844e-34, - 7.59635648e-01, - -2.23881857e00, - 2.27969529e-31, - -1.50500085e-32, - 4.54215287e-01, - -7.57727880e-01, - -1.84502320e-32, - -1.03285044e-32, - -2.26170260e-01, - 5.81482655e-01, - ] - ), - ) - - np.random.seed(42) - integrated_states = sol.noisy_integrate( - integrator=SolutionIntegrator.SCIPY_RK45, to_merge=SolutionMerge.NODES - ) - integrated_stated_covariance = np.cov(integrated_states["q"][:, -1, :]) - npt.assert_almost_equal( - integrated_stated_covariance, - np.array([[0.205039, -0.411671], [-0.411671, 0.971819]]), - decimal=6, - ) - else: - # Check some of the results - k, ref, m, cov, a, c = ( - algebraic_states["k"], - algebraic_states["ref"], - algebraic_states["m"], - algebraic_states["cholesky_cov"], - algebraic_states["a"], - algebraic_states["c"], + npt.assert_almost_equal( + k[:, 0], + np.array([-0.0813293, 0.2754021, 0.154386, -0.4924746, 0.0439148, -0.4026935, -0.0874262, 0.1938167]), + ) + npt.assert_almost_equal(ref[:, 0], np.array([2.81907786e-02, 2.84412560e-01, 0, 0])) + npt.assert_almost_equal( + m[:, 0], + np.array( + [ + 1.1112046e00, + -1.5191998e-04, + -8.4126212e-03, + 1.3675755e-02, + 3.8338798e-04, + 1.1107784e00, + -3.4509900e-02, + 2.9958236e-02, + -1.2298549e-02, + -4.7443854e-04, + 1.1068711e00, + 4.2697265e-02, + -5.4747221e-05, + -1.2329282e-02, + 4.9283932e-03, + 1.1096335e00, + ] + ), ) - if not with_scaling: - # Check objective function value - npt.assert_almost_equal(f[0, 0], 62.40222244200586) - - # detailed cost values - npt.assert_almost_equal(sol.detailed_cost[0]["cost_value_weighted"], 62.40222242539446) - npt.assert_almost_equal(sol.detailed_cost[1]["cost_value_weighted"], 1.6611394850611363e-08) - npt.assert_almost_equal( - f[0, 0], sum(sol.detailed_cost[i]["cost_value_weighted"] for i in range(len(sol.detailed_cost))) - ) - - # initial and final position - npt.assert_almost_equal(q[:, 0], np.array([0.34906585, 2.24586773])) - npt.assert_almost_equal(q[:, -1], np.array([0.9256103, 1.29037205])) - npt.assert_almost_equal(qdot[:, 0], np.array([0, 0])) - npt.assert_almost_equal(qdot[:, -1], np.array([0, 0])) - - npt.assert_almost_equal(tau[:, 0], np.array([0.42135681, -0.30494449])) - npt.assert_almost_equal(tau[:, -2], np.array([-0.39329963, 0.36152636])) - - npt.assert_almost_equal( - k[:, 0], - np.array( - [0.00227125, 0.01943845, -0.00045809, 0.04340353, -0.05890334, -0.02196787, 0.02044042, -0.08280278] - ), - ) - npt.assert_almost_equal(ref[:, 0], np.array([2.81907786e-02, 2.84412560e-01, 0, 0])) - npt.assert_almost_equal( - m[:, 0], - np.array( - [ - 1.11111643e00, - 9.66024409e-06, - -4.78746311e-04, - -8.69421987e-04, - 1.49883122e-04, - 1.11128979e00, - -1.34894811e-02, - -1.60812429e-02, - -1.23773893e-02, - -6.07070546e-05, - 1.11396504e00, - 5.46363498e-03, - -2.04675057e-05, - -1.22691010e-02, - 1.84207561e-03, - 1.10421909e00, - ] - ), - ) - - npt.assert_almost_equal( - cov[:, -2], - np.array( - [ - 0.00644836, - -0.00610657, - -0.00544246, - 0.00168837, - 0.0005854, - -0.00123564, - 0.0103952, - 0.01108306, - -0.00252879, - -0.00192049, - ] - ), - ) - - npt.assert_almost_equal( - a[:, 3], - np.array( - [ - 1.00000000e00, - -2.17926087e-13, - 1.26870284e-03, - -3.78607634e-02, - -2.56284891e-13, - 1.00000000e00, - -2.19752019e-01, - 4.81445536e-01, - -1.00000000e-01, - 1.09505432e-13, - 1.02554762e00, - 4.87997817e-02, - 9.63854391e-14, - -1.00000000e-01, - 4.91622255e-02, - 8.87744034e-01, - ] - ), - ) - - npt.assert_almost_equal( - c[:, 2], - np.array( - [ - 2.24899604e-16, - 4.19692812e-16, - -1.35499970e00, - 1.12950726e00, - -1.16296826e-16, - -5.23075855e-16, - 1.12950726e00, - -3.79903118e00, - 6.93079055e-17, - 4.43906938e-16, - -2.00791886e-02, - 4.98852395e-02, - 3.32248534e-16, - 1.04710774e-15, - -4.28795369e-02, - 9.36788627e-02, - -2.55942876e-13, - -2.73014494e-13, - 5.33498922e-02, - 4.09670671e-02, - 5.18153700e-15, - 3.81994693e-14, - -3.35841216e-04, - 1.26309820e-02, - ] - ), - ) - else: - # Check objective function value - npt.assert_almost_equal(f[0, 0], 62.40224045726969, decimal=4) - - # detailed cost values - npt.assert_almost_equal(sol.detailed_cost[0]["cost_value_weighted"], 62.40222242578194, decimal=4) - npt.assert_almost_equal(sol.detailed_cost[1]["cost_value_weighted"], 1.8031487750452925e-05, decimal=4) - npt.assert_almost_equal( - f[0, 0], sum(sol.detailed_cost[i]["cost_value_weighted"] for i in range(len(sol.detailed_cost))) - ) - - if with_cholesky and with_scaling and use_sx: - return - - # initial and final position - npt.assert_almost_equal(q[:, 0], np.array([0.34906585, 2.24586773])) - npt.assert_almost_equal(q[:, -1], np.array([0.9256103, 1.29037205])) - npt.assert_almost_equal(qdot[:, 0], np.array([0, 0])) - npt.assert_almost_equal(qdot[:, -1], np.array([0, 0])) - - npt.assert_almost_equal(tau[:, 0], np.array([0.42135677, -0.30494447])) - npt.assert_almost_equal(tau[:, -2], np.array([-0.39329968, 0.3615263])) - - npt.assert_almost_equal( - k[:, 0], - np.array( - [0.38339153, 0.16410165, 0.24810509, 0.42872769, -0.35368849, -0.10938936, 0.14249199, -0.25350259] - ), - ) - npt.assert_almost_equal(ref[:, 0], np.array([2.81907786e-02, 2.84412560e-01, 0, 0])) - npt.assert_almost_equal( - m[:, 0], - np.array( - [ - 1.11109420e00, - -2.00975244e-05, - 1.52182976e-03, - 1.80877721e-03, - 1.76457230e-04, - 1.11160762e00, - -1.58811508e-02, - -4.46861689e-02, - -1.24668133e-02, - -6.45898260e-05, - 1.12201319e00, - 5.81308511e-03, - -7.64514277e-06, - -1.24164397e-02, - 6.88061131e-04, - 1.11747957e00, - ] - ), - ) - npt.assert_almost_equal( - cov[:, -2], - np.array( - [ - -8.67400623e-03, - 5.77329567e-05, - -1.30885973e-03, - 1.12501586e-02, - 4.64929473e-03, - 2.32462786e-03, - 4.92631923e-03, - 3.97615552e-03, - 6.52664876e-03, - -6.66843408e-04, - ] - ), - ) + npt.assert_almost_equal( + cov[:, -2], + np.array( + [ + -2.1576659e-05, + 4.5807147e-05, + -2.3400401e-04, + 4.9389676e-04, + 4.5807147e-05, + -8.9858345e-05, + 4.0996883e-04, + -8.8392478e-04, + -2.3400401e-04, + 4.0996883e-04, + -4.2538567e-03, + 8.8954301e-03, + 4.9389676e-04, + -8.8392478e-04, + 8.8954301e-03, + -1.9118596e-02, + ] + ), + ) - npt.assert_almost_equal( - a[:, 3], - np.array( - [ - 1.00000000e00, - -8.35886644e-14, - -2.60671845e-02, - -6.51469362e-02, - -1.10663430e-14, - 1.00000000e00, - -3.68704938e-01, - 7.95318548e-01, - -1.00000000e-01, - -1.65656031e-14, - 1.13051096e00, - 7.33175161e-02, - -1.61686165e-14, - -1.00000000e-01, - 6.34380653e-02, - 9.83897210e-01, - ] - ), - ) + npt.assert_almost_equal( + a[:, 3], + np.array( + [ + 1.0000000e00, + -1.6381309e-31, + -2.7975098e-01, + 5.7472138e-01, + 9.5108043e-32, + 1.0000000e00, + -1.0146108e00, + 2.6772428e00, + -1.0000000e-01, + 3.0022057e-31, + 6.1784980e-01, + 1.1194146e00, + 1.9609916e-31, + -1.0000000e-01, + -4.0440906e-02, + 1.1627225e00, + ] + ), + ) + np.set_printoptions(threshold=30) + print(c[:, 2]) + npt.assert_almost_equal( + c[:, 2], + np.array( + [ + 3.74755747e-37, + -1.65536315e-35, + -1.35553765e00, + 1.12565454e00, + -3.76558849e-36, + 1.05336311e-35, + 1.12565454e00, + -3.79193715e00, + 4.51852858e-35, + -1.92552034e-34, + 9.02390584e-02, + -2.29668590e-01, + 2.42659035e-34, + -6.69404844e-34, + 7.59635648e-01, + -2.23881857e00, + 2.27969529e-31, + -1.50500085e-32, + 4.54215287e-01, + -7.57727880e-01, + -1.84502320e-32, + -1.03285044e-32, + -2.26170260e-01, + 5.81482655e-01, + ] + ), + ) - npt.assert_almost_equal( - c[:, 2], - np.array( - [ - -1.12917038e-15, - 4.53652494e-15, - -1.35499971e00, - 1.12950724e00, - 1.05525107e-14, - -2.09358023e-14, - 1.12950724e00, - -3.79903115e00, - -2.39923923e-14, - 4.58953582e-14, - -2.78756182e-02, - 1.26240135e-01, - -3.27866630e-14, - 7.03268708e-14, - -9.99367009e-02, - 2.09729743e-01, - -3.14543642e-12, - 3.70435383e-12, - 1.91480322e-02, - 8.03625184e-03, - -1.12721323e-12, - 2.00365744e-12, - 1.01115604e-03, - 2.88702060e-03, - ] - ), - ) + np.random.seed(42) + integrated_states = sol.noisy_integrate(integrator=SolutionIntegrator.SCIPY_RK45, to_merge=SolutionMerge.NODES) + integrated_stated_covariance = np.cov(integrated_states["q"][:, -1, :]) + npt.assert_almost_equal( + integrated_stated_covariance, + np.array([[0.205039, -0.411671], [-0.411671, 0.971819]]), + decimal=6, + ) diff --git a/tests/shard6/test_time_dependent_custom_model.py b/tests/shard6/test_time_dependent_custom_model.py index 2ec196062..97724d80c 100644 --- a/tests/shard6/test_time_dependent_custom_model.py +++ b/tests/shard6/test_time_dependent_custom_model.py @@ -1,9 +1,5 @@ -import pytest - +import platform from typing import Callable -import numpy as np -import numpy.testing as npt -from casadi import DM, MX, SX, vertcat, exp from bioptim import ( BoundsList, @@ -27,6 +23,12 @@ SolutionMerge, VariableScaling, ) +from casadi import DM, MX, SX, vertcat, exp +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils class Model: @@ -663,6 +665,33 @@ def test_time_dependent(test_index): use_sx=use_sx, ) + # Check the values which will be sent to the solver + np.random.seed(42) + match test_index: + case 0: + v_len = 120 + expected = [57.25320994195022, 32172.586744141103, 3.884658443332594e22] + case 1: + v_len = 120 + expected = [57.25320994195022, 32172.586744141103, 3.884658443332594e22] + case 2: + v_len = 175 + expected = [82.6544264934729, 32385.533507818545, -1.6254104960995158e19] + case 3: + v_len = 175 + expected = [82.6544264934729, 32385.533507818545, -1.6254104960995158e19] + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.random.rand(v_len, 1), + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + sol = ocp.solve() # Check cost diff --git a/tests/shard6/test_time_dependent_problems.py b/tests/shard6/test_time_dependent_problems.py index 177151b82..b85c773d1 100644 --- a/tests/shard6/test_time_dependent_problems.py +++ b/tests/shard6/test_time_dependent_problems.py @@ -6,11 +6,7 @@ Note that the final node is not tracked. """ -from casadi import MX, SX, vertcat, sin, Function, DM, reshape -import os -import pytest -import numpy as np -import numpy.testing as npt +import platform from bioptim import ( BiorbdModel, @@ -29,10 +25,13 @@ NonLinearProgram, PhaseDynamics, SolutionMerge, - ConstraintList, - Node, - Solver, ) +from casadi import MX, SX, vertcat, sin, Function, DM, reshape +import numpy as np +import numpy.testing as npt +import pytest + +from ..utils import TestUtils def time_dynamic( @@ -102,75 +101,6 @@ def custom_configure( ConfigureProblem.configure_dynamics_function(ocp, nlp, time_dynamic) -def custom_configure_tf( - ocp: OptimalControlProgram, nlp: NonLinearProgram, numerical_data_timeseries: dict[str, np.ndarray] = None -): - """ - Tell the program which variables are states and controls. - The user is expected to use the ConfigureProblem.configure_xxx functions. - - Parameters - ---------- - ocp: OptimalControlProgram - A reference to the ocp - nlp: NonLinearProgram - A reference to the phase - """ - - ConfigureProblem.configure_q(ocp, nlp, as_states=True, as_controls=False) - ConfigureProblem.configure_qdot(ocp, nlp, as_states=True, as_controls=False) - ConfigureProblem.configure_tau(ocp, nlp, as_states=False, as_controls=True) - - ConfigureProblem.configure_dynamics_function(ocp, nlp, tf_dynamic) - - -def tf_dynamic( - time: MX | SX, - states: MX | SX, - controls: MX | SX, - parameters: MX | SX, - algebraic_states: MX | SX, - numerical_timeseries: MX | SX, - nlp: NonLinearProgram, -) -> DynamicsEvaluation: - """ - The custom dynamics function that provides the derivative of the states: dxdt = f(t, x, u, p, a, d) - - Parameters - ---------- - time: MX | SX - The time of the system - states: MX | SX - The state of the system - controls: MX | SX - The controls of the system - parameters: MX | SX - The parameters acting on the system - algebraic_states: MX | SX - The Algebraic states variables of the system - numerical_timeseries: MX | SX - The numerical timeseries of the system - nlp: NonLinearProgram - A reference to the phase - - Returns - ------- - The derivative of the states in the tuple[MX | SX] format - """ - - q = DynamicsFunctions.get(nlp.states["q"], states) - qdot = DynamicsFunctions.get(nlp.states["qdot"], states) - tau = DynamicsFunctions.get(nlp.controls["tau"], controls) * ( - sin(nlp.dt_mx * nlp.ns - time) * time.ones(nlp.model.nb_tau) * 10 - ) - - # You can directly call biorbd function (as for ddq) or call bioptim accessor (as for dq) - dq = DynamicsFunctions.compute_qdot(nlp, q, qdot) - ddq = nlp.model.forward_dynamics(q, qdot, tau) - - return DynamicsEvaluation(dxdt=vertcat(dq, ddq), defects=None) - - def prepare_ocp( biorbd_model_path: str, n_phase: int, @@ -179,7 +109,6 @@ def prepare_ocp( minimize_time: bool, use_sx: bool, phase_dynamics: PhaseDynamics = PhaseDynamics.ONE_PER_NODE, - with_tf_dynamic: bool = False, ) -> OptimalControlProgram: """ Prepare the ocp @@ -231,22 +160,13 @@ def prepare_ocp( dynamics = DynamicsList() expand = not isinstance(ode_solver, OdeSolver.IRK) for i in range(len(bio_model)): - if with_tf_dynamic: - dynamics.add( - custom_configure_tf, - dynamic_function=tf_dynamic, - phase=i, - expand_dynamics=expand, - phase_dynamics=phase_dynamics, - ) - else: - dynamics.add( - custom_configure, - dynamic_function=time_dynamic, - phase=i, - expand_dynamics=expand, - phase_dynamics=phase_dynamics, - ) + dynamics.add( + custom_configure, + dynamic_function=time_dynamic, + phase=i, + expand_dynamics=expand, + phase_dynamics=phase_dynamics, + ) # Define states path constraint x_bounds = BoundsList() @@ -330,7 +250,7 @@ def test_time_dependent_problem(n_phase, integrator, control_type, minimize_time """ from bioptim.examples.torque_driven_ocp import example_multi_biorbd_model as ocp_module - bioptim_folder = os.path.dirname(ocp_module.__file__) + bioptim_folder = TestUtils.module_folder(ocp_module) if integrator == OdeSolver.IRK and use_sx: with pytest.raises( @@ -385,6 +305,60 @@ def test_time_dependent_problem(n_phase, integrator, control_type, minimize_time minimize_time=minimize_time, use_sx=use_sx, ) + # Check the values which will be sent to the solver + np.random.seed(42) + match integrator: + case OdeSolver.RK4: + if control_type == ControlType.CONSTANT: + v_len = 185 * n_phase + expected = ( + [18.5, 80006.0 if minimize_time else 6.0, 0.8715987034298607] + if n_phase == 1 + else [37.0, 400012.00000000006 if minimize_time else 12.0, 6.033764148108874] + ) + elif control_type == ControlType.LINEAR_CONTINUOUS: + v_len = 187 * n_phase + expected = ( + [18.699999999999996, 80006.0 if minimize_time else 6.0, 0.8715987034298607] + if n_phase == 1 + else [37.39999999999999, 400012.00000000006 if minimize_time else 12.0, 6.033764148108878] + ) + else: + raise ValueError("Test not implemented") + case OdeSolver.COLLOCATION: + v_len = 665 * n_phase + expected = ( + [66.5, 80006.0 if minimize_time else 6.0, 6.035552847184389] + if n_phase == 1 + else [133.0, 400012.00000000006 if minimize_time else 12.0, 28.618666282170977] + ) + case OdeSolver.IRK: + v_len = 305 * n_phase + expected = ( + [30.5, 320010.0 if minimize_time else 10.0, 4.283653839663469] + if n_phase == 1 + else [61.0, 1600019.9999999998 if minimize_time else 20.0, 8.125629434161866] + ) + case OdeSolver.TRAPEZOIDAL: + v_len = 187 * n_phase + expected = ( + [18.699999999999996, 80006.0 if minimize_time else 6.0, 1.5103810164979388] + if n_phase == 1 + else [37.39999999999999, 400012.00000000006 if minimize_time else 12.0, 7.154696449039014] + ) + case _: + raise ValueError("Test not implemented") + + TestUtils.compare_ocp_to_solve( + ocp, + v=np.ones((v_len, 1)) / 10, # Random creates nan in the g vector + expected_v_f_g=expected, + decimal=6, + ) + if platform.system() == "Windows": + return + + return sol = ocp.solve() if integrator is OdeSolver.IRK: @@ -446,9 +420,9 @@ def test_time_dependent_problem(n_phase, integrator, control_type, minimize_time ) npt.assert_almost_equal(sol.decision_time()[-1], 1.01985, decimal=5) - time_sym = MX.sym("T", 1, 1) - states_sym = ocp.nlp[0].states.mx - controls_sym = ocp.nlp[0].controls.mx + time_sym = ocp.nlp[0].time_cx + states_sym = ocp.nlp[0].states.cx + controls_sym = ocp.nlp[0].controls.cx dyn_fun = Function( "dynamics", diff --git a/tests/utils.py b/tests/utils.py index fb45d2b62..6d1ccca92 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,11 +1,8 @@ import importlib.util from pathlib import Path +import platform from typing import Any - -import numpy as np -import numpy.testing as npt -import pytest -from casadi import MX, Function +from types import ModuleType from bioptim import ( BiorbdModel, @@ -22,12 +19,38 @@ SolutionMerge, OptimizationVariableList, ) +from bioptim.interfaces.ipopt_interface import IpoptInterface +from casadi import MX, Function +import numpy as np +import numpy.testing as npt +import pytest class TestUtils: @staticmethod def bioptim_folder() -> str: - return str(Path(__file__).parent / "../bioptim") + return TestUtils._capitalize_folder_drive(str(Path(__file__).parent / "../bioptim")) + + @staticmethod + def module_folder(module: ModuleType) -> str: + return TestUtils._capitalize_folder_drive(str(Path(module.__file__).parent)) + + @staticmethod + def compare_ocp_to_solve(ocp: OptimalControlProgram, v: np.ndarray, expected_v_f_g: list[float], decimal: int = 6): + interface = IpoptInterface(ocp=ocp) + v_cx = interface.ocp.variables_vector + f = interface.dispatch_obj_func() + g = interface.dispatch_bounds()[0] + + values = Function("v", [v_cx], [v_cx, f, g])(v) + npt.assert_allclose([np.sum(value) for value in values], expected_v_f_g, rtol=10**-decimal) + + @staticmethod + def _capitalize_folder_drive(folder: str) -> str: + if platform.system() == "Windows" and folder[1] == ":": + # Capitilize the drive letter if it is windows + folder = folder[0].upper() + folder[1:] + return folder @staticmethod def load_module(path: str) -> Any: