[![PyPI-Server](https://img.shields.io/pypi/v/optool.svg)](https://pypi.org/project/optool/)
[![Built Status](https://gitlab.com/ocsept/optool/badges/main/pipeline.svg)](https://gitlab.com/ocsept/optool/)
[![Coverage](https://gitlab.com/ocsept/optool/badges/main/coverage.svg)](https://gitlab.com/ocsept/optool/)
[![ReadTheDocs](https://readthedocs.org/projects/optool/badge/?version=latest)](https://ocsept.gitlab.io/optool)
# Optool - Optimization tools
Generally usable utilities related to optimization problems.
Optool is a comprehensive Python package that simplifies the formulation of numerical optimization problems by
supporting the use of units of measurements and parallel execution of optimizations.
In addition, the package includes advanced data validation capabilities and provides out of the box serialization of a
variety of well-known data types.
## Highlights
- Easy to use optimization framework built around [CasADi], allowing to specify the problem with units of measurements.
- Integrated data validation using [Pydantic].
- Additional Pydantic-compatible fields for a variety of well-known data types such as [Numpy] arrays, [Pandas] Series
and DataFrame objects, unit and quantity objects of [Pint].
- Out of the box serialization to JSON of a variety of well-known data types within data models.
- Parallelization of optimizations with convenient redirection of logging statements.
## Installation
`optool` can be installed from PyPI:
```shell
python -m pip install optool
```
## Getting Started
The main purpose of this package is to simplify the formulation of optimization problems.
Following an [example application](https://web.casadi.org/blog/nlp-scaling/) of [CasADi], which aims to find the lowest
fuel consumption of a rocket in order to reach a certain target height, the corresponding optimization problem can be
formulated as follows:
```python
import numpy as np
from optool.optimization.ode import OrdinaryDifferentialEquation, ForwardEuler
from optool.optimization.problem import OptimizationProblem
from optool.uom import Quantity
# Define general parameters
time = Quantity(np.arange(0, 101, 1), "s")
gravitational_acceleration = Quantity(9.81, "m/s²")
fuel_consumption = Quantity(0.3, "g/(N*s)")
target_height = Quantity(100.0, "km")
initial_mass = Quantity(500.0, "t")
# Setup problem
prb = OptimizationProblem.casadi("Rocket launch")
integration_method = ForwardEuler
# Decision variables
height = prb.new_variable("height", len(time), "m")
height.lower_bounds = Quantity(np.hstack([np.zeros(len(time) - 1), target_height.m]), target_height.u)
height.upper_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 2, np.inf), target_height.m]), target_height.u)
height.nominal_values = target_height
speed = prb.new_variable("speed", len(time), "m/s")
speed.lower_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 1, -np.inf)]), "m/s")
speed.upper_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 1, np.inf)]), "m/s")
speed.nominal_values = Quantity(1500.0, "m/s")
mass = prb.new_variable("mass", len(time), "kg")
mass.lower_bounds = Quantity(np.hstack([initial_mass.m, np.zeros(len(time) - 1)]), initial_mass.u)
mass.upper_bounds = Quantity(np.hstack([initial_mass.m, np.full(len(time) - 1, np.inf)]), initial_mass.u)
mass.initial_guess = initial_mass
mass.nominal_values = initial_mass
thrust = prb.new_variable("control", len(time) - 1, "N")
thrust.lower_bounds = Quantity(0.0, "N")
thrust.nominal_values = Quantity(1.5e8, "N")
# Dynamic constraints
ode = OrdinaryDifferentialEquation(name="height_dynamics",
state_variable=height.regular,
input_variable=speed.regular[:-1],
function=lambda x, u: u)
prb.add_equation(integration_method.integrate(ode, time))
ode = OrdinaryDifferentialEquation(name="velocity_dynamics",
state_variable=speed.regular,
input_variable=thrust.regular / mass.regular[:-1],
function=lambda x, u: u - gravitational_acceleration)
prb.add_equation(integration_method.integrate(ode, time))
ode = OrdinaryDifferentialEquation(name="mass_dynamics",
state_variable=mass.regular,
input_variable=thrust.regular,
function=lambda x, u: -fuel_consumption * u)
prb.add_equation(integration_method.integrate(ode, time))
# Objective
prb.objective = mass.regular[0] - mass.regular[-1]
# Solve
prb.parse('ipopt')
response = prb.solve()
for val in response.debug_info.get_details():
print(val)
response.guarantee_success()
```
As a result, the optimal control strategy is to get the rocket to a high speed as quickly as possible, using a lot of
fuel, so that it is lighter and requires less thrust.
The propulsion is cut off after less than 10 seconds so that the rocket reaches the desired altitude in glide flight.
![rocket.jpg](docs/_static/rocket.jpg)
The complete code is available [here](docs/_static/rocket_demo.py).
## Dependencies
The following libraries are necessary to run the program code.
- [CasADi] is a symbolic framework for numeric optimization implementing automatic differentiation.
- [Humanize] provides various common string-related utilities like turning a number into a fuzzy human-readable
duration (e.g. *3 minutes ago*).
- [Loguru] intends to make Python logging less painful by adding a bunch of useful functionalities that solve caveats of
the standard loggers.
- [Numpy] is the fundamental package for scientific computing with Python.
- [Pandas] provides fast, powerful, flexible and easy to use features for data analysis and manipulation.
- [Pint] allows to define, operate and manipulate physical quantities.
It allows arithmetic operations between them and conversions from and to different units.
- [Pint-pandas] provides an extension to Pandas, which allows Pandas to recognize the quantities and store them in
Pandas data frames and series.
- [Pydantic] provides extensive data validation features and serialization capabilities using Python type hints.
Extra dependencies are needed for development, which can all be installed via
```shell
pip install -r requirements.txt
```
However, all processes are automated using [Tox], which automatically installs the
required dependencies.
Hence, [Tox] is the only Python package stringently necessary.
[CasADi]: https://pypi.org/project/casadi
[Humanize]: https://pypi.org/project/humanize
[Loguru]: https://pypi.org/project/loguru
[Numpy]: https://pypi.org/project/numpy
[Pandas]: https://pypi.org/project/pandas
[Pint-pandas]: https://pypi.org/project/pint-pandas
[Pint]: https://pypi.org/project/Pint
[Pydantic]: https://pypi.org/project/pydantic
[Tox]: https://pypi.org/project/tox
Raw data
{
"_id": null,
"home_page": "",
"name": "optool",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9.10",
"maintainer_email": "",
"keywords": "Serialization,Numerical optimization,Units of measurement,JSON,Pydantic,Pint,CasADi",
"author": "Andreas Ritter",
"author_email": "anritter@idsc.mavt.ethz.ch",
"download_url": "",
"platform": "any",
"description": "[![PyPI-Server](https://img.shields.io/pypi/v/optool.svg)](https://pypi.org/project/optool/)\n[![Built Status](https://gitlab.com/ocsept/optool/badges/main/pipeline.svg)](https://gitlab.com/ocsept/optool/)\n[![Coverage](https://gitlab.com/ocsept/optool/badges/main/coverage.svg)](https://gitlab.com/ocsept/optool/)\n[![ReadTheDocs](https://readthedocs.org/projects/optool/badge/?version=latest)](https://ocsept.gitlab.io/optool)\n\n# Optool - Optimization tools\n\nGenerally usable utilities related to optimization problems.\n\nOptool is a comprehensive Python package that simplifies the formulation of numerical optimization problems by\nsupporting the use of units of measurements and parallel execution of optimizations.\nIn addition, the package includes advanced data validation capabilities and provides out of the box serialization of a\nvariety of well-known data types.\n\n## Highlights\n\n- Easy to use optimization framework built around [CasADi], allowing to specify the problem with units of measurements.\n- Integrated data validation using [Pydantic].\n- Additional Pydantic-compatible fields for a variety of well-known data types such as [Numpy] arrays, [Pandas] Series\n and DataFrame objects, unit and quantity objects of [Pint].\n- Out of the box serialization to JSON of a variety of well-known data types within data models.\n- Parallelization of optimizations with convenient redirection of logging statements.\n\n## Installation\n\n`optool` can be installed from PyPI:\n\n```shell\npython -m pip install optool\n```\n\n## Getting Started\n\nThe main purpose of this package is to simplify the formulation of optimization problems.\nFollowing an [example application](https://web.casadi.org/blog/nlp-scaling/) of [CasADi], which aims to find the lowest\nfuel consumption of a rocket in order to reach a certain target height, the corresponding optimization problem can be\nformulated as follows:\n\n```python\nimport numpy as np\n\nfrom optool.optimization.ode import OrdinaryDifferentialEquation, ForwardEuler\nfrom optool.optimization.problem import OptimizationProblem\nfrom optool.uom import Quantity\n\n# Define general parameters\ntime = Quantity(np.arange(0, 101, 1), \"s\")\ngravitational_acceleration = Quantity(9.81, \"m/s\u00b2\")\nfuel_consumption = Quantity(0.3, \"g/(N*s)\")\ntarget_height = Quantity(100.0, \"km\")\ninitial_mass = Quantity(500.0, \"t\")\n\n# Setup problem\nprb = OptimizationProblem.casadi(\"Rocket launch\")\nintegration_method = ForwardEuler\n\n# Decision variables\nheight = prb.new_variable(\"height\", len(time), \"m\")\nheight.lower_bounds = Quantity(np.hstack([np.zeros(len(time) - 1), target_height.m]), target_height.u)\nheight.upper_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 2, np.inf), target_height.m]), target_height.u)\nheight.nominal_values = target_height\n\nspeed = prb.new_variable(\"speed\", len(time), \"m/s\")\nspeed.lower_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 1, -np.inf)]), \"m/s\")\nspeed.upper_bounds = Quantity(np.hstack([0.0, np.full(len(time) - 1, np.inf)]), \"m/s\")\nspeed.nominal_values = Quantity(1500.0, \"m/s\")\n\nmass = prb.new_variable(\"mass\", len(time), \"kg\")\nmass.lower_bounds = Quantity(np.hstack([initial_mass.m, np.zeros(len(time) - 1)]), initial_mass.u)\nmass.upper_bounds = Quantity(np.hstack([initial_mass.m, np.full(len(time) - 1, np.inf)]), initial_mass.u)\nmass.initial_guess = initial_mass\nmass.nominal_values = initial_mass\n\nthrust = prb.new_variable(\"control\", len(time) - 1, \"N\")\nthrust.lower_bounds = Quantity(0.0, \"N\")\nthrust.nominal_values = Quantity(1.5e8, \"N\")\n\n# Dynamic constraints\node = OrdinaryDifferentialEquation(name=\"height_dynamics\",\n state_variable=height.regular,\n input_variable=speed.regular[:-1],\n function=lambda x, u: u)\nprb.add_equation(integration_method.integrate(ode, time))\n\node = OrdinaryDifferentialEquation(name=\"velocity_dynamics\",\n state_variable=speed.regular,\n input_variable=thrust.regular / mass.regular[:-1],\n function=lambda x, u: u - gravitational_acceleration)\nprb.add_equation(integration_method.integrate(ode, time))\n\node = OrdinaryDifferentialEquation(name=\"mass_dynamics\",\n state_variable=mass.regular,\n input_variable=thrust.regular,\n function=lambda x, u: -fuel_consumption * u)\nprb.add_equation(integration_method.integrate(ode, time))\n\n# Objective\nprb.objective = mass.regular[0] - mass.regular[-1]\n\n# Solve\nprb.parse('ipopt')\nresponse = prb.solve()\nfor val in response.debug_info.get_details():\n print(val)\nresponse.guarantee_success()\n```\n\nAs a result, the optimal control strategy is to get the rocket to a high speed as quickly as possible, using a lot of\nfuel, so that it is lighter and requires less thrust.\nThe propulsion is cut off after less than 10 seconds so that the rocket reaches the desired altitude in glide flight.\n\n![rocket.jpg](docs/_static/rocket.jpg)\n\nThe complete code is available [here](docs/_static/rocket_demo.py).\n\n## Dependencies\n\nThe following libraries are necessary to run the program code.\n\n- [CasADi] is a symbolic framework for numeric optimization implementing automatic differentiation.\n- [Humanize] provides various common string-related utilities like turning a number into a fuzzy human-readable\n duration (e.g. *3 minutes ago*).\n- [Loguru] intends to make Python logging less painful by adding a bunch of useful functionalities that solve caveats of\n the standard loggers.\n- [Numpy] is the fundamental package for scientific computing with Python.\n- [Pandas] provides fast, powerful, flexible and easy to use features for data analysis and manipulation.\n- [Pint] allows to define, operate and manipulate physical quantities.\n It allows arithmetic operations between them and conversions from and to different units.\n- [Pint-pandas] provides an extension to Pandas, which allows Pandas to recognize the quantities and store them in\n Pandas data frames and series.\n- [Pydantic] provides extensive data validation features and serialization capabilities using Python type hints.\n\nExtra dependencies are needed for development, which can all be installed via\n\n```shell\n pip install -r requirements.txt\n```\n\nHowever, all processes are automated using [Tox], which automatically installs the\nrequired dependencies.\nHence, [Tox] is the only Python package stringently necessary.\n\n\n[CasADi]: https://pypi.org/project/casadi\n\n[Humanize]: https://pypi.org/project/humanize\n\n[Loguru]: https://pypi.org/project/loguru\n\n[Numpy]: https://pypi.org/project/numpy\n\n[Pandas]: https://pypi.org/project/pandas\n\n[Pint-pandas]: https://pypi.org/project/pint-pandas\n\n[Pint]: https://pypi.org/project/Pint\n\n[Pydantic]: https://pypi.org/project/pydantic\n\n[Tox]: https://pypi.org/project/tox\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Optimization tools",
"version": "0.9.0",
"project_urls": {
"API": "https://ocsept.gitlab.io/optool/api/index.html",
"Documentation": "https://ocsept.gitlab.io/optool",
"Source": "https://gitlab.com/ocsept/optool"
},
"split_keywords": [
"serialization",
"numerical optimization",
"units of measurement",
"json",
"pydantic",
"pint",
"casadi"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "c81bedd256de58e250835f8fae1700c4bcf5476b379894a28ba2459b9b1ef22a",
"md5": "133182bf429d8e038498777fd29633d9",
"sha256": "5d55f1794ed1bdfd5daa91a020c751be19e18b62d97811e9e683422776b05f24"
},
"downloads": -1,
"filename": "optool-0.9.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "133182bf429d8e038498777fd29633d9",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9.10",
"size": 66504,
"upload_time": "2023-08-05T19:58:15",
"upload_time_iso_8601": "2023-08-05T19:58:15.842431Z",
"url": "https://files.pythonhosted.org/packages/c8/1b/edd256de58e250835f8fae1700c4bcf5476b379894a28ba2459b9b1ef22a/optool-0.9.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-08-05 19:58:15",
"github": false,
"gitlab": true,
"bitbucket": false,
"codeberg": false,
"gitlab_user": "ocsept",
"gitlab_project": "optool",
"lcname": "optool"
}