Skip to content

Commit

Permalink
Prepare 0.14 (#120)
Browse files Browse the repository at this point in the history
* Bump 0.14

* Update readme and changelog

* Update changelog

* Add to_discrete|continuous_space aliases

* Better names in mixint
  • Loading branch information
relf authored Dec 13, 2023
1 parent 891009d commit 5e3e94d
Show file tree
Hide file tree
Showing 15 changed files with 81 additions and 59 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
Changes
-------

Version 0.14.0 - unreleased
Version 0.15.0 - unreleased
===========================

Version 0.14.0 - 13/12/2023
===========================

* `ego`: Fix ask-and-tell interface `suggest()` method in presence of discrete variable to work
in discrete not in continuous space
A few API breaking changes:
* `EgorConfig::xtypes` not an option anymore
* `EgorSolver::new_with_xtypes()` renamed `new` as `new` with xlimits is removed, use `to_xtypes` to convert `xlimits`
* `EgorConfig::no_discrete` attribute removed, use `EgorConfig::discrete()` method
* `SurrogateBuilder::new_with_xtypes_rng` renamed `new_with_xtypes`

Version 0.13.0 - 30/11/2023
===========================

Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egobox"
version = "0.13.0"
version = "0.14.0"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
edition = "2021"
description = "A toolbox for efficient global optimization"
Expand Down Expand Up @@ -31,10 +31,10 @@ persistent-ego = ["egobox-ego/persistent"]
blas = ["ndarray/blas", "egobox-gp/blas", "egobox-moe/blas", "egobox-ego/blas"]

[dependencies]
egobox-doe = { version = "0.13.0", path = "./doe" }
egobox-gp = { version = "0.13.0", path = "./gp" }
egobox-moe = { version = "0.13.0", path = "./moe", features = ["persistent"] }
egobox-ego = { version = "0.13.0", path = "./ego", features = ["persistent"] }
egobox-doe = { version = "0.14.0", path = "./doe" }
egobox-gp = { version = "0.14.0", path = "./gp" }
egobox-moe = { version = "0.14.0", path = "./moe", features = ["persistent"] }
egobox-ego = { version = "0.14.0", path = "./ego", features = ["persistent"] }

linfa = { version = "0.7", default-features = false }

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ Depending on the sub-packages you want to use, you have to add following declara

```text
[dependencies]
egobox-doe = { version = "0.11.0" }
egobox-gp = { version = "0.11.0" }
egobox-moe = { version = "0.11.0" }
egobox-ego = { version = "0.11.0" }
egobox-doe = { version = "0.14.0" }
egobox-gp = { version = "0.14.0" }
egobox-moe = { version = "0.14.0" }
egobox-ego = { version = "0.14.0" }
```

### Features
Expand Down Expand Up @@ -86,7 +86,7 @@ Otherwise, you can choose an external BLAS/LAPACK backend available through the
Thus, for instance, to use `gp` with the Intel MKL BLAS/LAPACK backend, you could specify in your `Cargo.toml` the following features:
```text
[dependencies]
egobox-gp = { version = "0.11.0", features = ["blas", "linfa/intel-mkl-static"] }
egobox-gp = { version = "0.14.0", features = ["blas", "linfa/intel-mkl-static"] }
```
or you could run the `gp` example as follows:
``` bash
Expand Down
2 changes: 1 addition & 1 deletion doe/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egobox-doe"
version = "0.13.0"
version = "0.14.0"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
edition = "2021"
description = "A library for design of experiments"
Expand Down
8 changes: 4 additions & 4 deletions ego/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egobox-ego"
version = "0.13.0"
version = "0.14.0"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
edition = "2021"
description = "A library for efficient global optimization"
Expand All @@ -16,11 +16,11 @@ persistent = ["serde_json"]
blas = ["ndarray-linalg", "linfa/ndarray-linalg", "linfa-pls/blas"]

[dependencies]
egobox-doe = { version = "0.13.0", path = "../doe", features = [
egobox-doe = { version = "0.14.0", path = "../doe", features = [
"serializable",
] }
egobox-gp = { version = "0.13.0", path = "../gp", features = ["serializable"] }
egobox-moe = { version = "0.13.0", path = "../moe", features = [
egobox-gp = { version = "0.14.0", path = "../gp", features = ["serializable"] }
egobox-moe = { version = "0.14.0", path = "../moe", features = [
"serializable",
] }

Expand Down
1 change: 1 addition & 0 deletions ego/src/criteria/ei.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ impl InfillCriterion for ExpectedImprovement {
}
}

/// Expected Improvement infill criterion
pub const EI: ExpectedImprovement = ExpectedImprovement {};
4 changes: 3 additions & 1 deletion ego/src/criteria/wb2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl InfillCriterion for WB2Criterion {
}

/// Computes the scaling factor used to scale WB2 infill criteria.
pub fn compute_wb2s_scale(
pub(crate) fn compute_wb2s_scale(
x: &ArrayView2<f64>,
obj_model: &dyn ClusteredSurrogate,
f_min: f64,
Expand All @@ -78,7 +78,9 @@ pub fn compute_wb2s_scale(
}
}

/// WB2 infill criterion
pub const WB2: WB2Criterion = WB2Criterion(Some(1.0));
/// WB2 scaled infill criterion
pub const WB2S: WB2Criterion = WB2Criterion(None);

#[cfg(test)]
Expand Down
6 changes: 2 additions & 4 deletions ego/src/egor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ impl<O: GroupFunc, SB: SurrogateBuilder> Egor<O, SB> {
y_hist: y_data,
}
} else {
let x_data = cast_to_discrete_values(&xtypes, &x_data);
let x_data = fold_with_enum_index(&xtypes, &x_data.view());
let x_data = to_discrete_space(&xtypes, &x_data.view());
info!("History: \n{}", concatenate![Axis(1), x_data, y_data]);

let x_opt = result
Expand All @@ -222,8 +221,7 @@ impl<O: GroupFunc, SB: SurrogateBuilder> Egor<O, SB> {
.unwrap()
.to_owned()
.insert_axis(Axis(0));
let x_opt = cast_to_discrete_values(&xtypes, &x_opt);
let x_opt = fold_with_enum_index(&xtypes, &x_opt.view());
let x_opt = to_discrete_space(&xtypes, &x_opt.view());
OptimResult {
x_opt: x_opt.row(0).to_owned(),
y_opt: result.state.get_full_best_cost().unwrap().to_owned(),
Expand Down
5 changes: 2 additions & 3 deletions ego/src/egor_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,9 @@ impl<SB: SurrogateBuilder> EgorService<SB> {
y_data: &ArrayBase<impl Data<Elem = f64>, Ix2>,
) -> Array2<f64> {
let xtypes = &self.solver.config.xtypes;
let x_data = unfold_with_enum_mask(xtypes, x_data);
let x_data = to_continuous_space(xtypes, x_data);
let x = self.solver.suggest(&x_data, y_data);
let x = cast_to_discrete_values(xtypes, &x);
fold_with_enum_index(xtypes, &x).to_owned()
to_discrete_space(xtypes, &x).to_owned()
}
}

Expand Down
11 changes: 5 additions & 6 deletions ego/src/egor_solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ impl<SB: SurrogateBuilder> EgorSolver<SB> {
let xtypes = config.xtypes.clone();
EgorSolver {
config,
xlimits: unfold_xtypes_as_continuous_limits(&xtypes),
xlimits: as_continuous_limits(&xtypes),
surrogate_builder: SB::new_with_xtypes(&xtypes),
rng,
}
Expand Down Expand Up @@ -312,7 +312,7 @@ where
let doe = hstart_doe.as_ref().or(self.config.doe.as_ref());

let (y_data, x_data) = if let Some(doe) = doe {
let doe = unfold_with_enum_mask(&self.config.xtypes, doe);
let doe = to_continuous_space(&self.config.xtypes, doe);

if doe.ncols() == self.xlimits.nrows() {
// only x are specified
Expand Down Expand Up @@ -970,10 +970,9 @@ where
x: &Array2<f64>,
) -> Array2<f64> {
let params = if self.config.discrete() {
// When xtypes is specified, we have to cast x to folded space
// as EgorSolver works internally in the continuous space
let xcast = cast_to_discrete_values(&self.config.xtypes, x);
fold_with_enum_index(&self.config.xtypes, &xcast.view())
// We have to cast x to folded space as EgorSolver
// works internally in the continuous space
to_discrete_space(&self.config.xtypes, x)
} else {
x.to_owned()
};
Expand Down
2 changes: 1 addition & 1 deletion ego/src/egor_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{collections::HashMap, iter::zip};
/// Max number of retry when adding a new point. Point addition may fail
/// if new point is too close to a previous point in the growing doe used
/// to train surrogate models modeling objective and constraints functions.
pub const MAX_POINT_ADDITION_RETRY: i32 = 3;
pub(crate) const MAX_POINT_ADDITION_RETRY: i32 = 3;

/// Find best (eg minimal) cost value (y_data\[0\]) with valid constraints (y_data\[1..\] < cstr_tol).
/// y_data containing ns samples [objective, cstr_1, ... cstr_nc] is given as a matrix (ns, nc + 1)
Expand Down
56 changes: 34 additions & 22 deletions ego/src/mixint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use std::marker::PhantomData;
///
/// Each level of an enumerate gives a new continuous dimension in [0, 1].
/// Each integer dimensions are relaxed continuously.
pub fn unfold_xtypes_as_continuous_limits<F: Float>(xtypes: &[XType]) -> Array2<F> {
pub fn as_continuous_limits<F: Float>(xtypes: &[XType]) -> Array2<F> {
let mut xlimits: Vec<F> = vec![];
let mut dim = 0;
xtypes.iter().for_each(|xtype| match xtype {
Expand Down Expand Up @@ -69,7 +69,7 @@ pub fn unfold_xtypes_as_continuous_limits<F: Float>(xtypes: &[XType]) -> Array2<
/// the input x may contain the mask [..., 0, 0, 1, ...] which will be contracted in [..., 2, ...]
/// meaning the "green" value.
/// This function is the opposite of unfold_with_enum_mask().
pub fn fold_with_enum_index<F: Float>(
pub(crate) fn fold_with_enum_index<F: Float>(
xtypes: &[XType],
x: &ArrayBase<impl Data<Elem = F>, Ix2>,
) -> Array2<F> {
Expand All @@ -91,7 +91,7 @@ pub fn fold_with_enum_index<F: Float>(
}

/// Compute dimension when all variables are continuously relaxed
fn compute_unfolded_dimension(xtypes: &[XType]) -> usize {
fn compute_continuous_dim(xtypes: &[XType]) -> usize {
xtypes
.iter()
.map(|s| match s {
Expand All @@ -108,11 +108,11 @@ fn compute_unfolded_dimension(xtypes: &[XType]) -> usize {
/// For instance, if an input dimension is typed ["blue", "red", "green"] a sample/row of
/// the input x may contain [..., 2, ...] which will be expanded in [..., 0, 0, 1, ...].
/// This function is the opposite of fold_with_enum_index().
pub fn unfold_with_enum_mask(
pub(crate) fn unfold_with_enum_mask(
xtypes: &[XType],
x: &ArrayBase<impl Data<Elem = f64>, Ix2>,
) -> Array2<f64> {
let mut xunfold = Array::zeros((x.nrows(), compute_unfolded_dimension(xtypes)));
let mut xunfold = Array::zeros((x.nrows(), compute_continuous_dim(xtypes)));
let mut unfold_index = 0;
xtypes.iter().enumerate().for_each(|(i, s)| match s {
XType::Cont(_, _) | XType::Int(_, _) | XType::Ord(_) => {
Expand All @@ -138,6 +138,15 @@ pub fn unfold_with_enum_mask(
xunfold
}

/// Continuous relaxation of x given possibly discrete types
/// Alias of `unfold_with_enum_mask`
pub fn to_continuous_space(
xtypes: &[XType],
x: &ArrayBase<impl Data<Elem = f64>, Ix2>,
) -> Array2<f64> {
unfold_with_enum_mask(xtypes, x)
}

/// Find closest value to `val` in given slice `v`.
fn take_closest<F: Float>(v: &[F], val: F) -> F {
let idx = Array::from_vec(v.to_vec())
Expand Down Expand Up @@ -193,7 +202,7 @@ fn cast_to_discrete_values_mut<F: Float>(
/// For instance, if an input dimension is typed ["blue", "red", "green"] in xlimits a sample/row of
/// the input x may contain the values (or mask) [..., 0, 0, 1, ...] to specify "green" for
/// this original dimension.
pub fn cast_to_discrete_values(
pub(crate) fn cast_to_discrete_values(
xtypes: &[XType],
x: &ArrayBase<impl Data<Elem = f64>, Ix2>,
) -> Array2<f64> {
Expand All @@ -202,6 +211,15 @@ pub fn cast_to_discrete_values(
xcast
}

/// Convenient method to pass from continuous unfolded space to discrete folded space
pub fn to_discrete_space(
xtypes: &[XType],
x: &ArrayBase<impl Data<Elem = f64>, Ix2>,
) -> Array2<f64> {
let x = cast_to_discrete_values(xtypes, x);
fold_with_enum_index(xtypes, &x)
}

enum Method {
Lhs,
FullFactorial,
Expand Down Expand Up @@ -641,21 +659,18 @@ impl MixintContext {

/// Compute input dim once unfolded due to continupous relaxation
pub fn get_unfolded_dim(&self) -> usize {
compute_unfolded_dimension(&self.xtypes)
compute_continuous_dim(&self.xtypes)
}

/// Create a mixed integer LHS
pub fn create_lhs_sampling<F: Float>(
&self,
seed: Option<u64>,
) -> MixintSampling<F, Lhs<F, Xoshiro256Plus>> {
let lhs = seed.map_or(
Lhs::new(&unfold_xtypes_as_continuous_limits(&self.xtypes)),
|seed| {
let rng = Xoshiro256Plus::seed_from_u64(seed);
Lhs::new(&unfold_xtypes_as_continuous_limits(&self.xtypes)).with_rng(rng)
},
);
let lhs = seed.map_or(Lhs::new(&as_continuous_limits(&self.xtypes)), |seed| {
let rng = Xoshiro256Plus::seed_from_u64(seed);
Lhs::new(&as_continuous_limits(&self.xtypes)).with_rng(rng)
});
MixintSampling {
method: lhs,
xtypes: self.xtypes.clone(),
Expand All @@ -667,7 +682,7 @@ impl MixintContext {
/// Create a mixed integer full factorial
pub fn create_ffact_sampling<F: Float>(&self) -> MixintSampling<F, FullFactorial<F>> {
MixintSampling {
method: FullFactorial::new(&unfold_xtypes_as_continuous_limits(&self.xtypes)),
method: FullFactorial::new(&as_continuous_limits(&self.xtypes)),
xtypes: self.xtypes.clone(),
output_in_folded_space: self.work_in_folded_space,
phantom: PhantomData,
Expand All @@ -679,13 +694,10 @@ impl MixintContext {
&self,
seed: Option<u64>,
) -> MixintSampling<F, Random<F, Xoshiro256Plus>> {
let rand = seed.map_or(
Random::new(&unfold_xtypes_as_continuous_limits(&self.xtypes)),
|seed| {
let rng = Xoshiro256Plus::seed_from_u64(seed);
Random::new(&unfold_xtypes_as_continuous_limits(&self.xtypes)).with_rng(rng)
},
);
let rand = seed.map_or(Random::new(&as_continuous_limits(&self.xtypes)), |seed| {
let rng = Xoshiro256Plus::seed_from_u64(seed);
Random::new(&as_continuous_limits(&self.xtypes)).with_rng(rng)
});
MixintSampling {
method: rand,
xtypes: self.xtypes.clone(),
Expand Down
4 changes: 2 additions & 2 deletions gp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egobox-gp"
version = "0.13.0"
version = "0.14.0"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
edition = "2021"
description = "A library for gaussian process modeling"
Expand All @@ -17,7 +17,7 @@ serializable = ["serde", "linfa/serde"]
blas = ["ndarray-linalg", "linfa/ndarray-linalg", "linfa-pls/blas"]

[dependencies]
egobox-doe = { version = "0.13.0", path = "../doe" }
egobox-doe = { version = "0.14.0", path = "../doe" }

linfa = { version = "0.7", default-features = false }
linfa-pls = { version = "0.7", default-features = false }
Expand Down
6 changes: 3 additions & 3 deletions moe/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "egobox-moe"
version = "0.13.0"
version = "0.14.0"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
edition = "2021"
description = "A library for mixture of expert gaussian processes"
Expand Down Expand Up @@ -29,8 +29,8 @@ serializable = [
blas = ["ndarray-linalg", "linfa/ndarray-linalg", "linfa-pls/blas"]

[dependencies]
egobox-doe = { version = "0.13.0", path = "../doe" }
egobox-gp = { version = "0.13.0", path = "../gp" }
egobox-doe = { version = "0.14.0", path = "../doe" }
egobox-gp = { version = "0.14.0", path = "../gp" }

linfa = { version = "0.7", default-features = false }
linfa-clustering = { version = "0.7", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ python-source = "python"

[tool.poetry]
name = "egobox"
version = "0.13.0"
version = "0.14.0"
description = "Python binding for egobox EGO optimizer written in Rust"
authors = ["Rémi Lafage <remi.lafage@onera.fr>"]
packages = [{ include = "egobox", from = "python" }]
Expand Down

0 comments on commit 5e3e94d

Please sign in to comment.