Skip to content

Commit

Permalink
Code generation for AffineSpace
Browse files Browse the repository at this point in the history
About:
- Support for AffineSpace in opengen (issue #333)
- Print support for SolverStatus
- Add .jinja extension to some jinja files (issue #343)
- Change hard coding of file names in builder
- Fix typo in icasadi_lib.rs

Issues:
- Addresses #333 #343
  • Loading branch information
alphaville committed Mar 27, 2024
1 parent 0ab34f7 commit 502c610
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 9 deletions.
11 changes: 8 additions & 3 deletions open-codegen/opengen/builder/optimizer_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
_ICASADI_PREFIX = 'icasadi_'
_ROS_PREFIX = 'ros_node_'

# Template files
_OPTIMIZER_RS = "optimizer.rs.jinja"
_OPTIMIZER_CARGO_TOML = "optimizer_cargo.toml.jinja"
_OPTIMIZER_BUILD_RS = "optimizer_build.rs.jinja"


def make_dir_if_not_exists(directory):
if not os.path.exists(directory):
Expand Down Expand Up @@ -243,7 +248,7 @@ def __generate_cargo_toml(self):
self.__logger.info("Generating Cargo.toml for target optimizer")
target_dir = self.__target_dir()
cargo_template = OpEnOptimizerBuilder.__get_template(
'optimizer_cargo.toml')
_OPTIMIZER_CARGO_TOML)
cargo_output_template = cargo_template.render(
meta=self.__meta,
build_config=self.__build_config,
Expand Down Expand Up @@ -540,7 +545,7 @@ def __generate_main_project_code(self):
"Generating main code for target optimizer (lib.rs)")
target_dir = self.__target_dir()
optimizer_rs_template = OpEnOptimizerBuilder.__get_template(
'optimizer.rs')
_OPTIMIZER_RS)
optimizer_rs_output_template = optimizer_rs_template.render(
solver_config=self.__solver_config,
meta=self.__meta,
Expand All @@ -557,7 +562,7 @@ def __generate_build_rs(self):
self.__logger.info("Generating build.rs for target optimizer")
target_dir = self.__target_dir()
build_rs_template = OpEnOptimizerBuilder.__get_template(
'optimizer_build.rs')
_OPTIMIZER_BUILD_RS)
build_rs_output_template = build_rs_template.render(
meta=self.__meta,
activate_clib_generation=self.__build_config.build_c_bindings)
Expand Down
1 change: 1 addition & 0 deletions open-codegen/opengen/constraints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
from .finite_set import *
from .halfspace import *
from .simplex import *
from .affine_space import *
40 changes: 40 additions & 0 deletions open-codegen/opengen/constraints/affine_space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import casadi.casadi as cs
import numpy as np
from .constraint import Constraint
import opengen.functions as fn


class AffineSpace(Constraint):
"""An affine constraint
A constraint of the form :math:`Ax = b`, where :math:`A` and :math:`b` are
a matrix and a vector of appropriate dimensions
"""

def __init__(self, A, b):
"""Constructor for an affine space
:return: new instance of AffineSpace
"""
self.__A = A.flatten('C')
self.__b = b

@property
def matrix_a(self):
return self.__A

@property
def vector_b(self):
return self.__b

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

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

def is_convex(self):
return True

