Skip to content
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

[RTR] Parameters uniformisation #846

Merged
merged 34 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b39086b
heritage from optimization variables
EveCharbie Feb 12, 2024
49abbf4
uniformized a little
EveCharbie Feb 15, 2024
8a4aac9
Merge remote-tracking branch 'pyomeca/master' into parameter_scaling
EveCharbie Feb 15, 2024
1250577
removed parameter scaling
EveCharbie Feb 16, 2024
e0555ea
fixed my own shits
EveCharbie Feb 16, 2024
17ad376
uniformized using penaltyHelpers
EveCharbie Feb 16, 2024
8263f2c
fixed some free variables
EveCharbie Feb 16, 2024
f5f074d
blacked
EveCharbie Feb 16, 2024
40ee00e
fixed mx_reduced problem in parameter function
EveCharbie Feb 16, 2024
a199d38
made requested changes
EveCharbie Feb 16, 2024
55a011b
reverted dt -> phases_dt
EveCharbie Feb 18, 2024
946e0b9
do not recreate a dt parameter
EveCharbie Feb 18, 2024
0408f5e
blacked
EveCharbie Feb 18, 2024
94e6b46
dt -> parameter_list
EveCharbie Feb 18, 2024
f65f776
fixed parameter test
EveCharbie Feb 18, 2024
67404f7
fixed stochastic tests
EveCharbie Feb 18, 2024
8f6ffc2
blacked
EveCharbie Feb 18, 2024
25040a5
fixed multi-phase
EveCharbie Feb 18, 2024
5edd086
blacked
EveCharbie Feb 18, 2024
4e3d225
fixed some tests
EveCharbie Feb 18, 2024
180ab9f
fixed some more tests
EveCharbie Feb 19, 2024
8c2cc6f
blacked
EveCharbie Feb 19, 2024
0d0f2a4
fixed some tests
EveCharbie Feb 19, 2024
3cef2bf
blacked
EveCharbie Feb 19, 2024
5c81bf8
some more
EveCharbie Feb 19, 2024
7155fe1
OK
EveCharbie Feb 19, 2024
89e0b8d
let's try
EveCharbie Feb 19, 2024
59c9467
scaling acados ?
EveCharbie Feb 19, 2024
d8d68b5
made requested changes
EveCharbie Feb 19, 2024
5d1113d
blacked
EveCharbie Feb 19, 2024
8e46325
Properly implemented scaling in Acados for states, controls and param…
pariterre Feb 20, 2024
7b17dcf
Changed the value to COLLATION time Lagrange that changed for some re…
pariterre Feb 20, 2024
9198131
Merge remote-tracking branch 'pyomeca/master' into parameter_scaling
pariterre Feb 21, 2024
7b772fa
Removed all occurrences of Lagrange.MINIMIZE_TIME
pariterre Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bioptim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
# --- Managing the parameters --- #
ParameterList
A list of Parameter
ParameterConstrainer
A placeholder for the ParameterList


