Skip to content

Commit

Permalink
Merge pull request #194 from alphaville/feature/codegen/halfspace
Browse files Browse the repository at this point in the history
Support for halfspaces in python
  • Loading branch information
alphaville authored Sep 4, 2020
2 parents c264264 + 6caaf6e commit 6bc4aff
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 133 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Note: This is the main Changelog file for the Rust solver. The Changelog file fo
v0.7.1-alpha.1
--------------------- -->

## [v0.7.1-alpha.1]
## [v0.7.1]

### Added

Expand Down Expand Up @@ -179,7 +179,7 @@ This is a breaking API change.
--------------------- -->

<!-- Releases -->
[Unreleased]: https://github.com/alphaville/optimization-engine/compare/v0.7.0...master
[Unreleased]: https://github.com/alphaville/optimization-engine/compare/v0.7.1...master
[v0.7.1]: https://github.com/alphaville/optimization-engine/compare/v0.7.0...v0.7.1
[v0.7.0]: https://github.com/alphaville/optimization-engine/compare/v0.6.2...v0.7.0
[v0.6.2]: https://github.com/alphaville/optimization-engine/compare/v0.6.1-alpha.2...v0.6.2
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ homepage = "https://alphaville.github.io/optimization-engine/"
repository = "https://github.com/alphaville/optimization-engine"

# Version of this crate (SemVer)
version = "0.7.1-alpha.1"
version = "0.7.1"

edition = "2018"