def is_compact(self):
return False
12 changes: 12 additions & 0 deletions open-codegen/opengen/tcp/solver_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,15 @@ def cost(self):
:return: Value of cost function at the solution
"""
return self.__dict__["__cost"]

def __repr__(self):
return "Solver Status Report:\n" + \
f"Exit status....... {self.exit_status}\n" + \
f"Num Outer Iters... {self.num_outer_iterations}\n" + \
f"Num Inner Iters... {self.num_inner_iterations}\n" + \
f"FPR............... {self.last_problem_norm_fpr}\n" + \
"Infeasibility\n" + \
f" L F1............ {self.f1_infeasibility}\n" + \
f" L Fw............ {self.f2_norm}\n" + \
f"Penalty............ {self.penalty}\n" + \
f"Time............... {self.solve_time_ms} ms\n"
2 changes: 1 addition & 1 deletion open-codegen/opengen/templates/icasadi/icasadi_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ pub fn precondition(

/// Computes the initial penalty
///
/// Make sure that you have called init_
/// Make sure that you have called init_{{ meta.optimizer_name }}
pub fn initial_penalty(
u: &[f64],
static_params: &[f64],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,18 @@ pub const {{meta.optimizer_name|upper}}_N1: usize = {{problem.dim_constraints_au
/// Number of penalty constraints
pub const {{meta.optimizer_name|upper}}_N2: usize = {{problem.dim_constraints_penalty() or 0}};

{% include "c/optimizer_cinterface.rs" %}
{% include "c/optimizer_cinterface.rs.jinja" %}

// ---Parameters of the constraints----------------------------------------------------------------------

{# CASE I: Ball* or Sphere #}
{% if 'Ball1' == problem.constraints.__class__.__name__ or 'Ball2' == problem.constraints.__class__.__name__ or 'BallInf' == problem.constraints.__class__.__name__ or 'Sphere2' == problem.constraints.__class__.__name__ -%}
/// Constraints: Centre of Ball
const CONSTRAINTS_BALL_XC: Option<&[f64]> = {% if problem.constraints.center is not none %}Some(&[{{problem.constraints.center | join(', ')}}]){% else %}None{% endif %};

/// Constraints: Radius of Ball
const CONSTRAINTS_BALL_RADIUS : f64 = {{problem.constraints.radius}};
{% elif 'Rectangle' == problem.constraints.__class__.__name__ -%}
{% endif %}
{# CASE II: Rectangle #}
{% if 'Rectangle' == problem.constraints.__class__.__name__ -%}
const CONSTRAINTS_XMIN :Option<&[f64]> = {% if problem.constraints.xmin is not none %}Some(&[
{%- for xmini in problem.constraints.xmin -%}
{%- if float('-inf') == xmini -%}std::f64::NEG_INFINITY{%- else -%}{{xmini}}{%- endif -%},
Expand All @@ -85,7 +86,6 @@ const CONSTRAINTS_XMAX :Option<&[f64]> = {% if problem.constraints.xmax is not n
{% endif %}



{% if problem.alm_set_c is not none %}
// ---Parameters of ALM-type constraints (Set C)---------------------------------------------------------
{% if 'Ball2' == problem.alm_set_c.__class__.__name__ or 'BallInf' == problem.alm_set_c.__class__.__name__ -%}
Expand Down Expand Up @@ -150,6 +150,10 @@ fn make_constraints() -> impl Constraint {
{% elif 'Rectangle' == problem.constraints.__class__.__name__ -%}
// - Rectangle:
Rectangle::new(CONSTRAINTS_XMIN, CONSTRAINTS_XMAX)
{% elif 'AffineSpace' == problem.constraints.__class__.__name__ -%}
let constraints_affine_a = vec![{{problem.constraints.matrix_a | join(', ')}}];
let constraints_affine_b = vec![{{problem.constraints.vector_b | join(', ')}}];
AffineSpace::new(constraints_affine_a, constraints_affine_b)
{% elif 'FiniteSet' == problem.constraints.__class__.__name__ -%}
// - Finite Set:
let data: &[&[f64]] = &[
Expand Down Expand Up @@ -192,6 +196,11 @@ fn make_constraints() -> impl Constraint {
let center_{{loop.index}}: Option<&[f64]> = {% if set_i.center is not none %}Some(&[{{set_i.center | join(', ')}}]){% else %}None{% endif %};
let set_{{loop.index}} = Sphere2::new(center_{{loop.index}}, radius_{{loop.index}});
let bounds = bounds.add_constraint(idx_{{loop.index}}, set_{{loop.index}});
{% elif 'AffineSpace' == set_i.__class__.__name__ -%}
let constraints_affine_a = vec![{{problem.constraints.matrix_a | join(', ')}}];
let constraints_affine_b = vec![{{problem.constraints.vector_b | join(', ')}}];
let set_{{loop.index}} = AffineSpace::new(constraints_affine_a, constraints_affine_b)
let bounds = bounds.add_constraint(idx_{{loop.index}}, set_{{loop.index}});
{% elif 'Simplex' == set_i.__class__.__name__ -%}
let alpha_{{loop.index}} = {{set_i.alpha}};
let set_{{loop.index}} = Simplex::new(alpha_{{loop.index}});
Expand Down

0 comments on commit 502c610

Please sign in to comment.