From f4da390dbc94b54b8e06fbd3f1466d4562a27405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lafage?= Date: Wed, 29 Mar 2023 11:35:29 +0200 Subject: [PATCH] Fix Egor solver best iter computation (#89) * Fix best iter computation * Bump ego, egobox 0.8.2 * Fix test * Adjust test for reproducibility --- Cargo.toml | 4 ++-- ego/Cargo.toml | 2 +- ego/src/egor_solver.rs | 1 + ego/src/egor_state.rs | 23 +++++++++++++++-------- pyproject.toml | 2 +- python/egobox/tests/test_mixintegor.py | 2 +- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4fb8fae7..07d9c600 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egobox" -version = "0.8.1" +version = "0.8.2" authors = ["Rémi Lafage "] edition = "2018" description = "A toolbox for efficient global optimization" @@ -28,7 +28,7 @@ blas = ["ndarray/blas", "egobox-gp/blas", "egobox-moe/blas", "egobox-ego/blas"] egobox-doe = { version = "0.8.1", path="./doe" } egobox-gp = { version = "0.8.1", path="./gp" } egobox-moe = { version = "0.8.1", path="./moe", features=["persistent"] } -egobox-ego = { version = "0.8.1", path="./ego", features=["persistent"] } +egobox-ego = { version = "0.8.2", path="./ego", features=["persistent"] } linfa = { version = "0.6.1", default-features = false } diff --git a/ego/Cargo.toml b/ego/Cargo.toml index 960e6637..bfa4da87 100644 --- a/ego/Cargo.toml +++ b/ego/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egobox-ego" -version = "0.8.1" +version = "0.8.2" authors = ["Rémi Lafage "] edition = "2018" description = "A library for efficient global optimization" diff --git a/ego/src/egor_solver.rs b/ego/src/egor_solver.rs index 4735d1a1..9405c6ab 100644 --- a/ego/src/egor_solver.rs +++ b/ego/src/egor_solver.rs @@ -547,6 +547,7 @@ where .data((x_data, y_data)) .clusterings(clusterings) .sampling(sampling); + initial_state.doe_size = doe.nrows(); initial_state.max_iters = self.n_iter as u64; initial_state.added = doe.nrows(); initial_state.no_point_added_retries = no_point_added_retries; diff --git a/ego/src/egor_state.rs b/ego/src/egor_state.rs index e814b1ce..d7039865 100644 --- a/ego/src/egor_state.rs +++ b/ego/src/egor_state.rs @@ -152,6 +152,8 @@ pub struct EgorState { /// Optimization status pub termination_status: TerminationStatus, + /// Initial doe size + pub(crate) doe_size: usize, /// Number of added points pub(crate) added: usize, /// Previous number of added points @@ -401,6 +403,7 @@ where time: Some(instant::Duration::new(0, 0)), termination_status: TerminationStatus::NotTerminated, + doe_size: 0, added: 0, prev_added: 0, no_point_added_retries: MAX_POINT_ADDITION_RETRY, @@ -426,6 +429,7 @@ where /// /// // Simulating a new, better parameter vector /// let mut state = state.data((array![[1.0f64], [2.0f64]], array![[10.0],[5.0]])); + /// state.iter = 2; /// state.param = Some(array![2.0f64]); /// state.cost = Some(array![5.0]); /// @@ -438,7 +442,7 @@ where /// assert!(state.is_best()); /// ``` fn update(&mut self) { - // TODO: better implementation should track only track + // TODO: better implementation should only track // current and best index in data and compare just them // without finding best in data each time let data = self.data.as_ref(); @@ -449,15 +453,18 @@ where } Some((x_data, y_data)) => { let best_index = find_best_result_index(y_data, self.cstr_tol); + let best_iter = best_index.saturating_sub(self.doe_size) as u64 + 1; - let param = x_data.row(best_index).to_owned(); - std::mem::swap(&mut self.prev_best_param, &mut self.best_param); - self.best_param = Some(param); + if best_iter > self.last_best_iter { + let param = x_data.row(best_index).to_owned(); + std::mem::swap(&mut self.prev_best_param, &mut self.best_param); + self.best_param = Some(param); - let cost = y_data.row(best_index).to_owned(); - std::mem::swap(&mut self.prev_best_cost, &mut self.best_cost); - self.best_cost = Some(cost); - self.last_best_iter = self.iter; + let cost = y_data.row(best_index).to_owned(); + std::mem::swap(&mut self.prev_best_cost, &mut self.best_cost); + self.best_cost = Some(cost); + self.last_best_iter = best_iter; + } } }; } diff --git a/pyproject.toml b/pyproject.toml index 1f312993..b0c88a35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ python-source="python" [tool.poetry] name = "egobox" -version = "0.8.1" +version = "0.8.2" description = "Python binding for egobox EGO optimizer written in Rust" authors = ["Rémi Lafage "] diff --git a/python/egobox/tests/test_mixintegor.py b/python/egobox/tests/test_mixintegor.py index dbe14f96..d8113b94 100644 --- a/python/egobox/tests/test_mixintegor.py +++ b/python/egobox/tests/test_mixintegor.py @@ -18,7 +18,7 @@ class TestMixintEgx(unittest.TestCase): def test_xsinx(self): xtypes = [egx.XSpec(egx.XType(egx.XType.INT), [0.0, 25.0])] - egor = egx.Egor(xsinx, xtypes, seed=42, n_doe=7) + egor = egx.Egor(xsinx, xtypes, seed=42, n_doe=3) res = egor.minimize(n_iter=10) print(f"Optimization f={res.y_opt} at {res.x_opt}") self.assertAlmostEqual(-15.125, res.y_opt[0], delta=5e-3)