# --- Managing the boundaries of the variables --- #
Expand Down Expand Up @@ -217,7 +219,7 @@
CyclicMovingHorizonEstimator,
MultiCyclicNonlinearModelPredictiveControl,
)
from .optimization.parameters import ParameterList
from .optimization.parameters import ParameterList, ParameterContainer
from .optimization.solution.solution import Solution
from .optimization.solution.solution_data import SolutionMerge, TimeAlignment
from .optimization.optimization_variable import OptimizationVariableList
Expand Down
31 changes: 19 additions & 12 deletions bioptim/dynamics/configure_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ def configure_dynamics_function(ocp, nlp, dyn_func, **extra_params):
The function to get the derivative of the states
"""

DynamicsFunctions.apply_parameters(nlp.parameters.mx, nlp)
DynamicsFunctions.apply_parameters(nlp)

if not isinstance(dyn_func, (tuple, list)):
dyn_func = (dyn_func,)
Expand All @@ -924,8 +924,8 @@ def configure_dynamics_function(ocp, nlp, dyn_func, **extra_params):
nlp.time_mx,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.mx,
nlp.algebraic_states.scaled.mx,
nlp.parameters.scaled.mx_reduced,
nlp.algebraic_states.scaled.mx_reduced,
nlp,
**extra_params,
)
Expand All @@ -941,8 +941,8 @@ def configure_dynamics_function(ocp, nlp, dyn_func, **extra_params):
time_span_sym,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.mx,
nlp.algebraic_states.scaled.mx,
nlp.parameters.scaled.mx_reduced,
nlp.algebraic_states.scaled.mx_reduced,
],
[dynamics_dxdt],
["t_span", "x", "u", "p", "a"],
Expand Down Expand Up @@ -972,8 +972,8 @@ def configure_dynamics_function(ocp, nlp, dyn_func, **extra_params):
time_span_sym,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.mx,
nlp.algebraic_states.scaled.mx,
nlp.parameters.scaled.mx_reduced,
nlp.algebraic_states.scaled.mx_reduced,
nlp.states_dot.scaled.mx_reduced,
],
[dynamics_eval.defects],
Expand Down Expand Up @@ -1016,16 +1016,16 @@ def configure_contact_function(ocp, nlp, dyn_func: Callable, **extra_params):
time_span_sym,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.mx,
nlp.algebraic_states.scaled.mx,
nlp.parameters.scaled.mx_reduced,
nlp.algebraic_states.scaled.mx_reduced,
],
[
dyn_func(
time_span_sym,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.mx,
nlp.algebraic_states.scaled.mx,
nlp.parameters.scaled.mx_reduced,
nlp.algebraic_states.scaled.mx_reduced,
nlp,
**extra_params,
)
Expand Down Expand Up @@ -1079,9 +1079,16 @@ def configure_soft_contact_function(ocp, nlp):
global_soft_contact_force_func = nlp.model.soft_contact_forces(
nlp.states["q"].mapping.to_second.map(q), nlp.states["qdot"].mapping.to_second.map(qdot)
)

# TODO: do not declare unuseful functions!
nlp.soft_contact_forces_func = Function(
"soft_contact_forces_func",
[nlp.time_mx, nlp.states.mx_reduced, nlp.controls.mx_reduced, nlp.parameters.mx],
[
nlp.time_mx,
nlp.states.scaled.mx_reduced,
nlp.controls.scaled.mx_reduced,
nlp.parameters.scaled.mx_reduced,
],
[global_soft_contact_force_func],
["t", "x", "u", "p"],
["soft_contact_forces"],
Expand Down
23 changes: 15 additions & 8 deletions bioptim/dynamics/dynamics_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ def stochastic_torque_driven(
q = DynamicsFunctions.get(nlp.states["q"], states)
qdot = DynamicsFunctions.get(nlp.states["qdot"], states)
tau = DynamicsFunctions.get(nlp.controls["tau"], controls)
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)
sensory_noise = DynamicsFunctions.get(nlp.parameters["sensory_noise"], parameters)

tau += nlp.model.compute_torques_from_noise_and_feedback(
nlp=nlp,
Expand All @@ -309,8 +311,8 @@ def stochastic_torque_driven(
controls=controls,
parameters=parameters,
algebraic_states=algebraic_states,
sensory_noise=nlp.parameters["sensory_noise"].mx,
motor_noise=nlp.parameters["motor_noise"].mx,
sensory_noise=sensory_noise,
motor_noise=motor_noise,
)
tau = tau + nlp.model.friction_coefficients @ qdot if with_friction else tau

Expand Down Expand Up @@ -366,6 +368,8 @@ def stochastic_torque_driven_free_floating_base(
qdot_roots = DynamicsFunctions.get(nlp.states["qdot_roots"], states)
qdot_joints = DynamicsFunctions.get(nlp.states["qdot_joints"], states)
tau_joints = DynamicsFunctions.get(nlp.controls["tau_joints"], controls)
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)
sensory_noise = DynamicsFunctions.get(nlp.parameters["sensory_noise"], parameters)

q_full = vertcat(q_roots, q_joints)
qdot_full = vertcat(qdot_roots, qdot_joints)
Expand All @@ -378,8 +382,8 @@ def stochastic_torque_driven_free_floating_base(
controls=controls,
parameters=parameters,
algebraic_states=algebraic_states,
motor_noise=nlp.parameters["motor_noise"].mx,
sensory_noise=nlp.parameters["sensory_noise"].mx,
motor_noise=motor_noise,
sensory_noise=sensory_noise,
)
tau_joints = tau_joints + nlp.model.friction_coefficients @ qdot_joints if with_friction else tau_joints

Expand Down Expand Up @@ -992,7 +996,7 @@ def get(var: OptimizationVariable, cx: MX | SX):
return var.mapping.to_second.map(cx[var.index, :])

@staticmethod
def apply_parameters(parameters: MX.sym, nlp):
def apply_parameters(nlp):
"""
Apply the parameter variables to the model. This should be called before calling the dynamics

