Skip to content

Commit 7af599b

Browse files
authored
Merge pull request #344 from alphaville/feature/333-affine-space-codegen
Support for affine spaces in code generation
2 parents a22bd12 + 847fbf3 commit 7af599b

File tree

13 files changed

+122
-24
lines changed

13 files changed

+122
-24
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ rpmalloc = { version = "0.2", features = [
9898
# epigraph of the squared Euclidean norm
9999
roots = "0.0.8"
100100

101-
# Least squares solver
101+
# Least squares solver (NOTE: ndarray must be version 0.15 - not 0.16)
102+
# Bug report: https://github.com/argmin-rs/modcholesky/issues/34
102103
ndarray = { version = "0.15", features = ["approx"] }
103104
modcholesky = "0.1"
104105

docs/python-interface.md

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,21 @@ following types of constraints:
7777

7878
| Constraint | Explanation |
7979
|--------------------|------------------------------------------------|
80-
| `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/np.array) |
81-
| `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/np.array) |
82-
| `Ball1` | L1 ball: `Ball(None, r)` creates an ell1-ball of radius `r` centered at the origin, and `BallInf(xc, r)` is an ell1-ball centered at point `xc` (list/np.array)|
83-
| `Sphere2` | Euclidean sphere: `Sphere2(None, r)` creates a Euclidean sphere of radius `r` centered at the origin, and `Sphere2(xc, r)` is a sphere centered at point `xc` (list/np.array) |
84-
| `Simplex` | A simplex of <em>size</em> $\alpha$ is a set of the form $\Delta_\alpha = \\{x \in \mathbb{R}^n {}:{} x_i \geq 0, \sum_i x_i = \alpha\\}$. Create one with `Simplex(alpha)`. Projections are computed using Condat's [fast projection method](https://link.springer.com/article/10.1007/s10107-015-0946-6). |
85-
| `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)`. |
86-
| `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]])`. |
87-
| `NoConstraints` | No constraints - the whole $\mathbb{R}^{n}$|
88-
| `Rectangle` | Rectangle, $$R = \\{u \in \mathbb{R}^{n_u} {}:{} f_{\min} \leq u \leq f_{\max}\\},$$ for example, `Rectangle(fmin, fmax)` |
89-
| `SecondOrderCone` | Second-order aka "ice cream" aka "Lorenz" cone |
90-
| `EpigraphSquaredNorm`| The epigraph of the squared Eucliden norm is a set of the form $X = \\{(z, t) \in \mathbb{R}^{n+1}: \Vert z \Vert \leq t\\}$. |
91-
| `CartesianProduct` | Cartesian product of any of the above. See more information below. |
80+
| `AffineSpace` | An affine space is a set of the form $\\{x\in\mathbb{R}^n {}:{} Ax = b\\}$ for a matrix $A\in \mathbb{R}^p$ and vector $b$. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.AffineSpace.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.affine_space)) |
81+
| `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/np.array) Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Ball2.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.ball2)) |
82+
| `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/np.array) Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.BallInf.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.ball_inf)) |
83+
| `Ball1` | L1 ball: `Ball(None, r)` creates an ell1-ball of radius `r` centered at the origin, and `BallInf(xc, r)` is an ell1-ball centered at point `xc` (list/np.array) Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Ball1.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.ball1)) |
84+
| `Sphere2` | Euclidean sphere: `Sphere2(None, r)` creates a Euclidean sphere of radius `r` centered at the origin, and `Sphere2(xc, r)` is a sphere centered at point `xc` (list/np.array) Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Sphere2.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.sphere2)) |
85+
| `Simplex` | A simplex of <em>size</em> $\alpha$ is a set of the form $\Delta_\alpha = \\{x \in \mathbb{R}^n {}:{} x_i \geq 0, \sum_i x_i = \alpha\\}$. Create one with `Simplex(alpha)`. Projections are computed using Condat's [fast projection method](https://link.springer.com/article/10.1007/s10107-015-0946-6). Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Simplex.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.simplex)) |
86+
| `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 straightforward: `Halfspace(c, b)`. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Halfspace.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.halfspace)) |
87+
| `Hyperplane` | A hyperplane is a set given by $H=\\{x \in \mathbb{R}^n {}:{} c^\intercal x =b \\}$. Docs: [Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Hyperplane.html) |
88+
| `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]])`. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.FiniteSet.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.finite_set)) |
89+
| `NoConstraints` | No constraints - the whole $\mathbb{R}^{n}$ Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.NoConstraints.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.no_constraints)) |
90+
| `Rectangle` | Rectangle, $$R = \\{u \in \mathbb{R}^{n_u} {}:{} f_{\min} \leq u \leq f_{\max}\\},$$ for example, `Rectangle(fmin, fmax)` Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Rectangle.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.rectangle)) |
91+
| `SecondOrderCone` | Second-order aka "ice cream" aka "Lorenz" cone. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.SecondOrderCone.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.soc)) |
92+
| `EpigraphSquaredNorm`| The epigraph of the squared Euclidean norm is a set of the form $X = \\{(z, t) \in \mathbb{R}^{n+1}: \Vert z \Vert \leq t\\}$. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.EpigraphSquaredNorm.html), Python: to be implemented) |
93+
| `CartesianProduct` | Cartesian product of any of the above. See more information below. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.CartesianProduct.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.cartesian)) |
94+
| `Zero` | The set $\\{0\\}$. Docs: ([Rust](https://docs.rs/optimization_engine/latest/optimization_engine/constraints/struct.Zero.html), [Python](https://alphaville.github.io/optimization-engine/api-dox/html/opengen.constraints.html#module-opengen.constraints.zero)) |
9295

9396

9497

@@ -424,7 +427,7 @@ Note here that the solver performed 6 outer
424427
[penalty-type iterations](https://en.wikipedia.org/wiki/Penalty_method)
425428
and 35 inner iterations overall. The infinity norm of the constraint
426429
violations is approximately $7.66\cdot 10^{-5}$ (which is below the
427-
default tolerance of $10^{-4}$. This means that the solution $u^\star$
430+
default tolerance of $10^{-4}$.) This means that the solution $u^\star$
428431
satisfies
429432

430433
<div class="math">

open-codegen/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

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

10+
## [0.9.0] - 2024-08-15
11+
12+
### Added
13+
14+
* Support for affine spaces in code generation (see `opengen.constraints.AffineSpace`)
15+
* TCP solver status can be printed
16+
17+
18+
1019
## [0.8.1] - 2024-07-17
1120

1221
### Added
@@ -193,6 +202,8 @@ Note: This is the Changelog file of `opengen` - the Python interface of OpEn
193202
* Project-specific `tcp_iface` TCP interface
194203
* Fixed `lbfgs` typo
195204

205+
[0.9.0]: https://github.com/alphaville/optimization-engine/compare/opengen-0.8.1...opengen-0.9.0
206+
[0.8.1]: https://github.com/alphaville/optimization-engine/compare/v0.9.0...opengen-0.8.1
196207
[0.8.1]: https://github.com/alphaville/optimization-engine/compare/v0.9.0...opengen-0.8.1
197208
[0.8.0]: https://github.com/alphaville/optimization-engine/compare/opengen-0.7.1...opengen-0.8.0
198209
[0.7.1]: https://github.com/alphaville/optimization-engine/compare/opengen-0.7.0...opengen-0.7.1

open-codegen/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.8.1
1+
0.9.0

open-codegen/opengen/builder/optimizer_builder.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
_ICASADI_PREFIX = 'icasadi_'
2727
_ROS_PREFIX = 'ros_node_'
2828

29+
# Template files
30+
_OPTIMIZER_RS = "optimizer.rs.jinja"
31+
_OPTIMIZER_CARGO_TOML = "optimizer_cargo.toml.jinja"
32+
_OPTIMIZER_BUILD_RS = "optimizer_build.rs.jinja"
33+
2934

3035
def make_dir_if_not_exists(directory):
3136
if not os.path.exists(directory):
@@ -243,7 +248,7 @@ def __generate_cargo_toml(self):
243248
self.__logger.info("Generating Cargo.toml for target optimizer")
244249
target_dir = self.__target_dir()
245250
cargo_template = OpEnOptimizerBuilder.__get_template(
246-
'optimizer_cargo.toml')
251+
_OPTIMIZER_CARGO_TOML)
247252
cargo_output_template = cargo_template.render(
248253
meta=self.__meta,
249254
build_config=self.__build_config,
@@ -540,7 +545,7 @@ def __generate_main_project_code(self):
540545
"Generating main code for target optimizer (lib.rs)")
541546
target_dir = self.__target_dir()
542547
optimizer_rs_template = OpEnOptimizerBuilder.__get_template(
543-
'optimizer.rs')
548+
_OPTIMIZER_RS)
544549
optimizer_rs_output_template = optimizer_rs_template.render(
545550
solver_config=self.__solver_config,
546551
meta=self.__meta,
@@ -557,7 +562,7 @@ def __generate_build_rs(self):
557562
self.__logger.info("Generating build.rs for target optimizer")
558563
target_dir = self.__target_dir()
559564
build_rs_template = OpEnOptimizerBuilder.__get_template(
560-
'optimizer_build.rs')
565+
_OPTIMIZER_BUILD_RS)
561566
build_rs_output_template = build_rs_template.render(
562567
meta=self.__meta,
563568
activate_clib_generation=self.__build_config.build_c_bindings)

open-codegen/opengen/constraints/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
from .finite_set import *
1212
from .halfspace import *
1313
from .simplex import *
14+
from .affine_space import *
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import casadi.casadi as cs
2+
import numpy as np
3+
from .constraint import Constraint
4+
import opengen.functions as fn
5+
6+
7+
class AffineSpace(Constraint):
8+
"""An affine constraint
9+
10+
A constraint of the form :math:`Ax = b`, where :math:`A` and :math:`b` are
11+
a matrix and a vector of appropriate dimensions
12+
"""
13+
14+
def __init__(self, A, b):
15+
"""Constructor for an affine space
16+
17+
:return: new instance of AffineSpace
18+
"""
19+
self.__A = A.flatten('C')
20+
self.__b = b
21+
22+
@property
23+
def matrix_a(self):
24+
"""Matrix A
25+
"""
26+
return self.__A
27+
28+
@property
29+
def vector_b(self):
30+
"""Vector b
31+
"""
32+
return self.__b
33+
34+
def distance_squared(self, u):
35+
"""Squared distance to affine space
36+
37+
Not implemented yet
38+
"""
39+
raise NotImplementedError()
40+
41+
def project(self, u):
42+
"""Projection on affine space
43+
44+
Not implemented yet
45+
"""
46+
raise NotImplementedError()
47+
48+
def is_convex(self):
49+
"""Affine spaces are convex sets
50+
"""
51+
return True
52+
53+
def is_compact(self):
54+
"""Affine spaces are not compact sets
55+
"""
56+
return False

open-codegen/opengen/tcp/solver_status.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,15 @@ def cost(self):
115115
:return: Value of cost function at the solution
116116
"""
117117
return self.__dict__["__cost"]
118+
119+
def __repr__(self):
120+
return "Solver Status Report:\n" + \
121+
f"Exit status....... {self.exit_status}\n" + \
122+
f"Num Outer Iters... {self.num_outer_iterations}\n" + \
123+
f"Num Inner Iters... {self.num_inner_iterations}\n" + \
124+
f"FPR............... {self.last_problem_norm_fpr}\n" + \
125+
"Infeasibility\n" + \
126+
f" L F1............ {self.f1_infeasibility}\n" + \
127+
f" L Fw............ {self.f2_norm}\n" + \
128+
f"Penalty............ {self.penalty}\n" + \
129+
f"Time............... {self.solve_time_ms} ms\n"
File renamed without changes.

open-codegen/opengen/templates/icasadi/icasadi_lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ pub fn precondition(
268268

269269
/// Computes the initial penalty
270270
///
271-
/// Make sure that you have called init_
271+
/// Make sure that you have called init_{{ meta.optimizer_name }}
272272
pub fn initial_penalty(
273273
u: &[f64],
274274
static_params: &[f64],

0 commit comments

Comments
 (0)