Skip to content

Commit

Permalink
Added test for add_expression_to_horizon
Browse files Browse the repository at this point in the history
  • Loading branch information
S-Dafarra committed Jul 30, 2023
1 parent f8c2849 commit 6a6c8ac
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 19 deletions.
26 changes: 10 additions & 16 deletions src/hippopt/base/multiple_shooting_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,11 +754,7 @@ def add_expression_to_horizon( # TODO: add tests
input_variables_names = [var.name() for var in input_variables]
base_name = name if name is not None else str(expression)

input_function = cs.Function(
base_name, input_variables, [expression], input_variables_names, ["output"]
)

max_n = 0
max_n = 1

if "max_steps" in kwargs:
max_n = kwargs["max_steps"]
Expand All @@ -769,39 +765,37 @@ def add_expression_to_horizon( # TODO: add tests
" greater than 0"
)

variables = {}
variables_generators = []
n = max_n
all_constant = True
for var in input_variables_names:
if var not in self._flattened_variables:
raise ValueError(
"Variable " + var + " not found in the optimization variables."
)
var_tuple = self._flattened_variables[var]
var_n = var_tuple[0]
all_constant = all_constant and var_n == 1
n = var_n if 1 < var_n < n else n
n = var_n if n == 1 or 1 < var_n < n else n

# With var_tuple[1]() we get a new generator for the specific variable
variables[var] = (var_tuple[0], var_tuple[1]())

if all_constant:
n = 1
variables_generators.append(var_tuple[1]())

for i in range(n):
x_k = {name: next(variables[name][1]) for name in variables}
x_k = [next(var) for var in variables_generators]

name = base_name + "[" + str(i) + "]"

if i == 0 and not apply_to_first_elements and not all_constant:
if i == 0 and not apply_to_first_elements and not n == 1:
continue

# In the following, we add the expressions through the problem
# interface, rather than the solver interface. In this way, we can exploit
# the machinery handling the generators, and we can switch the expression
# from constraints to costs
self.get_problem().add_expression(
mode=mode, expression=input_function(x_k)["output"], name=name, **kwargs
mode=mode,
expression=cs.substitute([expression], input_variables, x_k)[0],
name=name,
**kwargs
)

def set_initial_guess(
Expand Down
5 changes: 4 additions & 1 deletion src/hippopt/base/opti_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,10 @@ def _set_initial_guess_internal(
"The guess for the field "
+ base_name
+ field.name
+ " is supposed to be a list."
+ " is supposed to be a list. "
+ "Received "
+ str(type(guess))
+ " instead."
)

if len(corresponding_value) == len(guess):
Expand Down
17 changes: 15 additions & 2 deletions test/test_multiple_shooting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import casadi as cs
import numpy as np
import pytest

from hippopt import (
ExpressionType,
MultipleShootingSolver,
Expand Down Expand Up @@ -240,24 +239,27 @@ class MassFallingTestVariables(OptimizationObject):
metadata=time_varying_metadata(), default=None
)
g: StorageType = default_storage_field(Parameter)
foo: StorageType = default_storage_field(Variable)

def __post_init__(self):
self.g = -9.81 * np.ones(1)
self.masses = []
for _ in range(3):
self.masses.append(MassFallingState())
self.foo = np.zeros((3, 1))


def test_multiple_shooting():
guess = MassFallingTestVariables()
guess.masses = None
guess.foo = None

horizon = 100
dt = 0.01
initial_position = 1.0
initial_velocity = 0

problem, var, _ = OptimalControlProblem.create(
problem, var, symbolic = OptimalControlProblem.create(
input_structure=MassFallingTestVariables(),
horizon=horizon,
)
Expand Down Expand Up @@ -292,6 +294,16 @@ def test_multiple_shooting():
x0_name="initial_condition_simple",
)

problem.add_expression_to_horizon(
expression=(symbolic.foo >= 5), apply_to_first_elements=True
)

problem.add_expression_to_horizon(
expression=cs.sumsqr(symbolic.foo),
apply_to_first_elements=True,
mode=ExpressionType.minimize,
)

problem.set_initial_guess(guess)

sol = problem.solve()
Expand All @@ -317,5 +329,6 @@ def test_multiple_shooting():
assert float(sol.values.masses[1][i].v) == pytest.approx(expected_velocity)
assert float(sol.values.masses[2][i].x) == pytest.approx(expected_position)
assert float(sol.values.masses[2][i].v) == pytest.approx(expected_velocity)
assert sol.values.foo[i] == pytest.approx(5)
expected_position += dt * expected_velocity
expected_velocity += dt * guess.g

0 comments on commit 6a6c8ac

Please sign in to comment.