Expand All @@ -1004,10 +1008,13 @@ def apply_parameters(parameters: MX.sym, nlp):
The definition of the system
"""

for param in nlp.parameters:
for param_key in nlp.parameters:
# Call the pre dynamics function
if param.function[0]:
param.function[0](nlp.model, parameters[param.index], **param.params)
if nlp.parameters[param_key].function:
param = nlp.parameters[param_key]
param_scaling = nlp.parameters[param_key].scaling.scaling
param_reduced = nlp.parameters.scaled.mx_reduced[param.index]
param.function(nlp.model, param_reduced * param_scaling, **param.kwargs)

@staticmethod
def compute_qdot(nlp, q: MX | SX, qdot: MX | SX):
Expand Down
5 changes: 2 additions & 3 deletions bioptim/dynamics/integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ def __init__(self, ode: dict, ode_opt: dict):
self.cx = ode_opt["cx"]
self.t_span_sym = ode["t"]
self.x_sym = ode["x"]
self.u_sym = ode["p"]
self.u_sym = ode["p"] # TODO: @ pariterre could we put ode["u"] instead?
self.param_sym = ode["param"]
self.param_scaling = ode_opt["param_scaling"]
self.a_sym = ode["a"]
self.fun = ode["ode"]
self.implicit_fun = ode["implicit_ode"]
Expand All @@ -83,7 +82,7 @@ def __init__(self, ode: dict, ode_opt: dict):
self.dxdt(
states=self.x_sym,
controls=self.u_sym,
params=self.param_sym * self.param_scaling,
params=self.param_sym,
algebraic_states=self.a_sym,
),
self._input_names,
Expand Down
3 changes: 1 addition & 2 deletions bioptim/dynamics/ode_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def param_ode(self, nlp) -> MX:
-------
The symbolic parameters
"""
return nlp.parameters.cx
return nlp.parameters.scaled.cx_start

