From 5037c5c9cda1f5849dfa941aa1911fe9fb05d890 Mon Sep 17 00:00:00 2001 From: Ryan Roussel Date: Fri, 17 Jul 2026 15:58:47 -0500 Subject: [PATCH 1/2] check last contextual variable value for nans and raise error --- .../generators/bayesian/bayesian_generator.py | 21 ++++++++++++++++++- .../generators/bayesian/test_contextual_bo.py | 17 +++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/xopt/generators/bayesian/bayesian_generator.py b/xopt/generators/bayesian/bayesian_generator.py index 2d29a754..6a4e102e 100644 --- a/xopt/generators/bayesian/bayesian_generator.py +++ b/xopt/generators/bayesian/bayesian_generator.py @@ -411,9 +411,12 @@ def generate(self, n_candidates: int): # dict to track runtimes timing_results = {} + training_data = self.get_training_data(self.data) + self._validate_contextual_variables_no_nan(training_data) + # update internal model with internal data start_time = time.perf_counter() - model = self.train_model(self.get_training_data(self.data)) + model = self.train_model(training_data) timing_results["training"] = time.perf_counter() - start_time # propose candidates given model @@ -935,6 +938,22 @@ def get_model_input_bounds(self, data: pd.DataFrame) -> Dict[str, List[float]]: return variable_bounds + def _validate_contextual_variables_no_nan(self, data: pd.DataFrame): + if data.empty: + return + + last_row = data.iloc[-1] + contextual_with_nan = [ + name + for name in self.contextual_variables + if name in data.columns and pd.isna(last_row[name]) + ] + if contextual_with_nan: + raise ValueError( + "latest row contains NaN in contextual variable columns: " + + ", ".join(contextual_with_nan) + ) + @property def _candidate_names(self): """variable names corresponding to generated candidates""" diff --git a/xopt/tests/generators/bayesian/test_contextual_bo.py b/xopt/tests/generators/bayesian/test_contextual_bo.py index ba98f994..8fae5297 100644 --- a/xopt/tests/generators/bayesian/test_contextual_bo.py +++ b/xopt/tests/generators/bayesian/test_contextual_bo.py @@ -137,6 +137,23 @@ def test_contextual_variable_bounds_from_data(self): assert np.isclose(bounds["x2"][0], 0.3 - padding) assert np.isclose(bounds["x2"][1], 0.7 + padding) + def test_contextual_variable_nan_in_last_row_raises(self): + generator = UpperConfidenceBoundGenerator(vocs=self.vocs) + data = pd.DataFrame( + { + "x1": np.linspace(0.0, 1.0, 5), + "x2": np.array([0.3, 0.4, 0.5, 0.6, np.nan]), + "y": np.ones(5), + } + ) + generator.add_data(data) + + with pytest.raises( + ValueError, + match="latest row contains NaN in contextual variable columns", + ): + generator.generate(1) + def test_contextual_variable_explicit_domain_overrides_data(self): vocs = VOCS( variables={"x1": [0, 1], "x2": ContextualVariable(domain=[0.2, 0.4])}, From 5e5159bff2b6cbb8d2150476a1112b572a3e2f4e Mon Sep 17 00:00:00 2001 From: Ryan Roussel Date: Fri, 17 Jul 2026 16:37:13 -0500 Subject: [PATCH 2/2] Update bayesian_generator.py --- xopt/generators/bayesian/bayesian_generator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xopt/generators/bayesian/bayesian_generator.py b/xopt/generators/bayesian/bayesian_generator.py index 6a4e102e..f9ced1f4 100644 --- a/xopt/generators/bayesian/bayesian_generator.py +++ b/xopt/generators/bayesian/bayesian_generator.py @@ -939,9 +939,7 @@ def get_model_input_bounds(self, data: pd.DataFrame) -> Dict[str, List[float]]: return variable_bounds def _validate_contextual_variables_no_nan(self, data: pd.DataFrame): - if data.empty: - return - + """check to make sure that the last row of data does not contain NaN in any of the contextual variable columns""" last_row = data.iloc[-1] contextual_with_nan = [ name