Skip to content

Commit

Permalink
Made MINIMIZE_TIME dependent on node, and remove sol.times(), states(…
Browse files Browse the repository at this point in the history
…) and controls()
  • Loading branch information
pariterre committed Feb 14, 2024
1 parent 8caa5f1 commit c0e1952
Show file tree
Hide file tree
Showing 7 changed files with 6 additions and 114 deletions.
2 changes: 1 addition & 1 deletion bioptim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
)
from .optimization.parameters import ParameterList
from .optimization.solution.solution import Solution
from .optimization.solution.solution_data import SolutionMerge, TimeAlignment, TimeResolution
from .optimization.solution.solution_data import SolutionMerge, TimeAlignment
from .optimization.optimization_variable import OptimizationVariableList
from .optimization.variable_scaling import VariableScalingList, VariableScaling
from .optimization.variational_optimal_control_program import VariationalOptimalControlProgram
Expand Down
2 changes: 1 addition & 1 deletion bioptim/dynamics/configure_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ def stochastic_torque_driven_free_floating_base(
ConfigureProblem.torque_driven_free_floating_base(
ocp=ocp,
nlp=nlp,
with_contact=with_contact,
with_contact=with_contact, # TODO : this should be removed
with_friction=with_friction,
)

Expand Down
2 changes: 1 addition & 1 deletion bioptim/limits/objective_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def minimize_time(
taken into account elsewhere in the code)
"""

return controller.tf.cx
return controller.time.cx

@staticmethod
def get_dt(_):
Expand Down
6 changes: 2 additions & 4 deletions bioptim/limits/penalty_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,8 @@ def t_span(self) -> OptimizationVariable:
-------
"""
dt_mx = self.dt.mx
mx = vertcat(self.time.mx, dt_mx)
dt_cx = self.dt.cx
cx = vertcat(self.time.cx, dt_cx)
mx = vertcat(self.time.mx, self.dt.mx)
cx = vertcat(self.time.cx, self.dt.cx)

tp = OptimizationVariableList(self._nlp.cx, self._nlp.phase_dynamics == PhaseDynamics.SHARED_DURING_THE_PHASE)
n_val = cx.shape[0]
Expand Down
105 changes: 0 additions & 105 deletions bioptim/optimization/solution/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,45 +327,6 @@ def from_ocp(cls, ocp):

return cls(ocp=ocp)

def time(
self,
time_resolution: TimeResolution = TimeResolution.DECISION,
time_alignment: TimeAlignment = TimeAlignment.STATES,
to_merge: SolutionMerge | list[SolutionMerge] = None,
continuous: bool = True,
) -> list | np.ndarray:
"""
Returns the time vector at each node that matches stepwise_states or stepwise_controls
Parameters
----------
time_resolution: TimeResolution
The type of time vector to return
time_alignment: TimeAlignment
The type of alignment to perform. If TimeAlignment.STATES, then the time vector is aligned with the states
(i.e. all the subnodes and the last node time are present). If TimeAlignment.CONTROLS, then the time vector
is aligned with the controls (i.e. only starting of the node without the last node if CONTROL constant).
to_merge: SolutionMerge | list[SolutionMerge, ...]
The type of merge to perform. If None, then no merge is performed. It is often useful to merge NODES, but
is completely useless to merge KEYS
continuous: bool
If the time should be continuous throughout the whole ocp. If False, then the time is reset at the
beginning of each phase.
Returns
-------
The time vector at each node that matches stepwise_states or stepwise_controls
"""

if time_resolution == TimeResolution.DECISION:
return self.decision_time(to_merge=to_merge, time_alignment=time_alignment, continuous=continuous)
elif time_resolution == TimeResolution.STEPWISE:
return self.stepwise_time(to_merge=to_merge, time_alignment=time_alignment, continuous=continuous)
elif time_resolution == TimeResolution.NODE_SPAN:
return self.t_span(to_merge=to_merge, time_alignment=time_alignment, continuous=continuous)
else:
raise ValueError("Unrecognized time_resolution")

def t_span(
self,
to_merge: SolutionMerge | list[SolutionMerge] = None,
Expand Down Expand Up @@ -529,39 +490,6 @@ def _process_time_vector(

return times if len(times) > 1 else times[0]

def states(
self,
time_resolution: TimeResolution = TimeResolution.DECISION,
to_merge: SolutionMerge | list[SolutionMerge] = None,
scaled: bool = False,
):
"""
Returns the states
Parameters
----------
time_resolution: TimeResolution
The time resolution to return the states
to_merge: SolutionMerge | list[SolutionMerge, ...]
The type of merge to perform. If None, then no merge is performed.
scaled: bool
If the states should be scaled or not (note that scaled is as Ipopt received them, while unscaled is as the
model needs temps). If you don't know what it means, you probably want the unscaled version.
Returns
-------
The states
"""

if time_resolution == TimeResolution.STEPWISE:
return self.stepwise_states(to_merge=to_merge, scaled=scaled)
elif time_resolution == TimeResolution.DECISION:
return self.decision_states(to_merge=to_merge, scaled=scaled)
elif time_resolution == TimeResolution.NODE_SPAN:
raise NotImplementedError("NODE_SPAN is not implemented for states")
else:
raise ValueError("Unrecognized time_resolution")

def decision_states(self, scaled: bool = False, to_merge: SolutionMerge | list[SolutionMerge] = None):
"""
Returns the decision states
Expand Down Expand Up @@ -609,39 +537,6 @@ def stepwise_states(self, scaled: bool = False, to_merge: SolutionMerge | list[S
return data
return data if len(data) > 1 else data[0]

def controls(
self,
time_resolution: TimeResolution = TimeResolution.DECISION,
to_merge: SolutionMerge | list[SolutionMerge] = None,
scaled: bool = False,
):
"""
Returns the controls
Parameters
----------
time_resolution: TimeResolution
The time resolution to return the controls
to_merge: SolutionMerge | list[SolutionMerge, ...]
The type of merge to perform. If None, then no merge is performed.
scaled: bool
If the controls should be scaled or not (note that scaled is as Ipopt received them, while unscaled is as the
model needs temps). If you don't know what it means, you probably want the unscaled version.
Returns
-------
The controls
"""

if time_resolution == TimeResolution.STEPWISE:
return self.stepwise_controls(to_merge=to_merge, scaled=scaled)
elif time_resolution == TimeResolution.DECISION:
return self.decision_controls(to_merge=to_merge, scaled=scaled)
elif time_resolution == TimeResolution.NODE_SPAN:
raise NotImplementedError("NODE_SPAN is not implemented for controls")
else:
raise ValueError("Unrecognized time_resolution")

def decision_controls(self, scaled: bool = False, to_merge: SolutionMerge | list[SolutionMerge] = None):
"""
Returns the decision controls
Expand Down
1 change: 0 additions & 1 deletion tests/shard3/test_get_time_solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def _get_solution(
"n_shooting": 10,
"phase_dynamics": phase_dynamics,
"ode_solver": ode_solver_instance,
"phase_dynamics": PhaseDynamics.SHARED_DURING_THE_PHASE,
"control_type": control_type,
"use_sx": False,
}
Expand Down
2 changes: 1 addition & 1 deletion tests/shard4/test_penalty.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def test_penalty_targets_shapes():
@pytest.mark.parametrize("value", [0.1, -10])
def test_penalty_minimize_time(penalty_origin, value, phase_dynamics):
ocp = prepare_test_ocp(phase_dynamics=phase_dynamics)
t = [0]
t = [0.05 * ocp.nlp[0].ns]
phases_dt = [0.05]
x = [DM.ones((8, 1)) * value]
u = [0]
Expand Down

0 comments on commit c0e1952

Please sign in to comment.