def initialize_integrator(self, ocp, nlp, dynamics_index: int, node_index: int, **extra_opt) -> Callable:
"""
Expand Down Expand Up @@ -190,7 +190,6 @@ def initialize_integrator(self, ocp, nlp, dynamics_index: int, node_index: int,
"cx": nlp.cx,
"control_type": nlp.control_type,
"defects_type": self.defects_type,
"param_scaling": vertcat(*[nlp.parameters[key].scaling.scaling for key in nlp.parameters.keys()]),
"ode_index": node_index if nlp.dynamics_func[dynamics_index].size2_out("xdot") > 1 else 0,
"duplicate_starting_point": self.duplicate_starting_point,
**extra_opt,
Expand Down
3 changes: 2 additions & 1 deletion bioptim/examples/getting_started/custom_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
ObjectiveList,
PhaseDynamics,
VariableScaling,
VariableScalingList,
)


Expand Down Expand Up @@ -175,7 +176,7 @@ def prepare_ocp(
u_bounds["tau"][1, :] = 0

# Define the parameter to optimize
parameters = ParameterList()
parameters = ParameterList(use_sx=use_sx)
parameter_objectives = ParameterObjectiveList()
parameter_bounds = BoundsList()
parameter_init = InitialGuessList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def stochastic_forward_dynamics(
motor_noise = 0
sensory_noise = 0
if with_noise:
motor_noise = nlp.parameters["motor_noise"].mx
sensory_noise = nlp.parameters["sensory_noise"].mx
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)
sensory_noise = DynamicsFunctions.get(nlp.parameters["sensory_noise"], parameters)

mus_excitations_fb = mus_excitations
noise_torque = np.zeros(nlp.model.motor_noise_magnitude.shape)
Expand Down
4 changes: 2 additions & 2 deletions bioptim/examples/stochastic_optimal_control/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def dynamics_torque_driven_with_feedbacks(time, states, controls, parameters, al
k = DynamicsFunctions.get(nlp.algebraic_states["k"], algebraic_states)
k_matrix = StochasticBioModel.reshape_to_matrix(k, nlp.model.matrix_shape_k)

motor_noise = nlp.parameters["motor_noise"].mx
sensory_noise = nlp.parameters["sensory_noise"].mx
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)
sensory_noise = DynamicsFunctions.get(nlp.parameters["sensory_noise"], parameters)
end_effector = nlp.model.sensory_reference(time, states, controls, parameters, algebraic_states, nlp)

tau_feedback = get_excitation_with_feedback(k_matrix, end_effector, ref, sensory_noise)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def dynamics(self, states, controls, parameters, algebraic_states, nlp, with_noi
u = DynamicsFunctions.get(nlp.controls["u"], controls)
motor_noise = 0
if with_noise:
motor_noise = nlp.parameters["motor_noise"].mx
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)

qddot = -self.kapa * (q - u) - self.beta * qdot * sqrt(qdot[0] ** 2 + qdot[1] ** 2 + self.c**2) + motor_noise

return DynamicsEvaluation(dxdt=vertcat(qdot, qddot), defects=None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def dynamics(self, states, controls, parameters, algebraic_states, nlp, with_noi

motor_noise = 0
if with_noise:
motor_noise = nlp.parameters["motor_noise"].mx
motor_noise = DynamicsFunctions.get(nlp.parameters["motor_noise"], parameters)

qddot = -0.1 * (1 - q**2) * qdot - q + u + motor_noise

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
BiorbdModel,
PenaltyController,
ParameterObjectiveList,
VariableScaling,
)
from matplotlib import pyplot as plt

Expand Down Expand Up @@ -58,20 +59,12 @@ def prepare_ocp(
tau_mappings.add("tau", to_second=[None, 0], to_first=[1])

# Define the parameter to optimize
parameters = ParameterList()
parameters = ParameterList(use_sx=False)
parameter_init = InitialGuessList()
parameter_bounds = BoundsList()

parameters.add(
"max_tau",
my_parameter_function,
size=1,
)
parameters.add(
"min_tau",
my_parameter_function,
size=1,
)
parameters.add("max_tau", my_parameter_function, size=1, scaling=VariableScaling("max_tau", [1]))
parameters.add("min_tau", my_parameter_function, size=1, scaling=VariableScaling("min_tau", [1]))

parameter_init["max_tau"] = 1
parameter_init["min_tau"] = -1
Expand Down
16 changes: 7 additions & 9 deletions bioptim/gui/check_conditioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ def jacobian_hessian_constraints():
for controller in controllers:
controller.node_index = constraints.node_idx[0]

_, t0, x, u, a = constraints.get_variable_inputs(controllers)
p = nlp.parameters.cx
_, t0, x, u, p, a = constraints.get_variable_inputs(controllers)

list_constraints.append(
jacobian(
Expand Down Expand Up @@ -187,8 +186,7 @@ def jacobian_hessian_constraints():

for controller in controllers:
controller.node_index = constraints.node_idx[0]
_, t0, x, u, a = constraints.get_variable_inputs(controllers)
p = nlp.parameters.cx
_, t0, x, u, p, a = constraints.get_variable_inputs(controllers)

hessian_cas = hessian(
constraints.function[node_index](t0, phases_dt, x, u, p, a)[axis], vertcat_obj
Expand Down Expand Up @@ -390,14 +388,14 @@ def hessian_objective():

for controller in controllers:
controller.node_index = obj.node_idx[0]
_, t0, x, u, s = obj.get_variable_inputs(controllers)
params = nlp.parameters.cx
_, t0, x, u, p, a = obj.get_variable_inputs(controllers)

target = PenaltyHelpers.target(obj, obj.node_idx.index(node_index))

p = obj.weighted_function[node_index](t0, phases_dt, x, u, params, s, obj.weight, target)
penalty = obj.weighted_function[node_index](t0, phases_dt, x, u, p, a, obj.weight, target)

for i in range(p.shape[0]):
objective += p[i] ** 2
for i in range(penalty.shape[0]):
objective += penalty[i] ** 2

# create function to build the hessian
vertcat_obj = vertcat([], *nlp.X_scaled, *nlp.U_scaled) # time, states, controls
Expand Down
8 changes: 4 additions & 4 deletions bioptim/gui/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ def print(self):
print(f"**********")
print(f"PARAMETERS: ")
print("")
for parameter in self.ocp.nlp[phase_idx].parameters:
parameter, initial_guess, min_bound, max_bound, scaling = self._scaling_parameter(parameter.name)
for key in self.ocp.nlp[phase_idx].parameters:
parameter, initial_guess, min_bound, max_bound, scaling = self._scaling_parameter(key)
print(f"Name: {parameter.name}")
print(f"Size: {parameter.size}")
print(f"Initial_guess: {initial_guess}")
Expand Down Expand Up @@ -673,8 +673,8 @@ def _draw_nlp_cluster(self, g, phase_idx: int):

if len(self.ocp.nlp[phase_idx].parameters) > 0:
param_idx = 0
for param in self.ocp.nlp[phase_idx].parameters:
self._draw_parameter_node(g, phase_idx, param_idx, param.name)
for key in self.ocp.nlp[phase_idx].parameters:
self._draw_parameter_node(g, phase_idx, param_idx, key)
param_idx += 1
else:
node_str = "<u><b>Parameters</b></u><br/> No parameter set"
Expand Down
2 changes: 1 addition & 1 deletion bioptim/gui/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ def _compute_y_from_plot_func(
idx,
lambda p_idx, n_idx, sn_idx: u[n_idx][:, sn_idx] if n_idx < len(u) else np.ndarray((0, 1)),
)
p_node = PenaltyHelpers.parameters(penalty, lambda: np.array(p))
p_node = PenaltyHelpers.parameters(penalty, 0, lambda p_idx, n_idx, sn_idx: np.array(p))
a_node = PenaltyHelpers.states(
penalty,
idx,
Expand Down
4 changes: 2 additions & 2 deletions bioptim/interfaces/acados_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ def __acados_export_model(self, ocp):
p = ocp.nlp[0].parameters.cx
a = ocp.nlp[0].algebraic_states.cx_start
if ocp.parameters:
for param in ocp.parameters:
if str(param.cx)[:11] == f"time_phase_":
for key in ocp.parameters:
if str(ocp.parameters[key].cx)[:11] == f"time_phase_":
raise RuntimeError("Time constraint not implemented yet with Acados.")

self.nparams = ocp.nlp[0].parameters.shape
Expand Down
Loading
Loading