Expand Down
18 changes: 11 additions & 7 deletions docs/openrust-basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ Constraints implement the namesake trait, [`Constraint`]. Implementations of [`C

| Constraint | Explanation |
|----------------------|------------------------------------------------------|
| [`Ball2`] | $U= \\{u\in\mathbb{R}^n : \Vert u-u^0\Vert_2 \leq r\\}$ |
| [`BallInf`] | $U= \\{u\in\mathbb{R}^n : \Vert u-u^0\Vert_\infty \leq r\\}$ |
| [`Rectangle`] | $U= \\{u\in\mathbb{R}^n : u_{\min} \leq u \leq u_{\max}\\}$ |
| [`NoConstraints`] | $U = \mathbb{R}^n$ |
| [`FiniteSet`] | $U = \\{u^{(1)}, u^{(2)},\ldots,u^{(N)}\\}$ |
| [`SecondOrderCone`] | $U = \\{u=(z,t), t\in\mathbb{R}, \Vert{}z{}\Vert \leq \alpha t\\}$ |
| [`Zero`] | $U = \\{0\\}$ |
| [`Ball2`] | $U {}={} \\{u\in\mathbb{R}^n : \Vert u-u^0\Vert_2 \leq r\\}$ |
| [`BallInf`] | $U {}={} \\{u\in\mathbb{R}^n : \Vert u-u^0\Vert_\infty \leq r\\}$ |
| [`Halfspace`] | $U {}={} \\{u\in\mathbb{R}^n : \langle c, u\rangle \leq b\\}$ |
| [`Hyperplane`] | $U {}={} \\{u\in\mathbb{R}^n : \langle c, u\rangle {}={} b\\}$ |
| [`Rectangle`] | $U {}={} \\{u\in\mathbb{R}^n : u_{\min} \leq u \leq u_{\max}\\}$ |
| [`NoConstraints`] | $U {}={} \mathbb{R}^n$ |
| [`FiniteSet`] | $U {}={} \\{u^{(1)}, u^{(2)},\ldots,u^{(N)}\\}$ |
| [`SecondOrderCone`] | $U {}={} \\{u=(z,t), t\in\mathbb{R}, \Vert{}z{}\Vert \leq \alpha t\\}$ |
| [`Zero`] | $U {}={} \\{0\\}$ |
| [`CartesianProduct`] | Cartesian products of any of the above |

These are the most common constraints in practice.
Expand Down Expand Up @@ -353,6 +355,8 @@ the imposition of a maximum allowed duration, the exit status will be
[`Constraint`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/trait.Constraint.html
[`Ball2`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.Ball2.html
[`BallInf`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.BallInf.html
[`Halfspace`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.Halfspace.html
[`Hyperplane`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.Hyperplane.html
[`Zero`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.Zero.html
[`FiniteSet`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.FiniteSet.html
[`CartesianProduct`]: https://docs.rs/optimization_engine/*/optimization_engine/constraints/struct.CartesianProduct.html
Expand Down
3 changes: 2 additions & 1 deletion docs/python-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ following types of constraints:
|--------------------|------------------------------------------------|
| `Ball2` | Euclidean ball: `Ball2(None, r)` creates a Euclidean ball of radius `r` centered at the origin, and `Ball2(xc, r)` is a ball centered at point `xc` (list) |
| `BallInf` | Ball of infinity norm:`BallInf(None, r)` creates an infinity-norm ball of radius `r` centered at the origin, and `BallInf(xc, r)` is an infinity ball centered at point `xc` (list) |
| `Halfspace` | A halfspace is a set of the form $\\{u \in \mathbb{R}^{n_u} {}:{} \langle c, u\rangle \leq b \\}$, for a vector $c$ and a scalar $b$. The syntax is straightforwarrd: `Halfspace(c, b)`. |
| `FiniteSet` | Finite set, $\\{u^{(1)},\ldots,u^{(m)}\\}$; the set of point is provided as a list of lists, for example, `FiniteSet([[1,2],[2,3],[4,5]])`. The commonly used set of binary numbers, $\\{0, 1\\}$, is created with `FiniteSet([[0], [1]])`. |
| `NoConstraints` | No constraints - the whole $\mathbb{R}^{n}$|
| `Rectangle` | Rectangle, $$R = \\{u \in \mathbb{R}^{n_u} {}:{} f_{\min} \leq u \leq f_{\max}\\},$$ for example, `Rectangle(fmin, fmax)` |
Expand Down Expand Up @@ -282,7 +283,7 @@ $$
C = \\{0\\}.
$$

We also need to provide a compact set $Y \subseteq C^*$; we select
We may also to provide a compact set $Y \subseteq C^*$ (if we do not, this will be computed automatically); we select

$$Y = \\{y \in \mathbb{R}^{n_1}{}:{} \Vert y \Vert_{\infty} \leq 10^{12}\\}.$$

Expand Down
9 changes: 0 additions & 9 deletions icasadi/ci/install.sh

This file was deleted.

10 changes: 0 additions & 10 deletions icasadi/ci/script.sh

This file was deleted.

9 changes: 5 additions & 4 deletions open-codegen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

Note: This is the Changelog file of `opengen` - the Python interface of OpEn

## [0.6.0a1] - 2020-07-14
## [0.6.0] - 2020-09-03

### Added
### Added

* Support for half-spaces in problem constraints
* Added checks for `segments` in `CartesianProduct`

### Changed
Expand Down Expand Up @@ -54,6 +55,6 @@ Note: This is the Changelog file of `opengen` - the Python interface of OpEn
* Fixed `lbfgs` typo


[0.6.0a1]: https://github.com/alphaville/optimization-engine/compare/opengen-0.6.0a1...opengen-0.5.0
[0.5.0]: https://github.com/alphaville/optimization-engine/compare/opengen-0.5.0...opengen-0.4.1
[0.6.0]: https://github.com/alphaville/optimization-engine/compare/opengen-v0.5.0...opengen-0.6.0
[0.5.0]: https://github.com/alphaville/optimization-engine/compare/opengen-0.4.1...opengen-v0.5.0
[0.4.1]: https://github.com/alphaville/optimization-engine/compare/opengen-0.4.1...master
2 changes: 1 addition & 1 deletion open-codegen/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.0a1
0.6.0
15 changes: 8 additions & 7 deletions open-codegen/opengen/builder/set_y_calculator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ..constraints.rectangle import Rectangle
from ..constraints.ball_inf import BallInf
from ..constraints.halfspace import Halfspace


class SetYCalculator:
Expand All @@ -16,12 +17,7 @@ def __obtain_y_with_c_rectangle(self):
c = self.__set_c
xmin = c.xmin
xmax = c.xmax
if xmin is not None:
n = len(xmin)
elif xmax is not None:
n = len(xmax)
else:
raise Exception("Fatal error: both xmin and xmax are None")
n = c.dimension()

if xmin is None:
ymin = [0.0] * n
Expand All @@ -42,8 +38,13 @@ def __obtain_y_with_c_rectangle(self):
return Rectangle(ymin, ymax)

def obtain(self):
err_msg = 'The set C you chose is not supported yet ' \
'(report at https://github.com/alphaville/optimization-engine/issues)'
if isinstance(self.__set_c, Rectangle):
return self.__obtain_y_with_c_rectangle()
if self.__set_c.is_compact():
return self.__obtain_y_with_c_compact()
raise NotImplementedError()
if isinstance(self.__set_c, Halfspace):
err_msg = 'For the time being, you cannot use a Halfspace in set C (issue #197)'

raise NotImplementedError(err_msg)
1 change: 1 addition & 0 deletions open-codegen/opengen/constraints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from .cartesian import *
from .zero import *
from .finite_set import *
from .halfspace import *
2 changes: 1 addition & 1 deletion open-codegen/opengen/constraints/ball_inf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BallInf(Constraint):
Centered inf-ball around given point
"""

def __init__(self, center=None, radius: float = 0.0):
def __init__(self, center=None, radius: float = 1.0):
"""Constructor for an infinity ball constraint
Args:
Expand Down
67 changes: 67 additions & 0 deletions open-codegen/opengen/constraints/halfspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from .constraint import Constraint
import sys


class Halfspace(Constraint):
"""Halfspace
A halfspace is a set of the form H = {c'x <= b}, where c is a given
vector and b is a constant scalar.
"""

def __init__(self, normal_vector, offset):
"""Construct a new halfspace H = {c'x <= b}
:param normal_vector: vector c
"param offset: parameter b
"""
self.__normal_vector = [float(x) for x in normal_vector]
self.__offset = offset

@property
def normal_vector(self):
"""
Returns vector c
:return:
"""
return self.__normal_vector

@property
def offset(self):
"""
Returns parameter b
:return:
"""
return self.__offset

def dimension(self):
"""
Dimension of the halfspace
:return: length of normal vector
"""
return len(self.__normal_vector)

def project(self, u):
raise NotImplementedError()

def distance_squared(self, u):
raise NotImplementedError()

def is_convex(self):
"""
A halfspace is a convex set
:return:
"""
return True

def is_compact(self):
"""Whether the set is compact
H is compact iff b < 0 and c = 0, in which case H is empty
"""
eps = sys.float_info.epsilon
# if b < 0 and c = 0, then H is empty, hence compact
if self.offset < 0 and sum([self.normal_vector[idx]
for idx in range(self.dimension())]) < eps:
return True
return False
2 changes: 2 additions & 0 deletions open-codegen/opengen/constraints/rectangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,5 @@ def is_compact(self):
for i in range(len(self.__xmax)):
if self.__xmax[i] == float('inf'):
return False

return True
9 changes: 5 additions & 4 deletions open-codegen/opengen/constraints/soc.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ def distance_squared(self, u):
:return: distance from set as a float or a CasADi symbol
"""

if isinstance(u, cs.SX):
warnings.warn("This function does not accept casadi.SX; use casadi.MX instead")
# Need to check this?:
# if isinstance(u, cs.SX):
# warnings.warn("This function does not accept casadi.SX; use casadi.MX instead")

if fn.is_symbolic(u):
nu = u.size(1)
Expand All @@ -64,9 +65,9 @@ def distance_squared(self, u):
sq_norm_x = cs.dot(x, x) # squared norm of x
gamma = (a * norm_x + r)/(a**2 + 1)

fun1 = 0
fun1 = 0.
fun2 = sq_norm_x + r ** 2
fun3 = sq_norm_x * (1 - gamma * a / norm_x)**2 + (r - gamma)**2
fun3 = sq_norm_x * (1. - gamma * a / (cs.fmax(eps, norm_x)))**2 + (r - gamma)**2

condition0 = norm_x + cs.fabs(r) < eps
condition1 = norm_x <= a*r
Expand Down
53 changes: 15 additions & 38 deletions open-codegen/opengen/main.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,28 @@
import casadi.casadi as cs
import opengen as og

u = cs.SX.sym("u", 5) # decision variable (nu = 5)
p = cs.SX.sym("p", 2) # parameter (np = 2)
phi = og.functions.rosenbrock(u, p) # cost function
u = cs.SX.sym("u", 5) # decision variable (nu = 5)
p = cs.SX.sym("p", 2) # parameter (np = 2)
phi = cs.dot(u, u) # cost function

segment_ids = [0, 1, 2, 3, 4]
cstrs = [og.constraints.NoConstraints,
og.constraints.NoConstraints,
og.constraints.Ball2(None, 1.5),
og.constraints.NoConstraints,
og.constraints.NoConstraints]
dim = 5
bounds = og.constraints.CartesianProduct(segment_ids, cstrs)
bounds = og.constraints.Halfspace([1., 2., 1., 5., 2.], -10.39)

problem = og.builder.Problem(u, p, phi) \
.with_constraints(bounds)
problem = og.builder.Problem(u, p, phi) \
.with_constraints(bounds)

meta = og.config.OptimizerMeta() \
.with_version("0.0.0") \
.with_authors(["P. Sopasakis", "E. Fresk"]) \
.with_licence("CC4.0-By") \
.with_optimizer_name("potato")
meta = og.config.OptimizerMeta() \
.with_optimizer_name("halfspace_optimizer")

build_config = og.config.BuildConfiguration() \
.with_build_directory("my_optimizers") \
.with_build_mode(og.config.BuildConfiguration.RELEASE_MODE) \
.with_open_version('0.7.0-alpha.1') \
.with_tcp_interface_config()
solver_config = og.config.SolverConfiguration() \
.with_tolerance(1e-5) \
.with_delta_tolerance(1e-4) \
.with_initial_penalty(890) \
.with_penalty_weight_update_factor(5)
tcp_config = og.config.TcpServerConfiguration(bind_port=3305)
build_config = og.config.BuildConfiguration() \
.with_build_directory('my_optimizers') \
.with_build_mode(og.config.BuildConfiguration.DEBUG_MODE) \
.with_open_version('0.7.1-alpha') \
.with_tcp_interface_config(tcp_interface_config=tcp_config)

builder = og.builder.OpEnOptimizerBuilder(problem,
meta,
build_config,
solver_config).with_verbosity_level(3).with_generate_not_build_flag(True)

og.config.SolverConfiguration())
builder.build()

o = og.tcp.OptimizerTcpManager('my_optimizers/potato')
# o.start()
# r = o.call([1.0, 50.0])
# if r.is_ok():
# status = r.get()
# print(status.solution)
# o.kill()

Loading

0 comments on commit 6bc4aff

Please sign in to comment.