From c999ac241b1c7b6f43842306581a9de693a139f2 Mon Sep 17 00:00:00 2001 From: Stefano Date: Wed, 8 Mar 2023 19:31:17 +0100 Subject: [PATCH 01/12] Added first structure and first test. --- README.md | 13 ++++--- pyproject.toml | 7 ++++ setup.cfg | 5 +++ src/hippopt/__init__.py | 4 ++ src/hippopt/base/__init__.py | 1 + src/hippopt/base/optimization_object.py | 37 +++++++++++++++++++ src/hippopt/base/parameter.py | 13 +++++++ src/hippopt/base/variable.py | 13 +++++++ src/hippopt/common.py | 9 +++++ src/hippopt/integrators/__init__.py | 0 src/hippopt/multiple_shooting/__init__.py | 0 src/hippopt/multiple_shooting/builder.py | 0 .../multiple_shooting/discretized_variable.py | 0 .../robot_planning/constraints/__init__.py | 0 .../robot_planning/dynamics/__init__.py | 0 src/hippopt/robot_planning/tasks/__init__.py | 0 .../robot_planning/utilities/__init__.py | 0 .../robot_planning/variables/__init__.py | 0 src/hippopt/solver.py | 0 test/test_base.py | 27 ++++++++++++++ 20 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/hippopt/base/__init__.py create mode 100644 src/hippopt/base/optimization_object.py create mode 100644 src/hippopt/base/parameter.py create mode 100644 src/hippopt/base/variable.py create mode 100644 src/hippopt/common.py create mode 100644 src/hippopt/integrators/__init__.py create mode 100644 src/hippopt/multiple_shooting/__init__.py create mode 100644 src/hippopt/multiple_shooting/builder.py create mode 100644 src/hippopt/multiple_shooting/discretized_variable.py create mode 100644 src/hippopt/robot_planning/constraints/__init__.py create mode 100644 src/hippopt/robot_planning/dynamics/__init__.py create mode 100644 src/hippopt/robot_planning/tasks/__init__.py create mode 100644 src/hippopt/robot_planning/utilities/__init__.py create mode 100644 src/hippopt/robot_planning/variables/__init__.py create mode 100644 src/hippopt/solver.py create mode 100644 test/test_base.py diff --git a/README.md b/README.md index 5673f586..c3aa0b17 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # hippopt -### HIghly Pythonized Planning and OPTimization framework - +### HIgh Performance* Planning and OPTimization framework hippopt is an open-source framework for generating whole-body trajectories for legged robots, with a focus on direct transcription of optimal control problems solved with multiple-shooting methods. The framework takes as input the robot model and generates optimized trajectories that include both kinematic and dynamic quantities. +*supposedly + ## Features - [ ] Direct transcription of optimal control problems with multiple-shooting methods @@ -13,8 +14,11 @@ hippopt is an open-source framework for generating whole-body trajectories for l - [ ] Extensive documentation and examples to help you get started ## Installation - -TODO +It is suggested to use [``conda``](https://docs.conda.io/en/latest/). +```bash +conda install casadi pytest +pip install --no-deps -e .[all] +``` ## Citing this work @@ -41,4 +45,3 @@ This repository is maintained by: | | | | :----------------------------------------------------------: | :--------------------------------------------------: | | [](https://github.com/S-Dafarra) | [@S-Dafarra](https://github.com/S-Dafarra) | - diff --git a/pyproject.toml b/pyproject.toml index 884d3a71..0bba479c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,3 +15,10 @@ line-length = 88 [tool.isort] profile = "black" multi_line_output = 3 + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = [ + "test", +] diff --git a/setup.cfg b/setup.cfg index 1e34fae3..d75a8e16 100644 --- a/setup.cfg +++ b/setup.cfg @@ -50,13 +50,18 @@ package_dir = =src python_requires = >=3.10 install_requires = + numpy + casadi [options.extras_require] style = black isort +testing= + pytest all = %(style)s + %(testing)s [options.packages.find] where = src diff --git a/src/hippopt/__init__.py b/src/hippopt/__init__.py index e69de29b..a96bbf76 100644 --- a/src/hippopt/__init__.py +++ b/src/hippopt/__init__.py @@ -0,0 +1,4 @@ +from . import base +from .base.optimization_object import StorageType, default_storage_type, OptimizationObject +from .base.variable import Variable +from .base.parameter import Parameter diff --git a/src/hippopt/base/__init__.py b/src/hippopt/base/__init__.py new file mode 100644 index 00000000..f2a23186 --- /dev/null +++ b/src/hippopt/base/__init__.py @@ -0,0 +1 @@ +from . import optimization_object, parameter, variable diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py new file mode 100644 index 00000000..9d32d66c --- /dev/null +++ b/src/hippopt/base/optimization_object.py @@ -0,0 +1,37 @@ +from hippopt.common import abc, Any, Union, ClassVar, TypeVar, Type, copy, dataclasses, cs, np + +TOptimizationObject = TypeVar("TOptimizationObject", bound="OptimizationObject") +StorageType = Union[cs.MX, np.ndarray] + + +@dataclasses.dataclass +class OptimizationObject(abc.ABC): + ObjectType: ClassVar[str] = "optimization_object" + ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) + + def zero_copy_on_type_condition(self: TOptimizationObject, type_str: str) -> TOptimizationObject: + # Operate on a deep copy + param = copy.deepcopy(self) + param_dict = dataclasses.asdict(param) + + for field in dataclasses.fields(param): + if field.metadata.get("ObjectType", "") is not type_str: + continue + + shape = param_dict[field.name].shape + + if isinstance(param_dict[field.name], np.ndarray): + param.__setattr__(field.name, np.zeros(shape)) + elif isinstance(param_dict[field.name], (cs.MX, cs.SX)): + param.__setattr__(field.name, cs.MX.zeros(*shape)) + else: + raise TypeError(type(param_dict[field.name])) + + return param + + +def default_storage_type(input_type: Type[OptimizationObject]): + return dataclasses.field( + default=None, + metadata=input_type.ObjectTypeMetadata, + ) diff --git a/src/hippopt/base/parameter.py b/src/hippopt/base/parameter.py new file mode 100644 index 00000000..ab84db85 --- /dev/null +++ b/src/hippopt/base/parameter.py @@ -0,0 +1,13 @@ +from hippopt.common import Any, ClassVar, dataclasses +from hippopt.base.optimization_object import OptimizationObject + + +@dataclasses.dataclass +class Parameter(OptimizationObject): + """""" + + ObjectType: ClassVar[str] = "parameter" + ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) + + def zero_copy(self): + return self.zero_copy_on_type_condition(self.ObjectType) diff --git a/src/hippopt/base/variable.py b/src/hippopt/base/variable.py new file mode 100644 index 00000000..a85857dd --- /dev/null +++ b/src/hippopt/base/variable.py @@ -0,0 +1,13 @@ +from hippopt.common import Any, ClassVar, dataclasses +from hippopt.base.optimization_object import OptimizationObject + + +@dataclasses.dataclass +class Variable(OptimizationObject): + """""" + + ObjectType: ClassVar[str] = "variable" + ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) + + def zero_copy(self): + return self.zero_copy_on_type_condition(self.ObjectType) diff --git a/src/hippopt/common.py b/src/hippopt/common.py new file mode 100644 index 00000000..293c1795 --- /dev/null +++ b/src/hippopt/common.py @@ -0,0 +1,9 @@ +import abc +from typing import Any, ClassVar, Union, Type, TypeVar +import copy +import dataclasses + +import casadi as cs +import numpy as np + +__all__ = ['abc', 'Any', 'ClassVar', 'Union', 'Type', 'TypeVar', 'copy', 'dataclasses', 'cs', 'np'] diff --git a/src/hippopt/integrators/__init__.py b/src/hippopt/integrators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/multiple_shooting/__init__.py b/src/hippopt/multiple_shooting/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/multiple_shooting/builder.py b/src/hippopt/multiple_shooting/builder.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/multiple_shooting/discretized_variable.py b/src/hippopt/multiple_shooting/discretized_variable.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/robot_planning/constraints/__init__.py b/src/hippopt/robot_planning/constraints/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/robot_planning/dynamics/__init__.py b/src/hippopt/robot_planning/dynamics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/robot_planning/tasks/__init__.py b/src/hippopt/robot_planning/tasks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/robot_planning/utilities/__init__.py b/src/hippopt/robot_planning/utilities/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/robot_planning/variables/__init__.py b/src/hippopt/robot_planning/variables/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/hippopt/solver.py b/src/hippopt/solver.py new file mode 100644 index 00000000..e69de29b diff --git a/test/test_base.py b/test/test_base.py new file mode 100644 index 00000000..93fbbde4 --- /dev/null +++ b/test/test_base.py @@ -0,0 +1,27 @@ +from hippopt import Variable, Parameter, StorageType, default_storage_type +import dataclasses +import numpy as np + + +@dataclasses.dataclass +class TestVariable(Variable): + storage: StorageType = default_storage_type(Variable) + + +@dataclasses.dataclass +class TestParameter(Parameter): + storage: StorageType = default_storage_type(Parameter) + + +def test_zero_variable(): + test_var = TestVariable() + test_var.storage = np.ones(shape=3) + test_var_zero = test_var.zero_copy() + assert np.all(test_var_zero.storage == 0) + + +def test_zero_parameter(): + test_par = TestParameter() + test_par.storage = np.ones(shape=3) + test_par_zero = test_par.zero_copy() + assert np.all(test_par_zero.storage == 0) From 2aec1c6a049a198e79feef9f14d4f4838fe6e83f Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 10:59:33 +0100 Subject: [PATCH 02/12] Applied isort and black formatting. --- src/hippopt/__init__.py | 8 ++++++-- src/hippopt/base/optimization_object.py | 17 +++++++++++++++-- src/hippopt/base/parameter.py | 2 +- src/hippopt/base/variable.py | 2 +- src/hippopt/common.py | 15 +++++++++++++-- test/test_base.py | 4 +++- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/hippopt/__init__.py b/src/hippopt/__init__.py index a96bbf76..025c49fc 100644 --- a/src/hippopt/__init__.py +++ b/src/hippopt/__init__.py @@ -1,4 +1,8 @@ from . import base -from .base.optimization_object import StorageType, default_storage_type, OptimizationObject -from .base.variable import Variable +from .base.optimization_object import ( + OptimizationObject, + StorageType, + default_storage_type, +) from .base.parameter import Parameter +from .base.variable import Variable diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index 9d32d66c..eb64fc5f 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -1,4 +1,15 @@ -from hippopt.common import abc, Any, Union, ClassVar, TypeVar, Type, copy, dataclasses, cs, np +from hippopt.common import ( + Any, + ClassVar, + Type, + TypeVar, + Union, + abc, + copy, + cs, + dataclasses, + np, +) TOptimizationObject = TypeVar("TOptimizationObject", bound="OptimizationObject") StorageType = Union[cs.MX, np.ndarray] @@ -9,7 +20,9 @@ class OptimizationObject(abc.ABC): ObjectType: ClassVar[str] = "optimization_object" ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) - def zero_copy_on_type_condition(self: TOptimizationObject, type_str: str) -> TOptimizationObject: + def zero_copy_on_type_condition( + self: TOptimizationObject, type_str: str + ) -> TOptimizationObject: # Operate on a deep copy param = copy.deepcopy(self) param_dict = dataclasses.asdict(param) diff --git a/src/hippopt/base/parameter.py b/src/hippopt/base/parameter.py index ab84db85..26fba0dc 100644 --- a/src/hippopt/base/parameter.py +++ b/src/hippopt/base/parameter.py @@ -1,5 +1,5 @@ -from hippopt.common import Any, ClassVar, dataclasses from hippopt.base.optimization_object import OptimizationObject +from hippopt.common import Any, ClassVar, dataclasses @dataclasses.dataclass diff --git a/src/hippopt/base/variable.py b/src/hippopt/base/variable.py index a85857dd..c87f48eb 100644 --- a/src/hippopt/base/variable.py +++ b/src/hippopt/base/variable.py @@ -1,5 +1,5 @@ -from hippopt.common import Any, ClassVar, dataclasses from hippopt.base.optimization_object import OptimizationObject +from hippopt.common import Any, ClassVar, dataclasses @dataclasses.dataclass diff --git a/src/hippopt/common.py b/src/hippopt/common.py index 293c1795..d48c3a4a 100644 --- a/src/hippopt/common.py +++ b/src/hippopt/common.py @@ -1,9 +1,20 @@ import abc -from typing import Any, ClassVar, Union, Type, TypeVar import copy import dataclasses +from typing import Any, ClassVar, Type, TypeVar, Union import casadi as cs import numpy as np -__all__ = ['abc', 'Any', 'ClassVar', 'Union', 'Type', 'TypeVar', 'copy', 'dataclasses', 'cs', 'np'] +__all__ = [ + "abc", + "copy", + "dataclasses", + "Any", + "ClassVar", + "Type", + "TypeVar", + "Union", + "cs", + "np", +] diff --git a/test/test_base.py b/test/test_base.py index 93fbbde4..61b9f619 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -1,7 +1,9 @@ -from hippopt import Variable, Parameter, StorageType, default_storage_type import dataclasses + import numpy as np +from hippopt import Parameter, StorageType, Variable, default_storage_type + @dataclasses.dataclass class TestVariable(Variable): From 0f46ccb897893f8b24a42ef3a493d354ffbd8a9a Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 11:06:13 +0100 Subject: [PATCH 03/12] Use conda in the tests --- .github/workflows/ci_cd.yml | 68 +++++++++++-------------------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index a90fcd2c..24db234a 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -9,45 +9,8 @@ on: jobs: - package: - name: Package the project - runs-on: ubuntu-22.04 - - steps: - - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Install Python tools - run: pip install build twine - - - name: Create distributions - run: python -m build -o dist/ - - - name: Inspect dist folder - run: ls -lah dist/ - - - name: Check wheel's abi and platform tags - run: test $(find dist/ -name *-none-any.whl | wc -l) -gt 0 - - - name: Run twine check - run: twine check dist/* - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - path: dist/* - name: dist - test: name: 'Python${{ matrix.python }}@${{ matrix.os }}' - needs: package runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -62,20 +25,29 @@ jobs: steps: + - uses: actions/checkout@v2 + - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Download Python packages - uses: actions/download-artifact@v3 + - uses: conda-incubator/setup-miniconda@v2 with: - path: dist - name: dist - - - name: Install wheel - shell: bash - run: pip install dist/*.whl - - - name: Import the package - run: python -c "import hippopt" + miniforge-variant: Mambaforge + miniforge-version: latest + + - name: Dependencies + shell: bash -l {0} + run: | + mamba install casadi pytest + + - name: Install + shell: bash -l {0} + run: | + pip install --no-deps -e .[all] + + - name: Test + shell: bash -l {0} + run: | + pytest From b1f9cc18bc6ddd9ea96306237d3a40aaa12c2715 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 17:06:57 +0100 Subject: [PATCH 04/12] Added more empty files to define the code structure. --- src/hippopt/{solver.py => base/problem.py} | 0 src/hippopt/base/solver.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/hippopt/{solver.py => base/problem.py} (100%) create mode 100644 src/hippopt/base/solver.py diff --git a/src/hippopt/solver.py b/src/hippopt/base/problem.py similarity index 100% rename from src/hippopt/solver.py rename to src/hippopt/base/problem.py diff --git a/src/hippopt/base/solver.py b/src/hippopt/base/solver.py new file mode 100644 index 00000000..e69de29b From 939a5ffb10aabb3139d8aba33d92c4e898934e05 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 17:09:38 +0100 Subject: [PATCH 05/12] Added default initialization method and possibility to set it custom. Added tests on custom initialization --- src/hippopt/__init__.py | 5 +- src/hippopt/base/optimization_object.py | 53 +++++++++++-------- src/hippopt/base/parameter.py | 11 ++-- src/hippopt/base/variable.py | 11 ++-- test/test_base.py | 67 +++++++++++++++++++++++-- 5 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/hippopt/__init__.py b/src/hippopt/__init__.py index 025c49fc..db08a86a 100644 --- a/src/hippopt/__init__.py +++ b/src/hippopt/__init__.py @@ -2,7 +2,8 @@ from .base.optimization_object import ( OptimizationObject, StorageType, + TOptimizationObject, default_storage_type, ) -from .base.parameter import Parameter -from .base.variable import Variable +from .base.parameter import Parameter, TParameter +from .base.variable import TVariable, Variable diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index eb64fc5f..4b6c0364 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -17,34 +17,43 @@ @dataclasses.dataclass class OptimizationObject(abc.ABC): - ObjectType: ClassVar[str] = "optimization_object" - ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) - - def zero_copy_on_type_condition( - self: TOptimizationObject, type_str: str - ) -> TOptimizationObject: - # Operate on a deep copy - param = copy.deepcopy(self) - param_dict = dataclasses.asdict(param) - - for field in dataclasses.fields(param): - if field.metadata.get("ObjectType", "") is not type_str: + StorageType: ClassVar[str] = "generic" + StorageTypeMetadata: ClassVar[dict[str, Any]] = dict(StorageType=StorageType) + + def get_default_initialization( + self: TOptimizationObject, field_name: str + ) -> np.ndarray: + """ + Get the default initialization of a given field + It is supposed to be called only for the fields having the StorageType metadata + """ + return np.zeros(dataclasses.asdict(self)[field_name].shape) + + def get_default_initialized_object(self: TOptimizationObject) -> TOptimizationObject: + """ + :return: A copy of the object with its initial values + """ + + output = copy.deepcopy(self) + output_dict = dataclasses.asdict(output) + + for field in dataclasses.fields(output): + if "StorageType" in field.metadata: + output.__setattr__( + field.name, output.get_default_initialization(field.name) + ) continue - shape = param_dict[field.name].shape + if isinstance(output.__getattribute__(field.name), OptimizationObject): + output.__setattr__( + field.name, output.__getattribute__(field.name).get_default_initialized_object() + ) - if isinstance(param_dict[field.name], np.ndarray): - param.__setattr__(field.name, np.zeros(shape)) - elif isinstance(param_dict[field.name], (cs.MX, cs.SX)): - param.__setattr__(field.name, cs.MX.zeros(*shape)) - else: - raise TypeError(type(param_dict[field.name])) - - return param + return output def default_storage_type(input_type: Type[OptimizationObject]): return dataclasses.field( default=None, - metadata=input_type.ObjectTypeMetadata, + metadata=input_type.StorageTypeMetadata, ) diff --git a/src/hippopt/base/parameter.py b/src/hippopt/base/parameter.py index 26fba0dc..79ae6b8f 100644 --- a/src/hippopt/base/parameter.py +++ b/src/hippopt/base/parameter.py @@ -1,13 +1,12 @@ from hippopt.base.optimization_object import OptimizationObject -from hippopt.common import Any, ClassVar, dataclasses +from hippopt.common import Any, ClassVar, TypeVar, dataclasses + +TParameter = TypeVar("TParameter", bound="Parameter") @dataclasses.dataclass class Parameter(OptimizationObject): """""" - ObjectType: ClassVar[str] = "parameter" - ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) - - def zero_copy(self): - return self.zero_copy_on_type_condition(self.ObjectType) + StorageType: ClassVar[str] = "parameter" + StorageTypeMetadata: ClassVar[dict[str, Any]] = dict(StorageType=StorageType) diff --git a/src/hippopt/base/variable.py b/src/hippopt/base/variable.py index c87f48eb..082f9aa1 100644 --- a/src/hippopt/base/variable.py +++ b/src/hippopt/base/variable.py @@ -1,13 +1,12 @@ from hippopt.base.optimization_object import OptimizationObject -from hippopt.common import Any, ClassVar, dataclasses +from hippopt.common import Any, ClassVar, TypeVar, dataclasses + +TVariable = TypeVar("TVariable", bound="Variable") @dataclasses.dataclass class Variable(OptimizationObject): """""" - ObjectType: ClassVar[str] = "variable" - ObjectTypeMetadata: ClassVar[dict[str, Any]] = dict(ObjectType=ObjectType) - - def zero_copy(self): - return self.zero_copy_on_type_condition(self.ObjectType) + StorageType: ClassVar[str] = "variable" + StorageTypeMetadata: ClassVar[dict[str, Any]] = dict(StorageType=StorageType) diff --git a/test/test_base.py b/test/test_base.py index 61b9f619..50a8cdaf 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -2,28 +2,85 @@ import numpy as np -from hippopt import Parameter, StorageType, Variable, default_storage_type +from hippopt import ( + OptimizationObject, + Parameter, + StorageType, + TOptimizationObject, + Variable, + default_storage_type, +) @dataclasses.dataclass -class TestVariable(Variable): +class TestVariable(OptimizationObject): storage: StorageType = default_storage_type(Variable) @dataclasses.dataclass -class TestParameter(Parameter): +class TestParameter(OptimizationObject): storage: StorageType = default_storage_type(Parameter) def test_zero_variable(): test_var = TestVariable() test_var.storage = np.ones(shape=3) - test_var_zero = test_var.zero_copy() + test_var_zero = test_var.get_default_initialized_object() + assert test_var_zero.storage.shape == (3,) assert np.all(test_var_zero.storage == 0) def test_zero_parameter(): test_par = TestParameter() test_par.storage = np.ones(shape=3) - test_par_zero = test_par.zero_copy() + test_par_zero = test_par.get_default_initialized_object() + assert test_par_zero.storage.shape == (3,) assert np.all(test_par_zero.storage == 0) + + +@dataclasses.dataclass +class CustomInitializationVariable(OptimizationObject): + variable: StorageType = default_storage_type(Variable) + parameter: StorageType = default_storage_type(Parameter) + + def get_default_initialization( + self: TOptimizationObject, field_name: str + ) -> np.ndarray: + if field_name == "variable": + return 2 * np.ones(2) + + return OptimizationObject.get_default_initialization(self, field_name) + + +def test_custom_initialization(): + test_var = CustomInitializationVariable() + test_var.variable = np.ones(3) + test_var.parameter = np.ones(3) + test_var_init = test_var.get_default_initialized_object() + assert test_var_init.parameter.shape == (3,) + assert np.all(test_var_init.parameter == 0) + assert test_var_init.variable.shape == (2,) + assert np.all(test_var_init.variable == 2) + + +@dataclasses.dataclass +class AggregateClass(OptimizationObject): + aggregated: CustomInitializationVariable = CustomInitializationVariable() + other_parameter: StorageType = default_storage_type(Parameter) + other: str = "" + + +def test_aggregated(): + test_var = AggregateClass() + test_var.aggregated.variable = np.ones(3) + test_var.aggregated.parameter = np.ones(3) + test_var.other_parameter = np.ones(3) + test_var.other = "untouched" + test_var_init = test_var.get_default_initialized_object() + assert test_var_init.aggregated.parameter.shape == (3,) + assert np.all(test_var_init.aggregated.parameter == 0) + assert test_var_init.aggregated.variable.shape == (2,) + assert np.all(test_var_init.aggregated.variable == 2) + assert test_var_init.other_parameter.shape == (3,) + assert np.all(test_var_init.other_parameter == 0) + assert test_var_init.other == "untouched" From 5728f4dd7d0fe0933a1f79e8254080dc107fa611 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 17:12:46 +0100 Subject: [PATCH 06/12] Merged tasks and constraints folder in a more generic expressions folder. Applied black formatting. --- src/hippopt/base/optimization_object.py | 9 +++++++-- .../{constraints => expressions}/__init__.py | 0 src/hippopt/robot_planning/tasks/__init__.py | 0 test/test_base.py | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) rename src/hippopt/robot_planning/{constraints => expressions}/__init__.py (100%) delete mode 100644 src/hippopt/robot_planning/tasks/__init__.py diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index 4b6c0364..01adec8f 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -29,7 +29,9 @@ def get_default_initialization( """ return np.zeros(dataclasses.asdict(self)[field_name].shape) - def get_default_initialized_object(self: TOptimizationObject) -> TOptimizationObject: + def get_default_initialized_object( + self: TOptimizationObject, + ) -> TOptimizationObject: """ :return: A copy of the object with its initial values """ @@ -46,7 +48,10 @@ def get_default_initialized_object(self: TOptimizationObject) -> TOptimizationOb if isinstance(output.__getattribute__(field.name), OptimizationObject): output.__setattr__( - field.name, output.__getattribute__(field.name).get_default_initialized_object() + field.name, + output.__getattribute__( + field.name + ).get_default_initialized_object(), ) return output diff --git a/src/hippopt/robot_planning/constraints/__init__.py b/src/hippopt/robot_planning/expressions/__init__.py similarity index 100% rename from src/hippopt/robot_planning/constraints/__init__.py rename to src/hippopt/robot_planning/expressions/__init__.py diff --git a/src/hippopt/robot_planning/tasks/__init__.py b/src/hippopt/robot_planning/tasks/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/test_base.py b/test/test_base.py index 50a8cdaf..8e676be8 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -65,13 +65,13 @@ def test_custom_initialization(): @dataclasses.dataclass class AggregateClass(OptimizationObject): - aggregated: CustomInitializationVariable = CustomInitializationVariable() + aggregated: CustomInitializationVariable other_parameter: StorageType = default_storage_type(Parameter) other: str = "" def test_aggregated(): - test_var = AggregateClass() + test_var = AggregateClass(aggregated=CustomInitializationVariable()) test_var.aggregated.variable = np.ones(3) test_var.aggregated.parameter = np.ones(3) test_var.other_parameter = np.ones(3) From ec4ad80fd41a5d1c07fb3abf8ed00a7fa86a10c1 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 19:07:23 +0100 Subject: [PATCH 07/12] Avoid to use Union. --- setup.cfg | 2 +- src/hippopt/base/optimization_object.py | 15 ++------------- src/hippopt/common.py | 1 - 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/setup.cfg b/setup.cfg index d75a8e16..fb353f48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -50,8 +50,8 @@ package_dir = =src python_requires = >=3.10 install_requires = - numpy casadi + numpy [options.extras_require] style = diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index 01adec8f..11e4b688 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -1,18 +1,7 @@ -from hippopt.common import ( - Any, - ClassVar, - Type, - TypeVar, - Union, - abc, - copy, - cs, - dataclasses, - np, -) +from hippopt.common import Any, ClassVar, Type, TypeVar, abc, copy, cs, dataclasses, np TOptimizationObject = TypeVar("TOptimizationObject", bound="OptimizationObject") -StorageType = Union[cs.MX, np.ndarray] +StorageType = cs.MX | np.ndarray @dataclasses.dataclass diff --git a/src/hippopt/common.py b/src/hippopt/common.py index d48c3a4a..761dffe0 100644 --- a/src/hippopt/common.py +++ b/src/hippopt/common.py @@ -14,7 +14,6 @@ "ClassVar", "Type", "TypeVar", - "Union", "cs", "np", ] From d4f16bcc73b8a55cac327f15e091348efab618c8 Mon Sep 17 00:00:00 2001 From: Stefano Dafarra Date: Thu, 9 Mar 2023 19:11:17 +0100 Subject: [PATCH 08/12] Update README.md after review Co-authored-by: Silvio Traversaro --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3aa0b17..3a917313 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ hippopt is an open-source framework for generating whole-body trajectories for l ## Installation It is suggested to use [``conda``](https://docs.conda.io/en/latest/). ```bash -conda install casadi pytest +conda install -c conda-forge casadi pytest pip install --no-deps -e .[all] ``` From 0ddcd4de53483e2ec37db09b969affc6eb514b99 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 19:13:18 +0100 Subject: [PATCH 09/12] Installing also python via conda --- .github/workflows/ci_cd.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 24db234a..06b81e92 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -27,11 +27,6 @@ jobs: - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python }} - - uses: conda-incubator/setup-miniconda@v2 with: miniforge-variant: Mambaforge @@ -40,7 +35,7 @@ jobs: - name: Dependencies shell: bash -l {0} run: | - mamba install casadi pytest + mamba install python=${{ matrix.python }} casadi pytest - name: Install shell: bash -l {0} From cc780037ef274e9e8d95f28218eb2d5a1221a697 Mon Sep 17 00:00:00 2001 From: Stefano Date: Thu, 9 Mar 2023 19:35:12 +0100 Subject: [PATCH 10/12] Using post_init in test_base. --- test/test_base.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/test/test_base.py b/test/test_base.py index 8e676be8..9b390c06 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -16,15 +16,20 @@ class TestVariable(OptimizationObject): storage: StorageType = default_storage_type(Variable) + def __post_init__(self): + self.storage = np.ones(shape=3) + @dataclasses.dataclass class TestParameter(OptimizationObject): storage: StorageType = default_storage_type(Parameter) + def __post_init__(self): + self.storage = np.ones(shape=3) + def test_zero_variable(): test_var = TestVariable() - test_var.storage = np.ones(shape=3) test_var_zero = test_var.get_default_initialized_object() assert test_var_zero.storage.shape == (3,) assert np.all(test_var_zero.storage == 0) @@ -32,7 +37,6 @@ def test_zero_variable(): def test_zero_parameter(): test_par = TestParameter() - test_par.storage = np.ones(shape=3) test_par_zero = test_par.get_default_initialized_object() assert test_par_zero.storage.shape == (3,) assert np.all(test_par_zero.storage == 0) @@ -43,6 +47,10 @@ class CustomInitializationVariable(OptimizationObject): variable: StorageType = default_storage_type(Variable) parameter: StorageType = default_storage_type(Parameter) + def __post_init__(self): + self.variable = np.ones(shape=3) + self.parameter = np.ones(shape=3) + def get_default_initialization( self: TOptimizationObject, field_name: str ) -> np.ndarray: @@ -54,8 +62,6 @@ def get_default_initialization( def test_custom_initialization(): test_var = CustomInitializationVariable() - test_var.variable = np.ones(3) - test_var.parameter = np.ones(3) test_var_init = test_var.get_default_initialized_object() assert test_var_init.parameter.shape == (3,) assert np.all(test_var_init.parameter == 0) @@ -69,13 +75,14 @@ class AggregateClass(OptimizationObject): other_parameter: StorageType = default_storage_type(Parameter) other: str = "" + def __post_init__(self): + self.aggregated = CustomInitializationVariable() + self.other_parameter = np.ones(3) + self.other = "untouched" + def test_aggregated(): test_var = AggregateClass(aggregated=CustomInitializationVariable()) - test_var.aggregated.variable = np.ones(3) - test_var.aggregated.parameter = np.ones(3) - test_var.other_parameter = np.ones(3) - test_var.other = "untouched" test_var_init = test_var.get_default_initialized_object() assert test_var_init.aggregated.parameter.shape == (3,) assert np.all(test_var_init.aggregated.parameter == 0) From 09ecc8e6096016e0c62b6b8dea9822e613e27ebc Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 13 Mar 2023 10:45:30 +0100 Subject: [PATCH 11/12] Removed common.py --- src/hippopt/base/optimization_object.py | 8 +++++++- src/hippopt/base/parameter.py | 4 +++- src/hippopt/base/variable.py | 4 +++- src/hippopt/common.py | 19 ------------------- 4 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 src/hippopt/common.py diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index 11e4b688..ea410ecd 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -1,4 +1,10 @@ -from hippopt.common import Any, ClassVar, Type, TypeVar, abc, copy, cs, dataclasses, np +import abc +import copy +import dataclasses +from typing import Any, ClassVar, Type, TypeVar + +import casadi as cs +import numpy as np TOptimizationObject = TypeVar("TOptimizationObject", bound="OptimizationObject") StorageType = cs.MX | np.ndarray diff --git a/src/hippopt/base/parameter.py b/src/hippopt/base/parameter.py index 79ae6b8f..6d1db6a0 100644 --- a/src/hippopt/base/parameter.py +++ b/src/hippopt/base/parameter.py @@ -1,5 +1,7 @@ +import dataclasses +from typing import Any, ClassVar, TypeVar + from hippopt.base.optimization_object import OptimizationObject -from hippopt.common import Any, ClassVar, TypeVar, dataclasses TParameter = TypeVar("TParameter", bound="Parameter") diff --git a/src/hippopt/base/variable.py b/src/hippopt/base/variable.py index 082f9aa1..64912985 100644 --- a/src/hippopt/base/variable.py +++ b/src/hippopt/base/variable.py @@ -1,5 +1,7 @@ +import dataclasses +from typing import Any, ClassVar, TypeVar + from hippopt.base.optimization_object import OptimizationObject -from hippopt.common import Any, ClassVar, TypeVar, dataclasses TVariable = TypeVar("TVariable", bound="Variable") diff --git a/src/hippopt/common.py b/src/hippopt/common.py deleted file mode 100644 index 761dffe0..00000000 --- a/src/hippopt/common.py +++ /dev/null @@ -1,19 +0,0 @@ -import abc -import copy -import dataclasses -from typing import Any, ClassVar, Type, TypeVar, Union - -import casadi as cs -import numpy as np - -__all__ = [ - "abc", - "copy", - "dataclasses", - "Any", - "ClassVar", - "Type", - "TypeVar", - "cs", - "np", -] From 67c68b9b59852c7e6765c03344554d87d87eb4c1 Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 13 Mar 2023 10:50:07 +0100 Subject: [PATCH 12/12] Renamed default_storage_type to default_storage_field. --- src/hippopt/__init__.py | 2 +- src/hippopt/base/optimization_object.py | 4 ++-- test/test_base.py | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hippopt/__init__.py b/src/hippopt/__init__.py index db08a86a..927b4b4f 100644 --- a/src/hippopt/__init__.py +++ b/src/hippopt/__init__.py @@ -3,7 +3,7 @@ OptimizationObject, StorageType, TOptimizationObject, - default_storage_type, + default_storage_field, ) from .base.parameter import Parameter, TParameter from .base.variable import TVariable, Variable diff --git a/src/hippopt/base/optimization_object.py b/src/hippopt/base/optimization_object.py index ea410ecd..4d75da95 100644 --- a/src/hippopt/base/optimization_object.py +++ b/src/hippopt/base/optimization_object.py @@ -52,8 +52,8 @@ def get_default_initialized_object( return output -def default_storage_type(input_type: Type[OptimizationObject]): +def default_storage_field(cls: Type[OptimizationObject]): return dataclasses.field( default=None, - metadata=input_type.StorageTypeMetadata, + metadata=cls.StorageTypeMetadata, ) diff --git a/test/test_base.py b/test/test_base.py index 9b390c06..8c7dc394 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -8,13 +8,13 @@ StorageType, TOptimizationObject, Variable, - default_storage_type, + default_storage_field, ) @dataclasses.dataclass class TestVariable(OptimizationObject): - storage: StorageType = default_storage_type(Variable) + storage: StorageType = default_storage_field(cls=Variable) def __post_init__(self): self.storage = np.ones(shape=3) @@ -22,7 +22,7 @@ def __post_init__(self): @dataclasses.dataclass class TestParameter(OptimizationObject): - storage: StorageType = default_storage_type(Parameter) + storage: StorageType = default_storage_field(cls=Parameter) def __post_init__(self): self.storage = np.ones(shape=3) @@ -44,8 +44,8 @@ def test_zero_parameter(): @dataclasses.dataclass class CustomInitializationVariable(OptimizationObject): - variable: StorageType = default_storage_type(Variable) - parameter: StorageType = default_storage_type(Parameter) + variable: StorageType = default_storage_field(cls=Variable) + parameter: StorageType = default_storage_field(cls=Parameter) def __post_init__(self): self.variable = np.ones(shape=3) @@ -72,7 +72,7 @@ def test_custom_initialization(): @dataclasses.dataclass class AggregateClass(OptimizationObject): aggregated: CustomInitializationVariable - other_parameter: StorageType = default_storage_type(Parameter) + other_parameter: StorageType = default_storage_field(cls=Parameter) other: str = "" def __post_init__(self):