-
Notifications
You must be signed in to change notification settings - Fork 55
BREAKING CHANGE: Move ode_solver from OptimalControlProgram to DynamicsList.add #959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Only ACADOS is failing. |
Ipuch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 7 of 72 files at r1, all commit messages.
Reviewable status: 7 of 72 files reviewed, 10 unresolved discussions
bioptim/__init__.py line 185 at r1 (raw file):
from .misc.__version__ import __version__ from .misc.enums import ( Axis,
I'm not sure about this refactor...
Code quote:
from .dynamics.configure_problem import ConfigureProblem, DynamicsFcn, DynamicsList, Dynamics
from .dynamics.dynamics_evaluation import DynamicsEvaluation
from .dynamics.dynamics_functions import DynamicsFunctions
from .dynamics.fatigue.effort_perception import EffortPerception, TauEffortPerception
from .dynamics.fatigue.fatigue_dynamics import FatigueList
from .dynamics.fatigue.michaud_fatigue import MichaudFatigue, MichaudTauFatigue
from .dynamics.fatigue.xia_fatigue import XiaFatigue, XiaTauFatigue, XiaFatigueStabilized
from .dynamics.ode_solvers import OdeSolver, OdeSolverBase
from .gui.online_callback_server import PlottingServer
from .gui.plot import CustomPlot
from .interfaces import Solver
from .limits.constraints import ConstraintFcn, ConstraintList, Constraint, ParameterConstraintList
from .limits.fatigue_path_conditions import FatigueBounds, FatigueInitialGuess
from .limits.multinode_constraint import MultinodeConstraintFcn, MultinodeConstraintList, MultinodeConstraint
from .limits.multinode_objective import MultinodeObjectiveFcn, MultinodeObjectiveList, MultinodeObjective
from .limits.objective_functions import ObjectiveFcn, ObjectiveList, Objective, ParameterObjectiveList
from .limits.path_conditions import BoundsList, InitialGuessList, Bounds, InitialGuess
from .limits.penalty_controller import PenaltyController
from .limits.penalty_helpers import PenaltyHelpers
from .limits.phase_transition import PhaseTransitionFcn, PhaseTransitionList, PhaseTransition
from .misc.__version__ import __version__
from .misc.enums import (
Axis,bioptim/optimization/optimal_control_program.py line 523 at r1 (raw file):
is_ode_solver = isinstance(dynamics[i_dyn].ode_solver, OdeSolverBase) if not is_ode_solver: raise RuntimeError("ode_solver should be built an instance of OdeSolver")
It should be a method of Dynamic
Code quote:
if dyn.ode_solver is None:
dynamics[i_dyn].ode_solver = self._set_default_ode_solver()
is_ode_solver = isinstance(dynamics[i_dyn].ode_solver, OdeSolverBase)
if not is_ode_solver:
raise RuntimeError("ode_solver should be built an instance of OdeSolver")bioptim/optimization/optimal_control_program.py line 580 at r1 (raw file):
# Prepare path constraints and dynamics of the program NLP.add(self, "dynamics_type", dynamics, False) ode_solver = [dyn.ode_solver for dyn in dynamics]
Should it be method of DynamicsList ?
bioptim/optimization/optimal_control_program.py line 522 at r1 (raw file):
) if not is_ode_solver and not is_list_ode_solver: raise RuntimeError("ode_solver should be built an instance of OdeSolver or a list of OdeSolver")
If the idea of this PR is preserved, we can put these checks in some utils functions to move import them wherever we want.
Code quote:
if ode_solver is None:
ode_solver = self._set_default_ode_solver()
is_ode_solver = isinstance(ode_solver, OdeSolverBase)
is_list_ode_solver = (
all([isinstance(ode, OdeSolverBase) for ode in ode_solver])
if isinstance(ode_solver, list) or isinstance(ode_solver, tuple)
else False
)
if not is_ode_solver and not is_list_ode_solver:
raise RuntimeError("ode_solver should be built an instance of OdeSolver or a list of OdeSolver")bioptim/optimization/stochastic_optimal_control_program.py line 637 at r1 (raw file):
"duplicate_starting_point=True" ")" )
You don't check it anymore ? somewhere else ? (I havent read the whole PR at this stage.)
Code quote:
def _check_has_no_ode_solver_defined(**kwargs):
if "ode_solver" in kwargs:
raise ValueError(
"The ode_solver cannot be defined for a stochastic ocp. "
"The value is chosen based on the type of problem solved:"
"\n- TRAPEZOIDAL_EXPLICIT: OdeSolver.TRAPEZOIDAL() "
"\n- TRAPEZOIDAL_IMPLICIT: OdeSolver.TRAPEZOIDAL() "
"\n- COLLOCATION: "
"OdeSolver.COLLOCATION("
"method=problem_type.method, "
"polynomial_degree=problem_type.polynomial_degree, "
"duplicate_starting_point=True"
")"
)bioptim/optimization/variational_optimal_control_program.py line 74 at r1 (raw file):
"ode_solver cannot be defined in VariationalOptimalControlProgram since the integration is" " done by the variational integrator." )
for memory
Code quote:
if "ode_solver" in kwargs:
raise ValueError(
"ode_solver cannot be defined in VariationalOptimalControlProgram since the integration is"
" done by the variational integrator."
)bioptim/optimization/non_linear_program.py line 289 at r1 (raw file):
if not_direct_collocation and (x_init_all_point or a_init_all_point): raise ValueError("InterpolationType.ALL_POINTS must only be used with direct collocation")
thanks
Code quote:
if x_init is not None or a_init is not None:
not_direct_collocation = not self.ode_solver.is_direct_collocation
x_init_all_point = x_init.type == InterpolationType.ALL_POINTS if x_init is not None else False
a_init_all_point = a_init.type == InterpolationType.ALL_POINTS if a_init is not None else False
if not_direct_collocation and (x_init_all_point or a_init_all_point):
raise ValueError("InterpolationType.ALL_POINTS must only be used with direct collocation")bioptim/optimization/stochastic_optimal_control_program.py line 91 at r1 (raw file):
# Integrator for dyn in dynamics: dyn.ode_solver = self._set_default_ode_solver()
I would expect a:
dyn.check_has_no_ode_defined(...)bioptim/optimization/non_linear_program.py line 149 at r1 (raw file):
self.n_threads = None self.ns = None self.ode_solver = OdeSolver.RK4()
pretty dangerous now. I would be conservative and try something to set this attribute from self.dynamics to transfer the responsability to nlp.
bioptim/optimization/non_linear_program.py line 436 at r1 (raw file):
return 1 if self.ode_solver.is_direct_collocation: return self.dynamics[node_idx].shape_xall[1] - (1 if not self.ode_solver.duplicate_starting_point else 0)
Preserve the last version should be a priority, I think. But it might hide something more deep on why these methods are here ...
Code quote:
def n_states_stepwise_steps(self, node_idx) -> int:
"""
Parameters
----------
node_idx: int
The index of the node
Returns
-------
The number of states
"""
if node_idx >= self.ns:
return 1
if self.ode_solver.is_direct_collocation:
return self.dynamics[node_idx].shape_xall[1] - (1 if not self.ode_solver.duplicate_starting_point else 0)|
I really want to take the benefits of this refactor. Because this is a decision with a lot of impact. I reviewed only a part of it to see the main consequences. But the PR doesn't seem to justify why it's absolutely wanted. Don't work more on this one before we talk about it. |
|
Disussing the best choice with EVE : #Solution RETENUE
ode_solver = OdeSolverList()
ode_solver.add(OdeSolver.RK4, Dynamics(TORQUE_DRIVEN, contact_type, with_ ...))
ode_solver.add(OdeSolver.RK4, Dynamics(TORQUE_DRIVEN, contact_type, with_ ...))
ode_solver.integrate() |
EveCharbie
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 7 of 72 files reviewed, 9 unresolved discussions (waiting on @Ipuch)
bioptim/__init__.py line 185 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
I'm not sure about this refactor...
bad merge
bioptim/optimization/stochastic_optimal_control_program.py line 91 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
I would expect a:
dyn.check_has_no_ode_defined(...)
In stochastic, the ode_solver is defined by the SocpType because there is an interaction with the variables definition in the model. (But I have to check because the algebraic_states changes we have done might have fixed this issue)
EveCharbie
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 4 of 85 files reviewed, 9 unresolved discussions (waiting on @Ipuch)
bioptim/optimization/non_linear_program.py line 149 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
pretty dangerous now. I would be conservative and try something to set this attribute from self.dynamics to transfer the responsability to nlp.
Done.
bioptim/optimization/optimal_control_program.py line 523 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
It should be a method of Dynamic
Done.
bioptim/optimization/optimal_control_program.py line 580 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
Should it be method of DynamicsList ?
Done.
bioptim/optimization/stochastic_optimal_control_program.py line 637 at r1 (raw file):
Previously, Ipuch (Pierre Puchaud) wrote…
You don't check it anymore ? somewhere else ? (I havent read the whole PR at this stage.)
Done.
|
@Ipuch I tried doing the refactor we agreed on but it is complex and will create conflicts with my other PRs. |
Okay, can you open a PR en top of it ? so we can track all the changes together. I think there are already some littles conflicts can you solve them first ? |
|
@Ipuch could this be merged ? |
Ipuch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 42 of 72 files at r1, 9 of 20 files at r2, 3 of 5 files at r3, 35 of 35 files at r5, all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on @EveCharbie)
|
@EveCharbie two things and I'm okay your merge it, yourself if in the hurry.
|
Ipuch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 1 of 1 files at r6, all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on @EveCharbie)
BREAKING CHANGE: Move
ode_solverfrom OptimalControlProgram to DynamicsList.addSummary:
This PR introduces a significant breaking change to the Bioptim API regarding the specification of the Ordinary Differential Equation (ODE) solver. The
ode_solverargument has been removed from theOptimalControlProgramconstructor and is now a required argument for each dynamics configuration added viaDynamicsList.add().Motivation:
Previously, a single
ode_solverwas specified at theOptimalControlProgramlevel, applying uniformly to all phases of a multiphase problem. This lacked flexibility for scenarios where different phases might benefit from different integration schemes (e.g., using a stiff solver for one phase and a standard Runge-Kutta for another).Moving the
ode_solverspecification to theDynamicsList.addmethod:ode_solversetting with other phase-specific dynamics arguments likeexpand_dynamicsandphase_dynamics.This is a breaking change and will require modifications to existing Bioptim scripts.
If your code previously defined an
OptimalControlProgramby passing theode_solverargument, you will encounter an error with this new version. You must update your code as follows:ode_solverargument from yourOptimalControlProgram(...)call.ode_solverargument to each call where you define phase dynamics usingdynamics.add(...).Example Migration:
Let's illustrate with a snippet from a typical
prepare_ocpfunction:Code BEFORE this PR:
Code AFTER this PR:
This change is