From 558bf63d863421c8a1778f5b2d39f1b28988a8f9 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 08:55:45 +0200 Subject: [PATCH 01/24] Shorten docstrings to avoid cutoff --- baybe/parameters/numerical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baybe/parameters/numerical.py b/baybe/parameters/numerical.py index 5c6c34117..950821c98 100644 --- a/baybe/parameters/numerical.py +++ b/baybe/parameters/numerical.py @@ -18,7 +18,7 @@ @define(frozen=True, slots=False) class NumericalDiscreteParameter(DiscreteParameter): - """Parameter class for discrete numerical parameters (a.k.a. setpoints).""" + """Class for discrete numerical parameters (a.k.a. setpoints).""" # class variables is_numerical: ClassVar[bool] = True @@ -100,7 +100,7 @@ def is_in_range(self, item: float) -> bool: # noqa: D102 @define(frozen=True, slots=False) class NumericalContinuousParameter(ContinuousParameter): - """Parameter class for continuous numerical parameters.""" + """Class for continuous numerical parameters.""" # class variables is_numerical: ClassVar[bool] = True From e576e663d0c4ea3d63620602f3b0673307bc6d02 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 10:14:57 +0200 Subject: [PATCH 02/24] Remove bold strings since they mess up output & fix campaign output --- baybe/campaign.py | 7 ++----- baybe/objectives/desirability.py | 11 ++++------- baybe/objectives/single.py | 9 +++------ baybe/searchspace/continuous.py | 13 +++++-------- baybe/searchspace/core.py | 6 ++---- baybe/searchspace/discrete.py | 27 ++++++++++++--------------- 6 files changed, 28 insertions(+), 45 deletions(-) diff --git a/baybe/campaign.py b/baybe/campaign.py index e7e93839f..07184489d 100644 --- a/baybe/campaign.py +++ b/baybe/campaign.py @@ -84,16 +84,13 @@ class Campaign(SerialMixin): """The cached recommendations.""" def __str__(self) -> str: - start_bold = "\033[1m" - end_bold = "\033[0m" - # Get str representation of campaign fields fields_to_print = [self.searchspace, self.objective, self.recommender] fields_str = "\n\n".join(str(x) for x in fields_to_print) # Put all relevant attributes of the campaign in one string - campaign_str = f"""{start_bold}Campaign{end_bold} - \n{start_bold}Meta Data{end_bold}\nBatches Done: {self.n_batches_done} + campaign_str = f"""Campaign + \nMeta Data\nBatches Done: {self.n_batches_done} \rFits Done: {self.n_fits_done}\n\n{fields_str}\n""" return campaign_str.replace("\n", "\n ").replace("\r", "\r ") diff --git a/baybe/objectives/desirability.py b/baybe/objectives/desirability.py index 1346a6f4e..bbc71cf1c 100644 --- a/baybe/objectives/desirability.py +++ b/baybe/objectives/desirability.py @@ -122,17 +122,14 @@ def _normalized_weights(self) -> np.ndarray: return np.asarray(self.weights) / np.sum(self.weights) def __str__(self) -> str: - start_bold = "\033[1m" - end_bold = "\033[0m" - targets_list = [target.summary() for target in self.targets] targets_df = pd.DataFrame(targets_list) targets_df["Weight"] = self.weights - objective_str = f"""{start_bold}Objective{end_bold} - \n{start_bold}Type: {end_bold}{self.__class__.__name__} - \n{start_bold}Targets {end_bold}\n{targets_df} - \n{start_bold}Scalarizer: {end_bold}{self.scalarizer.name}""" + objective_str = f"""Objective + \nType: {self.__class__.__name__} + \nTargets \n{targets_df} + \nScalarizer: {self.scalarizer.name}""" return objective_str.replace("\n", "\n ") diff --git a/baybe/objectives/single.py b/baybe/objectives/single.py index 2e91f2343..bf51c93b8 100644 --- a/baybe/objectives/single.py +++ b/baybe/objectives/single.py @@ -16,15 +16,12 @@ class SingleTargetObjective(Objective): """The single target considered by the objective.""" def __str__(self) -> str: - start_bold = "\033[1m" - end_bold = "\033[0m" - targets_list = [target.summary() for target in self.targets] targets_df = pd.DataFrame(targets_list) - objective_str = f"""{start_bold}Objective{end_bold} - \n{start_bold}Type: {end_bold}{self.__class__.__name__} - \n{start_bold}Targets {end_bold}\n{targets_df}""" + objective_str = f"""Objective + \nType: {self.__class__.__name__} + \nTargets \n{targets_df}""" return objective_str.replace("\n", "\n ") diff --git a/baybe/searchspace/continuous.py b/baybe/searchspace/continuous.py index 7b898810a..e0213cff3 100644 --- a/baybe/searchspace/continuous.py +++ b/baybe/searchspace/continuous.py @@ -71,9 +71,6 @@ def __str__(self) -> str: if self.is_empty: return "" - start_bold = "\033[1m" - end_bold = "\033[0m" - # Convert the lists to dataFrames to be able to use pretty_printing param_list = [param.summary() for param in self.parameters] eq_constraints_list = [constr.summary() for constr in self.constraints_lin_eq] @@ -89,13 +86,13 @@ def __str__(self) -> str: nonlinear_constr_df = pd.DataFrame(nonlin_constraints_list) # Put all attributes of the continuous class in one string - continuous_str = f"""{start_bold}Continuous Search Space{end_bold} - \n{start_bold}Continuous Parameters{end_bold}\n{pretty_print_df(param_df)} - \n{start_bold}List of Linear Equality Constraints{end_bold} + continuous_str = f"""Continuous Search Space + \nContinuous Parameters\n{pretty_print_df(param_df)} + \nList of Linear Equality Constraints \r{pretty_print_df(lin_eq_constr_df)} - \n{start_bold}List of Linear Inequality Constraints{end_bold} + \nList of Linear Inequality Constraints \r{pretty_print_df(lin_ineq_constr_df)} - \n{start_bold}List of Nonlinear Constraints{end_bold} + \nList of Nonlinear Constraints \r{pretty_print_df(nonlinear_constr_df)}""" return continuous_str.replace("\n", "\n ").replace("\r", "\r ") diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index 23af0188f..341cd71db 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -66,10 +66,8 @@ class SearchSpace(SerialMixin): """The (potentially empty) continuous subspace of the overall search space.""" def __str__(self) -> str: - start_bold = "\033[1m" - end_bold = "\033[0m" - head_str = f"""{start_bold}Search Space{end_bold} - \n{start_bold}Search Space Type: {end_bold}{self.type.name}""" + head_str = f"""Search Space + \nSearch Space Type: {self.type.name}""" # Check the sub space size to avoid adding unwanted break lines # if the sub space is empty diff --git a/baybe/searchspace/discrete.py b/baybe/searchspace/discrete.py index 41797702e..d7b582a02 100644 --- a/baybe/searchspace/discrete.py +++ b/baybe/searchspace/discrete.py @@ -120,9 +120,6 @@ def __str__(self) -> str: if self.is_empty: return "" - start_bold = "\033[1m" - end_bold = "\033[0m" - # Convert the lists to dataFrames to be able to use pretty_printing param_list = [param.summary() for param in self.parameters] constraints_list = [constr.summary() for constr in self.constraints] @@ -136,18 +133,18 @@ def __str__(self) -> str: metadata_count = len(self.metadata) # Put all attributes of the discrete class in one string. - discrete_str = f"""{start_bold}Discrete Search Space{end_bold} - \n{start_bold}Discrete Parameters{end_bold}\n{pretty_print_df(param_df)} - \n{start_bold}Experimental Representation{end_bold} - \r{pretty_print_df(self.exp_rep)}\n\n{start_bold}Metadata:{end_bold} - \r{_METADATA_COLUMNS[0]}: {was_recommended_count}/{metadata_count} - \r{_METADATA_COLUMNS[1]}: {was_measured_count}/{metadata_count} - \r{_METADATA_COLUMNS[2]}: {dont_recommend_count}/{metadata_count} - \n{start_bold}Constraints{end_bold}\n{pretty_print_df(constraints_df)} - \n{start_bold}Computational Representation{end_bold} - \r{pretty_print_df(self.comp_rep)}""" - - return discrete_str.replace("\n", "\n ").replace("\r", "\r ") + discrete_str = f"""Discrete Search Space + \nDiscrete Parameters\n{pretty_print_df(param_df)} + \nExperimental Representation + \n{pretty_print_df(self.exp_rep)}\n\nMetadata: + {_METADATA_COLUMNS[0]}: {was_recommended_count}/{metadata_count} + {_METADATA_COLUMNS[1]}: {was_measured_count}/{metadata_count} + {_METADATA_COLUMNS[2]}: {dont_recommend_count}/{metadata_count} + \nConstraints\n{pretty_print_df(constraints_df)} + \nComputational Representation + \n{pretty_print_df(self.comp_rep)}""" + + return discrete_str.replace("\n", "\n ") @exp_rep.validator def _validate_exp_rep( # noqa: DOC101, DOC103 From c33114e0f9771e14ab96f97ef40d7dacc9d149c1 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 10:51:00 +0200 Subject: [PATCH 03/24] Add utility for indentation in printing --- baybe/utils/plotting.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 9a8697665..5c321f2f9 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -140,3 +140,9 @@ def create_example_plots( else: warnings.warn("Plots could not be saved.") plt.close() + + +def indent(text: str, amount: int = 3, ch: str = " ") -> str: + """Indent a given text by a certain amount.""" + padding = amount * ch + return "".join(padding + line for line in text.splitlines(True)) From 91d8b32a54dbd47fcf13f285fc5e92ba1d43d247 Mon Sep 17 00:00:00 2001 From: AdrianSosic Date: Mon, 26 Aug 2024 13:36:01 +0200 Subject: [PATCH 04/24] Specifying boolean flag by using keyword --- baybe/utils/plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 5c321f2f9..4f6c8fe1f 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -145,4 +145,4 @@ def create_example_plots( def indent(text: str, amount: int = 3, ch: str = " ") -> str: """Indent a given text by a certain amount.""" padding = amount * ch - return "".join(padding + line for line in text.splitlines(True)) + return "".join(padding + line for line in text.splitlines(keepends=True)) From 28d88ad9a877fc964021598f04686e931744d806 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Mon, 2 Sep 2024 13:20:35 +0200 Subject: [PATCH 05/24] Add utility for genereating str representations --- baybe/utils/plotting.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 4f6c8fe1f..c6e15499d 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -146,3 +146,19 @@ def indent(text: str, amount: int = 3, ch: str = " ") -> str: """Indent a given text by a certain amount.""" padding = amount * ch return "".join(padding + line for line in text.splitlines(keepends=True)) + + +def create_str_representation(header: str, fields: list[any]) -> str: + """Create the nested `str`representation that is used in the `__str__` methods. + + Args: + header: The header, typically the name of the class. + fields: List of fields that should be printed with an indentation. + + Returns: + The representation with indented fields. + """ + # Add a ":" to header if it does not end with a "." + if not header.endswith("."): + header += ":" + return "\n".join([header] + [indent(str(f)) for f in fields]) From 1bbd43ca30590f7200da04896ea8898db3e1d8b1 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 3 Sep 2024 16:35:59 +0200 Subject: [PATCH 06/24] Add option for single line string representation --- baybe/utils/plotting.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index c6e15499d..04a5e9b2e 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -148,17 +148,34 @@ def indent(text: str, amount: int = 3, ch: str = " ") -> str: return "".join(padding + line for line in text.splitlines(keepends=True)) -def create_str_representation(header: str, fields: list[any]) -> str: +def create_str_representation( + header: str, fields: list[any], *, single_line: bool = False +) -> str: """Create the nested `str`representation that is used in the `__str__` methods. Args: header: The header, typically the name of the class. fields: List of fields that should be printed with an indentation. + single_line: If ``True``, print the representation on a single line. Used for + simple fields that would require manual configuration otherwise. Only + applicable for lists with a single fields. + + Raises: + ValueError: If ``single_line`` is ``True`` but ``fields`` contains more than one + element. Returns: The representation with indented fields. """ - # Add a ":" to header if it does not end with a "." - if not header.endswith("."): + # Add a ":" to header if it does not end with a ":" + if not header.endswith(":"): header += ":" + + if single_line: + if len(fields) > 1: + raise ValueError( + "single_line is only applicable for lists with a single field" + ) + return f"{header} {str(fields[0])}" + return "\n".join([header] + [indent(str(f)) for f in fields]) From 4586c59a37fff0aa52c0a73aa91612bb06e565d2 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 16:55:57 +0200 Subject: [PATCH 07/24] Enhance pretty printing function for dataframes --- baybe/utils/dataframe.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/baybe/utils/dataframe.py b/baybe/utils/dataframe.py index 7eec8c38d..80d3c1c4a 100644 --- a/baybe/utils/dataframe.py +++ b/baybe/utils/dataframe.py @@ -449,7 +449,13 @@ def fuzzy_row_match( return pd.Index(inds_matched) -def pretty_print_df(df: pd.DataFrame, max_rows: int = 6, max_columns: int = 4) -> str: +def pretty_print_df( + df: pd.DataFrame, + max_rows: int = 6, + max_columns: int = 4, + max_colwidth: int = 16, + precision: int = 3, +) -> str: """Convert a dataframe into a pretty/readable format. This function returns a customized str representation of the dataframe. @@ -459,6 +465,8 @@ def pretty_print_df(df: pd.DataFrame, max_rows: int = 6, max_columns: int = 4) - df: The dataframe to be printed. max_rows: Maximum number of rows to display. max_columns: Maximum number of columns to display. + max_colwidth: Maximum width of an individual column. + precision: Number of digits to which numbers should be rounded. Returns: The values to be printed as a str table. @@ -469,8 +477,19 @@ def pretty_print_df(df: pd.DataFrame, max_rows: int = 6, max_columns: int = 4) - max_rows, "display.max_columns", max_columns, + "display.max_colwidth", + max_colwidth, + "display.precision", + precision, "expand_frame_repr", False, ): - str_df = str(df) + # Pandas does not truncate the names of columns with long names, which makes + # computational representations barely readable in some of the examples. Hence, + # we truncate them manually. For details, see + # https://stackoverflow.com/questions/64976267/pandas-truncate-column-names) + str_df = df.rename( + columns=lambda x: x[:max_colwidth], + ) + str_df = str(str_df) return str_df From f0171aae3639759ff7cb7b07d8177ba772348bad Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 15:52:14 +0200 Subject: [PATCH 08/24] Improve visuals in examples --- examples/Basics/recommenders.py | 7 +++++-- examples/Constraints_Continuous/hybrid_space.py | 8 ++++---- .../Constraints_Continuous/linear_constraints.py | 16 ++++++++-------- .../continuous_space_botorch_function.py | 4 ++-- examples/Serialization/basic_serialization.py | 1 + 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/Basics/recommenders.py b/examples/Basics/recommenders.py index 3d541a1ad..4759041df 100644 --- a/examples/Basics/recommenders.py +++ b/examples/Basics/recommenders.py @@ -45,6 +45,7 @@ # Per default the initial recommender chosen is a random recommender. + INITIAL_RECOMMENDER = RandomRecommender() ### Available surrogate models @@ -52,8 +53,10 @@ # This model uses available data to model the objective function as well as the uncertainty. # The surrogate model is then used by the acquisition function to make recommendations. -# The following are the available basic surrogates -print(get_subclasses(Surrogate)) +# The following are the available basic surrogates: + +for subclass in get_subclasses(Surrogate): + print(subclass) # Per default a Gaussian Process is used # You can change the used kernel by using the optional `kernel` keyword. diff --git a/examples/Constraints_Continuous/hybrid_space.py b/examples/Constraints_Continuous/hybrid_space.py index 7cde1128c..ea6d26c3a 100644 --- a/examples/Constraints_Continuous/hybrid_space.py +++ b/examples/Constraints_Continuous/hybrid_space.py @@ -74,8 +74,8 @@ ] # We model the following constraints: -# `1.0*x_1 + 1.0*x_2 = 1.0` -# `1.0*x_3 - 1.0*x_4 = 2.0` +# - $1.0*x_1 + 1.0*x_2 = 1.0$ +# - $1.0*x_3 - 1.0*x_4 = 2.0$ constraints = [ DiscreteSumConstraint( @@ -118,7 +118,7 @@ measurements = campaign.measurements TOLERANCE = 0.01 -# `1.0*x_1 + 1.0*x_2 = 1.0` +# $1.0*x_1 + 1.0*x_2 = 1.0$ print( "1.0*x_1 + 1.0*x_2 = 1.0 satisfied in all recommendations? ", @@ -127,7 +127,7 @@ ), ) -# `1.0*x_3 - 1.0*x_4 = 2.0` +# $1.0*x_3 - 1.0*x_4 = 2.0$ print( "1.0*x_3 - 1.0*x_4 = 2.0 satisfied in all recommendations? ", diff --git a/examples/Constraints_Continuous/linear_constraints.py b/examples/Constraints_Continuous/linear_constraints.py index def3bc6d9..85fb7f54c 100644 --- a/examples/Constraints_Continuous/linear_constraints.py +++ b/examples/Constraints_Continuous/linear_constraints.py @@ -58,10 +58,10 @@ ] # We model the following constraints: -# `1.0*x_1 + 1.0*x_2 = 1.0` -# `1.0*x_3 - 1.0*x_4 = 2.0` -# `1.0*x_1 + 1.0*x_3 >= 1.0` -# `2.0*x_2 + 3.0*x_4 <= 1.0` which is equivalent to `-2.0*x_2 - 3.0*x_4 >= -1.0` +# 1. $1.0*x_1 + 1.0*x_2 = 1.0$ +# 2. $1.0*x_3 - 1.0*x_4 = 2.0$ +# 3. $1.0*x_1 + 1.0*x_3 >= 1.0$ +# 4. $2.0*x_2 + 3.0*x_4 <= 1.0$ which is equivalent to $-2.0*x_2 - 3.0*x_4 >= -1.0$ constraints = [ ContinuousLinearEqualityConstraint( @@ -112,7 +112,7 @@ measurements = campaign.measurements TOLERANCE = 0.01 -# `1.0*x_1 + 1.0*x_2 = 1.0` +# $1.0*x_1 + 1.0*x_2 = 1.0$ print( "1.0*x_1 + 1.0*x_2 = 1.0 satisfied in all recommendations? ", @@ -121,7 +121,7 @@ ), ) -# `1.0*x_3 - 1.0*x_4 = 2.0` +# $1.0*x_3 - 1.0*x_4 = 2.0$ print( "1.0*x_3 - 1.0*x_4 = 2.0 satisfied in all recommendations? ", @@ -130,14 +130,14 @@ ), ) -# `1.0*x_1 + 1.0*x_3 >= 1.0` +# $1.0*x_1 + 1.0*x_3 >= 1.0$ print( "1.0*x_1 + 1.0*x_3 >= 1.0 satisfied in all recommendations? ", (1.0 * measurements["x_1"] + 1.0 * measurements["x_3"]).ge(1.0 - TOLERANCE).all(), ) -# `2.0*x_2 + 3.0*x_4 <= 1.0` +# $2.0*x_2 + 3.0*x_4 <= 1.0$ print( "2.0*x_2 + 3.0*x_4 <= 1.0 satisfied in all recommendations? ", diff --git a/examples/Searchspaces/continuous_space_botorch_function.py b/examples/Searchspaces/continuous_space_botorch_function.py index 46b7f48db..aa7bc7246 100644 --- a/examples/Searchspaces/continuous_space_botorch_function.py +++ b/examples/Searchspaces/continuous_space_botorch_function.py @@ -49,8 +49,8 @@ ### Creating the searchspace and the objective -# Since the searchspace is continuous test, we construct `NumericalContinuousParameter`s -# We use that data of the test function to deduce bounds and number of parameters. +# Since the searchspace is continuous, we use `NumericalContinuousParameter`s. +# We use the data of the test function to deduce bounds and number of parameters. parameters = [ NumericalContinuousParameter( diff --git a/examples/Serialization/basic_serialization.py b/examples/Serialization/basic_serialization.py index 72f8caeb1..4f4aadac5 100644 --- a/examples/Serialization/basic_serialization.py +++ b/examples/Serialization/basic_serialization.py @@ -76,6 +76,7 @@ print(campaign_recreate, end="\n" * 3) # Verify that both objects are equal. + assert campaign == campaign_recreate print("Passed basic assertion check!") From 43dd762dd2a231a99a33a8fc0782522b08934276 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 16:24:51 +0200 Subject: [PATCH 09/24] Replace use of outdated objective --- examples/Serialization/create_from_config.py | 6 +++--- examples/Serialization/validate_config.py | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/Serialization/create_from_config.py b/examples/Serialization/create_from_config.py index f11b336ea..9ca83a117 100644 --- a/examples/Serialization/create_from_config.py +++ b/examples/Serialization/create_from_config.py @@ -59,14 +59,13 @@ "constraints": [] }, "objective": { - "mode": "SINGLE", - "targets": [ + "type": "SingleTargetObjective", + "target": { "type": "NumericalTarget", "name": "Yield", "mode": "MAX" } - ] }, "recommender": { "type": "TwoPhaseMetaRecommender", @@ -96,5 +95,6 @@ campaign = Campaign.from_config(CONFIG) # We now perform a recommendation as usual and print it. + recommendation = campaign.recommend(batch_size=3) print(recommendation) diff --git a/examples/Serialization/validate_config.py b/examples/Serialization/validate_config.py index 52e8bd311..c6aa37252 100644 --- a/examples/Serialization/validate_config.py +++ b/examples/Serialization/validate_config.py @@ -58,14 +58,13 @@ "constraints": [] }, "objective": { - "mode": "SINGLE", - "targets": [ + "type": "SingleTargetObjective", + "target": { "type": "NumericalTarget", "name": "Yield", "mode": "MAX" } - ] }, "recommender": { "type": "TwoPhaseMetaRecommender", @@ -129,14 +128,13 @@ "constraints": [] }, "objective": { - "mode": "SINGLE", - "targets": [ + "type": "SingleTargetObjective", + "target": { "type": "NumericalTarget", "name": "Yield", "mode": "MAX" } - ] }, "recommender": { "type": "TwoPhaseMetaRecommender", From 74dc63561b37d420781068b11a2abddedcdbe43c Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 15:34:36 +0200 Subject: [PATCH 10/24] Improve printing for surrogates --- baybe/surrogates/base.py | 11 +++++++++++ baybe/surrogates/custom.py | 12 ++++++++++++ baybe/surrogates/gaussian_process/core.py | 12 ++++++++++++ baybe/surrogates/linear.py | 12 ++++++++++++ baybe/surrogates/ngboost.py | 12 ++++++++++++ baybe/surrogates/random_forest.py | 12 ++++++++++++ 6 files changed, 71 insertions(+) diff --git a/baybe/surrogates/base.py b/baybe/surrogates/base.py index 680333185..74035a054 100644 --- a/baybe/surrogates/base.py +++ b/baybe/surrogates/base.py @@ -28,6 +28,7 @@ ) from baybe.serialization.mixin import SerialMixin from baybe.utils.dataframe import to_tensor +from baybe.utils.plotting import create_str_representation from baybe.utils.scaling import ColumnTransformer if TYPE_CHECKING: @@ -312,6 +313,16 @@ def fit( def _fit(self, train_x: Tensor, train_y: Tensor) -> None: """Perform the actual fitting logic.""" + def __str__(self) -> str: + fields = [ + create_str_representation( + "Supports Transfer Learning", + [self.supports_transfer_learning], + single_line=True, + ), + ] + return create_str_representation(self.__class__.__name__, fields) + @define class IndependentGaussianSurrogate(Surrogate, ABC): diff --git a/baybe/surrogates/custom.py b/baybe/surrogates/custom.py index c3054c3d9..2bede8fac 100644 --- a/baybe/surrogates/custom.py +++ b/baybe/surrogates/custom.py @@ -27,6 +27,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction from baybe.utils.numerical import DTypeFloatONNX +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: import onnxruntime as ort @@ -137,3 +138,14 @@ def validate_compatibility(cls, searchspace: SearchSpace) -> None: f"a one-dimensional computational representation or " f"{CustomDiscreteParameter.__name__}." ) + + def __str__(self) -> str: + output_str = super().__str__() + # Replace first line which contains the incorrect name of the parent class + output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + fields = [ + create_str_representation( + "ONNX input name", [self.onnx_input_name], single_line=True + ) + ] + return create_str_representation(output_str, fields) diff --git a/baybe/surrogates/gaussian_process/core.py b/baybe/surrogates/gaussian_process/core.py index 2778e85a7..9d5845068 100644 --- a/baybe/surrogates/gaussian_process/core.py +++ b/baybe/surrogates/gaussian_process/core.py @@ -22,6 +22,7 @@ DefaultKernelFactory, _default_noise_factory, ) +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: from botorch.models.model import Model @@ -211,3 +212,14 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: botorch.optim.fit.fit_gpytorch_mll_torch(mll, step_limit=200) else: botorch.fit.fit_gpytorch_mll(mll) + + def __str__(self) -> str: + output_str = super().__str__() + # Replace first line which contains the incorrect name of the parent class + output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + fields = [ + create_str_representation( + "Kernel factory", [self.kernel_factory], single_line=True + ), + ] + return create_str_representation(output_str, fields) diff --git a/baybe/surrogates/linear.py b/baybe/surrogates/linear.py index b0d5f9c36..0b38c745c 100644 --- a/baybe/surrogates/linear.py +++ b/baybe/surrogates/linear.py @@ -10,6 +10,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: from torch import Tensor @@ -58,3 +59,14 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: # See base class. self._model = ARDRegression(**(self.model_params)) self._model.fit(train_x, train_y.ravel()) + + def __str__(self) -> str: + output_str = super().__str__() + # Replace first line which contains the incorrect name of the parent class + output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + fields = [ + create_str_representation( + "Model Params", [self.model_params], single_line=True + ) + ] + return create_str_representation(output_str, fields) diff --git a/baybe/surrogates/ngboost.py b/baybe/surrogates/ngboost.py index 7f4a7a1a3..871f78b30 100644 --- a/baybe/surrogates/ngboost.py +++ b/baybe/surrogates/ngboost.py @@ -11,6 +11,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: from botorch.models.transforms.input import InputTransform @@ -82,3 +83,14 @@ def _estimate_moments( def _fit(self, train_x: Tensor, train_y: Tensor) -> None: # See base class. self._model = NGBRegressor(**(self.model_params)).fit(train_x, train_y.ravel()) + + def __str__(self) -> str: + output_str = super().__str__() + # Replace first line which contains the incorrect name of the parent class + output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + fields = [ + create_str_representation( + "Model Params", [self.model_params], single_line=True + ) + ] + return create_str_representation(output_str, fields) diff --git a/baybe/surrogates/random_forest.py b/baybe/surrogates/random_forest.py index 2db112b99..c74281454 100644 --- a/baybe/surrogates/random_forest.py +++ b/baybe/surrogates/random_forest.py @@ -13,6 +13,7 @@ from baybe.surrogates.base import Surrogate from baybe.surrogates.utils import batchify_ensemble_predictor, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: from botorch.models.ensemble import EnsemblePosterior @@ -104,3 +105,14 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: # See base class. self._model = RandomForestRegressor(**(self.model_params)) self._model.fit(train_x.numpy(), train_y.numpy().ravel()) + + def __str__(self) -> str: + output_str = super().__str__() + # Replace first line which contains the incorrect name of the parent class + output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + fields = [ + create_str_representation( + "Model Params", [self.model_params], single_line=True + ) + ] + return create_str_representation(output_str, fields) From 21345cc2807df9fcd28beecd30be97188e5acef4 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 15:35:27 +0200 Subject: [PATCH 11/24] Improve printing for recommenders --- baybe/recommenders/meta/sequential.py | 26 +++++++++++++++++++ baybe/recommenders/pure/bayesian/botorch.py | 22 ++++++++++++++++ .../pure/nonpredictive/clustering.py | 17 ++++++++++++ .../pure/nonpredictive/sampling.py | 17 ++++++++++++ 4 files changed, 82 insertions(+) diff --git a/baybe/recommenders/meta/sequential.py b/baybe/recommenders/meta/sequential.py index d7d48e5e0..effc123aa 100644 --- a/baybe/recommenders/meta/sequential.py +++ b/baybe/recommenders/meta/sequential.py @@ -22,6 +22,7 @@ block_serialization_hook, converter, ) +from baybe.utils.plotting import create_str_representation @define @@ -64,6 +65,18 @@ def select_recommender( # noqa: D102 else self.initial_recommender ) + def __str__(self) -> str: + fields = [ + create_str_representation( + "Initial recommender", [self.initial_recommender] + ), + create_str_representation("Recommender", [self.recommender]), + create_str_representation( + "Switch after", [self.switch_after], single_line=True + ), + ] + return create_str_representation(self.__class__.__name__, fields) + @define class SequentialMetaRecommender(MetaRecommender): @@ -164,6 +177,13 @@ def select_recommender( # noqa: D102 return recommender + def __str__(self) -> str: + fields = [ + create_str_representation("Recommenders", [self.recommender]), + create_str_representation("Mode", [self.mode], single_line=True), + ] + return create_str_representation(self.__class__.__name__, fields) + @define class StreamingSequentialMetaRecommender(MetaRecommender): @@ -242,6 +262,12 @@ def select_recommender( # noqa: D102 return self._last_recommender # type: ignore[return-value] + def __str__(self) -> str: + fields = [ + create_str_representation("Recommenders", [self.recommenders]), + ] + return create_str_representation(self.__class__.__name__, fields) + # The recommender iterable cannot be serialized converter.register_unstructure_hook( diff --git a/baybe/recommenders/pure/bayesian/botorch.py b/baybe/recommenders/pure/bayesian/botorch.py index d5fc36420..5350d282c 100644 --- a/baybe/recommenders/pure/bayesian/botorch.py +++ b/baybe/recommenders/pure/bayesian/botorch.py @@ -17,6 +17,7 @@ SubspaceDiscrete, ) from baybe.utils.dataframe import to_tensor +from baybe.utils.plotting import create_str_representation from baybe.utils.sampling_algorithms import ( DiscreteSamplingMethod, sample_numerical_df, @@ -295,3 +296,24 @@ def _recommend_hybrid( rec_exp = pd.concat([rec_disc_exp, rec_cont_exp], axis=1) return rec_exp + + def __str__(self) -> str: + fields = [ + create_str_representation("Surrogate", [self.surrogate_model]), + create_str_representation( + "Acquisition function", [self.acquisition_function], single_line=True + ), + create_str_representation( + "Compatibility", [self.compatibility], single_line=True + ), + create_str_representation( + "Sequential continuous", [self.sequential_continuous], single_line=True + ), + create_str_representation( + "Hybrid sampler", [self.hybrid_sampler], single_line=True + ), + create_str_representation( + "Sampling percentage", [self.sampling_percentage], single_line=True + ), + ] + return create_str_representation(self.__class__.__name__, fields) diff --git a/baybe/recommenders/pure/nonpredictive/clustering.py b/baybe/recommenders/pure/nonpredictive/clustering.py index 425d9202b..2370c6347 100644 --- a/baybe/recommenders/pure/nonpredictive/clustering.py +++ b/baybe/recommenders/pure/nonpredictive/clustering.py @@ -13,6 +13,7 @@ from baybe.recommenders.pure.nonpredictive.base import NonPredictiveRecommender from baybe.searchspace import SearchSpaceType, SubspaceDiscrete +from baybe.utils.plotting import create_str_representation @define @@ -125,6 +126,22 @@ def _recommend_discrete( # Convert positional indices into DataFrame indices and return result return candidates_comp.index[selection] + def __str__(self) -> str: + fields = [ + create_str_representation( + "Compatibility", [self.compatibility], single_line=True + ), + create_str_representation( + "Name of clustering parameter", + [self.model_cluster_num_parameter_name], + single_line=True, + ), + create_str_representation( + "Model parameters", [self.model_params], single_line=True + ), + ] + return create_str_representation(self.__class__.__name__, fields) + @define class PAMClusteringRecommender(SKLearnClusteringRecommender): diff --git a/baybe/recommenders/pure/nonpredictive/sampling.py b/baybe/recommenders/pure/nonpredictive/sampling.py index dd8a1a73f..1958a2e5a 100644 --- a/baybe/recommenders/pure/nonpredictive/sampling.py +++ b/baybe/recommenders/pure/nonpredictive/sampling.py @@ -8,6 +8,7 @@ from baybe.recommenders.pure.nonpredictive.base import NonPredictiveRecommender from baybe.searchspace import SearchSpace, SearchSpaceType, SubspaceDiscrete +from baybe.utils.plotting import create_str_representation from baybe.utils.sampling_algorithms import farthest_point_sampling @@ -45,6 +46,14 @@ def _recommend_hybrid( cont_random.index = disc_random.index return pd.concat([disc_random, cont_random], axis=1) + def __str__(self) -> str: + fields = [ + create_str_representation( + "Compatibility", [self.compatibility], single_line=True + ) + ] + return create_str_representation(self.__class__.__name__, fields) + class FPSRecommender(NonPredictiveRecommender): """An initial recommender that selects candidates via Farthest Point Sampling.""" @@ -70,3 +79,11 @@ def _recommend_discrete( candidates_scaled = np.ascontiguousarray(scaler.transform(candidates_comp)) ilocs = farthest_point_sampling(candidates_scaled, batch_size) return candidates_comp.index[ilocs] + + def __str__(self) -> str: + fields = [ + create_str_representation( + "Compatibility", [self.compatibility], single_line=True + ) + ] + return create_str_representation(self.__class__.__name__, fields) From 8c3f4cc5f317c55f740b259341703de2a918c240 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Tue, 20 Aug 2024 15:35:39 +0200 Subject: [PATCH 12/24] Improve printing for campaigns --- baybe/campaign.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/baybe/campaign.py b/baybe/campaign.py index 07184489d..11584f3bb 100644 --- a/baybe/campaign.py +++ b/baybe/campaign.py @@ -29,6 +29,7 @@ telemetry_record_value, ) from baybe.utils.boolean import eq_dataframe +from baybe.utils.plotting import create_str_representation @define @@ -84,16 +85,18 @@ class Campaign(SerialMixin): """The cached recommendations.""" def __str__(self) -> str: - # Get str representation of campaign fields - fields_to_print = [self.searchspace, self.objective, self.recommender] - fields_str = "\n\n".join(str(x) for x in fields_to_print) - - # Put all relevant attributes of the campaign in one string - campaign_str = f"""Campaign - \nMeta Data\nBatches Done: {self.n_batches_done} - \rFits Done: {self.n_fits_done}\n\n{fields_str}\n""" - - return campaign_str.replace("\n", "\n ").replace("\r", "\r ") + metadata_fields = [ + create_str_representation( + "Batches done", [self.n_batches_done], single_line=True + ), + create_str_representation( + "Fits done", [self.n_fits_done], single_line=True + ), + ] + metadata = create_str_representation("Meta Data:", metadata_fields) + fields = [metadata, self.searchspace, self.objective, self.recommender] + + return create_str_representation(self.__class__.__name__, fields) @property def measurements(self) -> pd.DataFrame: From c468288b9681d9c081b617f295d6a4edaba70e24 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 22 Aug 2024 14:58:58 +0200 Subject: [PATCH 13/24] Improve printing for search spaces --- baybe/searchspace/continuous.py | 36 +++++++++++++++----------- baybe/searchspace/core.py | 22 ++++++++-------- baybe/searchspace/discrete.py | 45 +++++++++++++++++++++++---------- 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/baybe/searchspace/continuous.py b/baybe/searchspace/continuous.py index e0213cff3..cb7df3eb4 100644 --- a/baybe/searchspace/continuous.py +++ b/baybe/searchspace/continuous.py @@ -30,6 +30,7 @@ from baybe.serialization import SerialMixin, converter, select_constructor_hook from baybe.utils.basic import to_tuple from baybe.utils.dataframe import pretty_print_df +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: from baybe.searchspace.core import SearchSpace @@ -81,21 +82,26 @@ def __str__(self) -> str: constr.summary() for constr in self.constraints_nonlin ] param_df = pd.DataFrame(param_list) - lin_eq_constr_df = pd.DataFrame(eq_constraints_list) - lin_ineq_constr_df = pd.DataFrame(ineq_constraints_list) - nonlinear_constr_df = pd.DataFrame(nonlin_constraints_list) - - # Put all attributes of the continuous class in one string - continuous_str = f"""Continuous Search Space - \nContinuous Parameters\n{pretty_print_df(param_df)} - \nList of Linear Equality Constraints - \r{pretty_print_df(lin_eq_constr_df)} - \nList of Linear Inequality Constraints - \r{pretty_print_df(lin_ineq_constr_df)} - \nList of Nonlinear Constraints - \r{pretty_print_df(nonlinear_constr_df)}""" - - return continuous_str.replace("\n", "\n ").replace("\r", "\r ") + lin_eq_df = pd.DataFrame(eq_constraints_list) + lin_ineq_df = pd.DataFrame(ineq_constraints_list) + nonlinear_df = pd.DataFrame(nonlin_constraints_list) + + fields = [ + create_str_representation( + "Continuous Parameters:", [pretty_print_df(param_df)] + ), + create_str_representation( + "Linear Equality Constraints:", [pretty_print_df(lin_eq_df)] + ), + create_str_representation( + "Linear Inequality Constraints:", [pretty_print_df(lin_ineq_df)] + ), + create_str_representation( + "Non-linear Constraints:", [pretty_print_df(nonlinear_df)] + ), + ] + + return create_str_representation(self.__class__.__name__, fields) @property def constraints_cardinality(self) -> tuple[ContinuousCardinalityConstraint, ...]: diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index 341cd71db..cc971f1eb 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -25,6 +25,7 @@ from baybe.searchspace.validation import validate_parameters from baybe.serialization import SerialMixin, converter, select_constructor_hook from baybe.telemetry import TELEM_LABELS, telemetry_record_value +from baybe.utils.plotting import create_str_representation class SearchSpaceType(Enum): @@ -66,17 +67,16 @@ class SearchSpace(SerialMixin): """The (potentially empty) continuous subspace of the overall search space.""" def __str__(self) -> str: - head_str = f"""Search Space - \nSearch Space Type: {self.type.name}""" - - # Check the sub space size to avoid adding unwanted break lines - # if the sub space is empty - discrete_str = f"\n\n{self.discrete}" if not self.discrete.is_empty else "" - continuous_str = ( - f"\n\n{self.continuous}" if not self.continuous.is_empty else "" - ) - searchspace_str = f"{head_str}{discrete_str}{continuous_str}" - return searchspace_str.replace("\n", "\n ").replace("\r", "\r ") + fields = [ + create_str_representation( + "Search Space Type", [self.type.name], single_line=True + ), + ] + if not self.discrete.is_empty: + fields.append(self.discrete) + if not self.continuous.is_empty: + fields.append(self.continuous) + return create_str_representation(self.__class__.__name__, fields) def __attrs_post_init__(self): """Perform validation and record telemetry values.""" diff --git a/baybe/searchspace/discrete.py b/baybe/searchspace/discrete.py index d7b582a02..7716575ae 100644 --- a/baybe/searchspace/discrete.py +++ b/baybe/searchspace/discrete.py @@ -39,6 +39,7 @@ ) from baybe.utils.memory import bytes_to_human_readable from baybe.utils.numerical import DTypeFloatNumpy +from baybe.utils.plotting import create_str_representation if TYPE_CHECKING: import polars as pl @@ -132,19 +133,37 @@ def __str__(self) -> str: dont_recommend_count = len(self.metadata[self.metadata[_METADATA_COLUMNS[2]]]) metadata_count = len(self.metadata) - # Put all attributes of the discrete class in one string. - discrete_str = f"""Discrete Search Space - \nDiscrete Parameters\n{pretty_print_df(param_df)} - \nExperimental Representation - \n{pretty_print_df(self.exp_rep)}\n\nMetadata: - {_METADATA_COLUMNS[0]}: {was_recommended_count}/{metadata_count} - {_METADATA_COLUMNS[1]}: {was_measured_count}/{metadata_count} - {_METADATA_COLUMNS[2]}: {dont_recommend_count}/{metadata_count} - \nConstraints\n{pretty_print_df(constraints_df)} - \nComputational Representation - \n{pretty_print_df(self.comp_rep)}""" - - return discrete_str.replace("\n", "\n ") + metadata_fields = [ + create_str_representation( + f"{_METADATA_COLUMNS[0]}", + [f"{was_recommended_count}/{metadata_count}"], + single_line=True, + ), + create_str_representation( + f"{_METADATA_COLUMNS[1]}", + [f"{was_measured_count}/{metadata_count}"], + single_line=True, + ), + create_str_representation( + f"{_METADATA_COLUMNS[2]}", + [f"{dont_recommend_count}/{metadata_count}"], + single_line=True, + ), + ] + fields = [ + create_str_representation( + "Discrete Parameters", [pretty_print_df(param_df)] + ), + create_str_representation( + "Experimental Representation", [pretty_print_df(self.exp_rep)] + ), + create_str_representation("Meta Data", metadata_fields), + create_str_representation("Constraints", [pretty_print_df(constraints_df)]), + create_str_representation( + "Computational Representation", [pretty_print_df(self.comp_rep)] + ), + ] + return create_str_representation(self.__class__.__name__, fields) @exp_rep.validator def _validate_exp_rep( # noqa: DOC101, DOC103 From 68838f4833a5f8cef946d898c204235e65857021 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 22 Aug 2024 14:59:10 +0200 Subject: [PATCH 14/24] Improve printing for objectives --- baybe/objectives/desirability.py | 19 +++++++++++++------ baybe/objectives/single.py | 13 +++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/baybe/objectives/desirability.py b/baybe/objectives/desirability.py index bbc71cf1c..fb21ebb5f 100644 --- a/baybe/objectives/desirability.py +++ b/baybe/objectives/desirability.py @@ -16,7 +16,9 @@ from baybe.targets.base import Target from baybe.targets.numerical import NumericalTarget from baybe.utils.basic import to_tuple +from baybe.utils.dataframe import pretty_print_df from baybe.utils.numerical import geom_mean +from baybe.utils.plotting import create_str_representation from baybe.utils.validation import finite_float @@ -126,12 +128,17 @@ def __str__(self) -> str: targets_df = pd.DataFrame(targets_list) targets_df["Weight"] = self.weights - objective_str = f"""Objective - \nType: {self.__class__.__name__} - \nTargets \n{targets_df} - \nScalarizer: {self.scalarizer.name}""" - - return objective_str.replace("\n", "\n ") + fields = [ + create_str_representation( + "Type", [self.__class__.__name__], single_line=True + ), + create_str_representation("Targets", [pretty_print_df(targets_df)]), + create_str_representation( + "Scalarizer", [self.scalarizer.name], single_line=True + ), + ] + + return create_str_representation("Objective", fields) def transform(self, data: pd.DataFrame) -> pd.DataFrame: # noqa: D102 # See base class. diff --git a/baybe/objectives/single.py b/baybe/objectives/single.py index bf51c93b8..476f52f11 100644 --- a/baybe/objectives/single.py +++ b/baybe/objectives/single.py @@ -6,6 +6,8 @@ from baybe.objectives.base import Objective from baybe.targets.base import Target +from baybe.utils.dataframe import pretty_print_df +from baybe.utils.plotting import create_str_representation @define(frozen=True, slots=False) @@ -19,11 +21,14 @@ def __str__(self) -> str: targets_list = [target.summary() for target in self.targets] targets_df = pd.DataFrame(targets_list) - objective_str = f"""Objective - \nType: {self.__class__.__name__} - \nTargets \n{targets_df}""" + fields = [ + create_str_representation( + "Type", [self.__class__.__name__], single_line=True + ), + create_str_representation("Targets", [pretty_print_df(targets_df)]), + ] - return objective_str.replace("\n", "\n ") + return create_str_representation("Objective", fields) @property def targets(self) -> tuple[Target, ...]: # noqa: D102 From 8a77f2bfd9c4939938197c666493fe5b232bb7c6 Mon Sep 17 00:00:00 2001 From: AdrianSosic Date: Mon, 26 Aug 2024 13:37:26 +0200 Subject: [PATCH 15/24] Make numbered list in example unnumbered --- examples/Constraints_Continuous/linear_constraints.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Constraints_Continuous/linear_constraints.py b/examples/Constraints_Continuous/linear_constraints.py index 85fb7f54c..74934276c 100644 --- a/examples/Constraints_Continuous/linear_constraints.py +++ b/examples/Constraints_Continuous/linear_constraints.py @@ -58,10 +58,10 @@ ] # We model the following constraints: -# 1. $1.0*x_1 + 1.0*x_2 = 1.0$ -# 2. $1.0*x_3 - 1.0*x_4 = 2.0$ -# 3. $1.0*x_1 + 1.0*x_3 >= 1.0$ -# 4. $2.0*x_2 + 3.0*x_4 <= 1.0$ which is equivalent to $-2.0*x_2 - 3.0*x_4 >= -1.0$ +# - $1.0*x_1 + 1.0*x_2 = 1.0$ +# - $1.0*x_3 - 1.0*x_4 = 2.0$ +# - $1.0*x_1 + 1.0*x_3 >= 1.0$ +# - $2.0*x_2 + 3.0*x_4 <= 1.0$ which is equivalent to $-2.0*x_2 - 3.0*x_4 >= -1.0$ constraints = [ ContinuousLinearEqualityConstraint( From 2bca721c138c45f568ac45acc3040e9b103d2e5e Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Wed, 4 Sep 2024 15:18:42 +0200 Subject: [PATCH 16/24] Fix mypy errors --- baybe/recommenders/meta/sequential.py | 2 +- baybe/searchspace/core.py | 4 ++-- baybe/utils/plotting.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/baybe/recommenders/meta/sequential.py b/baybe/recommenders/meta/sequential.py index effc123aa..4ed3a803e 100644 --- a/baybe/recommenders/meta/sequential.py +++ b/baybe/recommenders/meta/sequential.py @@ -179,7 +179,7 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - create_str_representation("Recommenders", [self.recommender]), + create_str_representation("Recommenders", [self.recommenders]), create_str_representation("Mode", [self.mode], single_line=True), ] return create_str_representation(self.__class__.__name__, fields) diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index cc971f1eb..a7c1417cc 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -73,9 +73,9 @@ def __str__(self) -> str: ), ] if not self.discrete.is_empty: - fields.append(self.discrete) + fields.append(str(self.discrete)) if not self.continuous.is_empty: - fields.append(self.continuous) + fields.append(str(self.continuous)) return create_str_representation(self.__class__.__name__, fields) def __attrs_post_init__(self): diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 04a5e9b2e..5bfe366a3 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -149,7 +149,7 @@ def indent(text: str, amount: int = 3, ch: str = " ") -> str: def create_str_representation( - header: str, fields: list[any], *, single_line: bool = False + header: str, fields: list[Any], *, single_line: bool = False ) -> str: """Create the nested `str`representation that is used in the `__str__` methods. From 3ea0669af05e871b880db0375081bb71753fd51b Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 5 Sep 2024 14:43:59 +0200 Subject: [PATCH 17/24] Rename utility to to_string --- baybe/campaign.py | 14 ++++------ baybe/objectives/desirability.py | 14 ++++------ baybe/objectives/single.py | 10 +++---- baybe/recommenders/meta/sequential.py | 24 +++++++---------- baybe/recommenders/pure/bayesian/botorch.py | 20 ++++++-------- .../pure/nonpredictive/clustering.py | 14 ++++------ .../pure/nonpredictive/sampling.py | 18 ++++--------- baybe/searchspace/continuous.py | 20 +++++--------- baybe/searchspace/core.py | 8 +++--- baybe/searchspace/discrete.py | 26 +++++++------------ baybe/surrogates/base.py | 6 ++--- baybe/surrogates/custom.py | 8 +++--- baybe/surrogates/gaussian_process/core.py | 8 +++--- baybe/surrogates/linear.py | 10 +++---- baybe/surrogates/ngboost.py | 10 +++---- baybe/surrogates/random_forest.py | 10 +++---- baybe/utils/plotting.py | 6 ++--- 17 files changed, 81 insertions(+), 145 deletions(-) diff --git a/baybe/campaign.py b/baybe/campaign.py index 11584f3bb..51a8b70af 100644 --- a/baybe/campaign.py +++ b/baybe/campaign.py @@ -29,7 +29,7 @@ telemetry_record_value, ) from baybe.utils.boolean import eq_dataframe -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string @define @@ -86,17 +86,13 @@ class Campaign(SerialMixin): def __str__(self) -> str: metadata_fields = [ - create_str_representation( - "Batches done", [self.n_batches_done], single_line=True - ), - create_str_representation( - "Fits done", [self.n_fits_done], single_line=True - ), + to_string("Batches done", [self.n_batches_done], single_line=True), + to_string("Fits done", [self.n_fits_done], single_line=True), ] - metadata = create_str_representation("Meta Data:", metadata_fields) + metadata = to_string("Meta Data:", metadata_fields) fields = [metadata, self.searchspace, self.objective, self.recommender] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @property def measurements(self) -> pd.DataFrame: diff --git a/baybe/objectives/desirability.py b/baybe/objectives/desirability.py index fb21ebb5f..1a5c21401 100644 --- a/baybe/objectives/desirability.py +++ b/baybe/objectives/desirability.py @@ -18,7 +18,7 @@ from baybe.utils.basic import to_tuple from baybe.utils.dataframe import pretty_print_df from baybe.utils.numerical import geom_mean -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string from baybe.utils.validation import finite_float @@ -129,16 +129,12 @@ def __str__(self) -> str: targets_df["Weight"] = self.weights fields = [ - create_str_representation( - "Type", [self.__class__.__name__], single_line=True - ), - create_str_representation("Targets", [pretty_print_df(targets_df)]), - create_str_representation( - "Scalarizer", [self.scalarizer.name], single_line=True - ), + to_string("Type", [self.__class__.__name__], single_line=True), + to_string("Targets", [pretty_print_df(targets_df)]), + to_string("Scalarizer", [self.scalarizer.name], single_line=True), ] - return create_str_representation("Objective", fields) + return to_string("Objective", fields) def transform(self, data: pd.DataFrame) -> pd.DataFrame: # noqa: D102 # See base class. diff --git a/baybe/objectives/single.py b/baybe/objectives/single.py index 476f52f11..232483845 100644 --- a/baybe/objectives/single.py +++ b/baybe/objectives/single.py @@ -7,7 +7,7 @@ from baybe.objectives.base import Objective from baybe.targets.base import Target from baybe.utils.dataframe import pretty_print_df -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string @define(frozen=True, slots=False) @@ -22,13 +22,11 @@ def __str__(self) -> str: targets_df = pd.DataFrame(targets_list) fields = [ - create_str_representation( - "Type", [self.__class__.__name__], single_line=True - ), - create_str_representation("Targets", [pretty_print_df(targets_df)]), + to_string("Type", [self.__class__.__name__], single_line=True), + to_string("Targets", [pretty_print_df(targets_df)]), ] - return create_str_representation("Objective", fields) + return to_string("Objective", fields) @property def targets(self) -> tuple[Target, ...]: # noqa: D102 diff --git a/baybe/recommenders/meta/sequential.py b/baybe/recommenders/meta/sequential.py index 4ed3a803e..667a16f8a 100644 --- a/baybe/recommenders/meta/sequential.py +++ b/baybe/recommenders/meta/sequential.py @@ -22,7 +22,7 @@ block_serialization_hook, converter, ) -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string @define @@ -67,15 +67,11 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - create_str_representation( - "Initial recommender", [self.initial_recommender] - ), - create_str_representation("Recommender", [self.recommender]), - create_str_representation( - "Switch after", [self.switch_after], single_line=True - ), + to_string("Initial recommender", [self.initial_recommender]), + to_string("Recommender", [self.recommender]), + to_string("Switch after", [self.switch_after], single_line=True), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @define @@ -179,10 +175,10 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - create_str_representation("Recommenders", [self.recommenders]), - create_str_representation("Mode", [self.mode], single_line=True), + to_string("Recommenders", [self.recommenders]), + to_string("Mode", [self.mode], single_line=True), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @define @@ -264,9 +260,9 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - create_str_representation("Recommenders", [self.recommenders]), + to_string("Recommenders", [self.recommenders]), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) # The recommender iterable cannot be serialized diff --git a/baybe/recommenders/pure/bayesian/botorch.py b/baybe/recommenders/pure/bayesian/botorch.py index 5350d282c..a123bf4ec 100644 --- a/baybe/recommenders/pure/bayesian/botorch.py +++ b/baybe/recommenders/pure/bayesian/botorch.py @@ -17,7 +17,7 @@ SubspaceDiscrete, ) from baybe.utils.dataframe import to_tensor -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string from baybe.utils.sampling_algorithms import ( DiscreteSamplingMethod, sample_numerical_df, @@ -299,21 +299,17 @@ def _recommend_hybrid( def __str__(self) -> str: fields = [ - create_str_representation("Surrogate", [self.surrogate_model]), - create_str_representation( + to_string("Surrogate", [self.surrogate_model]), + to_string( "Acquisition function", [self.acquisition_function], single_line=True ), - create_str_representation( - "Compatibility", [self.compatibility], single_line=True - ), - create_str_representation( + to_string("Compatibility", [self.compatibility], single_line=True), + to_string( "Sequential continuous", [self.sequential_continuous], single_line=True ), - create_str_representation( - "Hybrid sampler", [self.hybrid_sampler], single_line=True - ), - create_str_representation( + to_string("Hybrid sampler", [self.hybrid_sampler], single_line=True), + to_string( "Sampling percentage", [self.sampling_percentage], single_line=True ), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) diff --git a/baybe/recommenders/pure/nonpredictive/clustering.py b/baybe/recommenders/pure/nonpredictive/clustering.py index 2370c6347..49e62911a 100644 --- a/baybe/recommenders/pure/nonpredictive/clustering.py +++ b/baybe/recommenders/pure/nonpredictive/clustering.py @@ -13,7 +13,7 @@ from baybe.recommenders.pure.nonpredictive.base import NonPredictiveRecommender from baybe.searchspace import SearchSpaceType, SubspaceDiscrete -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string @define @@ -128,19 +128,15 @@ def _recommend_discrete( def __str__(self) -> str: fields = [ - create_str_representation( - "Compatibility", [self.compatibility], single_line=True - ), - create_str_representation( + to_string("Compatibility", [self.compatibility], single_line=True), + to_string( "Name of clustering parameter", [self.model_cluster_num_parameter_name], single_line=True, ), - create_str_representation( - "Model parameters", [self.model_params], single_line=True - ), + to_string("Model parameters", [self.model_params], single_line=True), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @define diff --git a/baybe/recommenders/pure/nonpredictive/sampling.py b/baybe/recommenders/pure/nonpredictive/sampling.py index 1958a2e5a..f88157332 100644 --- a/baybe/recommenders/pure/nonpredictive/sampling.py +++ b/baybe/recommenders/pure/nonpredictive/sampling.py @@ -8,7 +8,7 @@ from baybe.recommenders.pure.nonpredictive.base import NonPredictiveRecommender from baybe.searchspace import SearchSpace, SearchSpaceType, SubspaceDiscrete -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string from baybe.utils.sampling_algorithms import farthest_point_sampling @@ -47,12 +47,8 @@ def _recommend_hybrid( return pd.concat([disc_random, cont_random], axis=1) def __str__(self) -> str: - fields = [ - create_str_representation( - "Compatibility", [self.compatibility], single_line=True - ) - ] - return create_str_representation(self.__class__.__name__, fields) + fields = [to_string("Compatibility", [self.compatibility], single_line=True)] + return to_string(self.__class__.__name__, fields) class FPSRecommender(NonPredictiveRecommender): @@ -81,9 +77,5 @@ def _recommend_discrete( return candidates_comp.index[ilocs] def __str__(self) -> str: - fields = [ - create_str_representation( - "Compatibility", [self.compatibility], single_line=True - ) - ] - return create_str_representation(self.__class__.__name__, fields) + fields = [to_string("Compatibility", [self.compatibility], single_line=True)] + return to_string(self.__class__.__name__, fields) diff --git a/baybe/searchspace/continuous.py b/baybe/searchspace/continuous.py index cb7df3eb4..d2ca4265b 100644 --- a/baybe/searchspace/continuous.py +++ b/baybe/searchspace/continuous.py @@ -30,7 +30,7 @@ from baybe.serialization import SerialMixin, converter, select_constructor_hook from baybe.utils.basic import to_tuple from baybe.utils.dataframe import pretty_print_df -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: from baybe.searchspace.core import SearchSpace @@ -87,21 +87,13 @@ def __str__(self) -> str: nonlinear_df = pd.DataFrame(nonlin_constraints_list) fields = [ - create_str_representation( - "Continuous Parameters:", [pretty_print_df(param_df)] - ), - create_str_representation( - "Linear Equality Constraints:", [pretty_print_df(lin_eq_df)] - ), - create_str_representation( - "Linear Inequality Constraints:", [pretty_print_df(lin_ineq_df)] - ), - create_str_representation( - "Non-linear Constraints:", [pretty_print_df(nonlinear_df)] - ), + to_string("Continuous Parameters:", [pretty_print_df(param_df)]), + to_string("Linear Equality Constraints:", [pretty_print_df(lin_eq_df)]), + to_string("Linear Inequality Constraints:", [pretty_print_df(lin_ineq_df)]), + to_string("Non-linear Constraints:", [pretty_print_df(nonlinear_df)]), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @property def constraints_cardinality(self) -> tuple[ContinuousCardinalityConstraint, ...]: diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index a7c1417cc..2210f966d 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -25,7 +25,7 @@ from baybe.searchspace.validation import validate_parameters from baybe.serialization import SerialMixin, converter, select_constructor_hook from baybe.telemetry import TELEM_LABELS, telemetry_record_value -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string class SearchSpaceType(Enum): @@ -68,15 +68,13 @@ class SearchSpace(SerialMixin): def __str__(self) -> str: fields = [ - create_str_representation( - "Search Space Type", [self.type.name], single_line=True - ), + to_string("Search Space Type", [self.type.name], single_line=True), ] if not self.discrete.is_empty: fields.append(str(self.discrete)) if not self.continuous.is_empty: fields.append(str(self.continuous)) - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) def __attrs_post_init__(self): """Perform validation and record telemetry values.""" diff --git a/baybe/searchspace/discrete.py b/baybe/searchspace/discrete.py index 7716575ae..dbb112a1c 100644 --- a/baybe/searchspace/discrete.py +++ b/baybe/searchspace/discrete.py @@ -39,7 +39,7 @@ ) from baybe.utils.memory import bytes_to_human_readable from baybe.utils.numerical import DTypeFloatNumpy -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: import polars as pl @@ -134,36 +134,30 @@ def __str__(self) -> str: metadata_count = len(self.metadata) metadata_fields = [ - create_str_representation( + to_string( f"{_METADATA_COLUMNS[0]}", [f"{was_recommended_count}/{metadata_count}"], single_line=True, ), - create_str_representation( + to_string( f"{_METADATA_COLUMNS[1]}", [f"{was_measured_count}/{metadata_count}"], single_line=True, ), - create_str_representation( + to_string( f"{_METADATA_COLUMNS[2]}", [f"{dont_recommend_count}/{metadata_count}"], single_line=True, ), ] fields = [ - create_str_representation( - "Discrete Parameters", [pretty_print_df(param_df)] - ), - create_str_representation( - "Experimental Representation", [pretty_print_df(self.exp_rep)] - ), - create_str_representation("Meta Data", metadata_fields), - create_str_representation("Constraints", [pretty_print_df(constraints_df)]), - create_str_representation( - "Computational Representation", [pretty_print_df(self.comp_rep)] - ), + to_string("Discrete Parameters", [pretty_print_df(param_df)]), + to_string("Experimental Representation", [pretty_print_df(self.exp_rep)]), + to_string("Meta Data", metadata_fields), + to_string("Constraints", [pretty_print_df(constraints_df)]), + to_string("Computational Representation", [pretty_print_df(self.comp_rep)]), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @exp_rep.validator def _validate_exp_rep( # noqa: DOC101, DOC103 diff --git a/baybe/surrogates/base.py b/baybe/surrogates/base.py index 74035a054..b4f0e4fef 100644 --- a/baybe/surrogates/base.py +++ b/baybe/surrogates/base.py @@ -28,7 +28,7 @@ ) from baybe.serialization.mixin import SerialMixin from baybe.utils.dataframe import to_tensor -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string from baybe.utils.scaling import ColumnTransformer if TYPE_CHECKING: @@ -315,13 +315,13 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: def __str__(self) -> str: fields = [ - create_str_representation( + to_string( "Supports Transfer Learning", [self.supports_transfer_learning], single_line=True, ), ] - return create_str_representation(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, fields) @define diff --git a/baybe/surrogates/custom.py b/baybe/surrogates/custom.py index 2bede8fac..f59be4aee 100644 --- a/baybe/surrogates/custom.py +++ b/baybe/surrogates/custom.py @@ -27,7 +27,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction from baybe.utils.numerical import DTypeFloatONNX -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: import onnxruntime as ort @@ -144,8 +144,6 @@ def __str__(self) -> str: # Replace first line which contains the incorrect name of the parent class output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] fields = [ - create_str_representation( - "ONNX input name", [self.onnx_input_name], single_line=True - ) + to_string("ONNX input name", [self.onnx_input_name], single_line=True) ] - return create_str_representation(output_str, fields) + return to_string(output_str, fields) diff --git a/baybe/surrogates/gaussian_process/core.py b/baybe/surrogates/gaussian_process/core.py index 9d5845068..472f1c159 100644 --- a/baybe/surrogates/gaussian_process/core.py +++ b/baybe/surrogates/gaussian_process/core.py @@ -22,7 +22,7 @@ DefaultKernelFactory, _default_noise_factory, ) -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: from botorch.models.model import Model @@ -218,8 +218,6 @@ def __str__(self) -> str: # Replace first line which contains the incorrect name of the parent class output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] fields = [ - create_str_representation( - "Kernel factory", [self.kernel_factory], single_line=True - ), + to_string("Kernel factory", [self.kernel_factory], single_line=True), ] - return create_str_representation(output_str, fields) + return to_string(output_str, fields) diff --git a/baybe/surrogates/linear.py b/baybe/surrogates/linear.py index 0b38c745c..24a91377a 100644 --- a/baybe/surrogates/linear.py +++ b/baybe/surrogates/linear.py @@ -10,7 +10,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: from torch import Tensor @@ -64,9 +64,5 @@ def __str__(self) -> str: output_str = super().__str__() # Replace first line which contains the incorrect name of the parent class output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [ - create_str_representation( - "Model Params", [self.model_params], single_line=True - ) - ] - return create_str_representation(output_str, fields) + fields = [to_string("Model Params", [self.model_params], single_line=True)] + return to_string(output_str, fields) diff --git a/baybe/surrogates/ngboost.py b/baybe/surrogates/ngboost.py index 871f78b30..e2ea7ff00 100644 --- a/baybe/surrogates/ngboost.py +++ b/baybe/surrogates/ngboost.py @@ -11,7 +11,7 @@ from baybe.surrogates.base import IndependentGaussianSurrogate from baybe.surrogates.utils import batchify_mean_var_prediction, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: from botorch.models.transforms.input import InputTransform @@ -88,9 +88,5 @@ def __str__(self) -> str: output_str = super().__str__() # Replace first line which contains the incorrect name of the parent class output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [ - create_str_representation( - "Model Params", [self.model_params], single_line=True - ) - ] - return create_str_representation(output_str, fields) + fields = [to_string("Model Params", [self.model_params], single_line=True)] + return to_string(output_str, fields) diff --git a/baybe/surrogates/random_forest.py b/baybe/surrogates/random_forest.py index c74281454..4092d5df8 100644 --- a/baybe/surrogates/random_forest.py +++ b/baybe/surrogates/random_forest.py @@ -13,7 +13,7 @@ from baybe.surrogates.base import Surrogate from baybe.surrogates.utils import batchify_ensemble_predictor, catch_constant_targets from baybe.surrogates.validation import get_model_params_validator -from baybe.utils.plotting import create_str_representation +from baybe.utils.plotting import to_string if TYPE_CHECKING: from botorch.models.ensemble import EnsemblePosterior @@ -110,9 +110,5 @@ def __str__(self) -> str: output_str = super().__str__() # Replace first line which contains the incorrect name of the parent class output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [ - create_str_representation( - "Model Params", [self.model_params], single_line=True - ) - ] - return create_str_representation(output_str, fields) + fields = [to_string("Model Params", [self.model_params], single_line=True)] + return to_string(output_str, fields) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 5bfe366a3..9c6cb24bb 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -148,10 +148,8 @@ def indent(text: str, amount: int = 3, ch: str = " ") -> str: return "".join(padding + line for line in text.splitlines(keepends=True)) -def create_str_representation( - header: str, fields: list[Any], *, single_line: bool = False -) -> str: - """Create the nested `str`representation that is used in the `__str__` methods. +def to_string(header: str, fields: list[Any], *, single_line: bool = False) -> str: + """Create a nested string representation. Args: header: The header, typically the name of the class. From a7b3ad3dcfd3fe647f11fadee1de24591b3b67ca Mon Sep 17 00:00:00 2001 From: AdrianSosic Date: Thu, 5 Sep 2024 15:06:15 +0200 Subject: [PATCH 18/24] Improve docstring of new str utility --- baybe/utils/plotting.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 9c6cb24bb..cbeca8e41 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -152,18 +152,17 @@ def to_string(header: str, fields: list[Any], *, single_line: bool = False) -> s """Create a nested string representation. Args: - header: The header, typically the name of the class. - fields: List of fields that should be printed with an indentation. - single_line: If ``True``, print the representation on a single line. Used for - simple fields that would require manual configuration otherwise. Only - applicable for lists with a single fields. + header: The header, typically the name of a class. + fields: Fields to be printed with an indentation. + single_line: If ``True``, print the representation on a single line. + Only applicable when given a single field. Raises: ValueError: If ``single_line`` is ``True`` but ``fields`` contains more than one element. Returns: - The representation with indented fields. + The string representation with indented fields. """ # Add a ":" to header if it does not end with a ":" if not header.endswith(":"): @@ -172,7 +171,7 @@ def to_string(header: str, fields: list[Any], *, single_line: bool = False) -> s if single_line: if len(fields) > 1: raise ValueError( - "single_line is only applicable for lists with a single field" + "``single_line`` is only applicable when given a single field." ) return f"{header} {str(fields[0])}" From 8107f57004b4bd3c0d819629441d9987541b20c8 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 5 Sep 2024 17:13:20 +0200 Subject: [PATCH 19/24] Change list of fields into tuple using * operator --- baybe/campaign.py | 8 ++++---- baybe/objectives/desirability.py | 8 ++++---- baybe/objectives/single.py | 6 +++--- baybe/recommenders/meta/sequential.py | 18 +++++++++--------- baybe/recommenders/pure/bayesian/botorch.py | 14 +++++++------- .../pure/nonpredictive/clustering.py | 8 ++++---- .../pure/nonpredictive/sampling.py | 8 ++++---- baybe/searchspace/continuous.py | 10 +++++----- baybe/searchspace/core.py | 4 ++-- baybe/searchspace/discrete.py | 18 +++++++++--------- baybe/surrogates/base.py | 4 ++-- baybe/surrogates/custom.py | 10 +++------- baybe/surrogates/gaussian_process/core.py | 8 +++----- baybe/surrogates/linear.py | 8 +++----- baybe/surrogates/ngboost.py | 8 +++----- baybe/surrogates/random_forest.py | 8 +++----- baybe/utils/plotting.py | 4 ++-- 17 files changed, 70 insertions(+), 82 deletions(-) diff --git a/baybe/campaign.py b/baybe/campaign.py index 51a8b70af..e8d216cfb 100644 --- a/baybe/campaign.py +++ b/baybe/campaign.py @@ -86,13 +86,13 @@ class Campaign(SerialMixin): def __str__(self) -> str: metadata_fields = [ - to_string("Batches done", [self.n_batches_done], single_line=True), - to_string("Fits done", [self.n_fits_done], single_line=True), + to_string("Batches done", self.n_batches_done, single_line=True), + to_string("Fits done", self.n_fits_done, single_line=True), ] - metadata = to_string("Meta Data:", metadata_fields) + metadata = to_string("Meta Data:", *metadata_fields) fields = [metadata, self.searchspace, self.objective, self.recommender] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @property def measurements(self) -> pd.DataFrame: diff --git a/baybe/objectives/desirability.py b/baybe/objectives/desirability.py index 1a5c21401..5aac1103f 100644 --- a/baybe/objectives/desirability.py +++ b/baybe/objectives/desirability.py @@ -129,12 +129,12 @@ def __str__(self) -> str: targets_df["Weight"] = self.weights fields = [ - to_string("Type", [self.__class__.__name__], single_line=True), - to_string("Targets", [pretty_print_df(targets_df)]), - to_string("Scalarizer", [self.scalarizer.name], single_line=True), + to_string("Type", self.__class__.__name__, single_line=True), + to_string("Targets", pretty_print_df(targets_df)), + to_string("Scalarizer", self.scalarizer.name, single_line=True), ] - return to_string("Objective", fields) + return to_string("Objective", *fields) def transform(self, data: pd.DataFrame) -> pd.DataFrame: # noqa: D102 # See base class. diff --git a/baybe/objectives/single.py b/baybe/objectives/single.py index 232483845..59f322279 100644 --- a/baybe/objectives/single.py +++ b/baybe/objectives/single.py @@ -22,11 +22,11 @@ def __str__(self) -> str: targets_df = pd.DataFrame(targets_list) fields = [ - to_string("Type", [self.__class__.__name__], single_line=True), - to_string("Targets", [pretty_print_df(targets_df)]), + to_string("Type", self.__class__.__name__, single_line=True), + to_string("Targets", pretty_print_df(targets_df)), ] - return to_string("Objective", fields) + return to_string("Objective", *fields) @property def targets(self) -> tuple[Target, ...]: # noqa: D102 diff --git a/baybe/recommenders/meta/sequential.py b/baybe/recommenders/meta/sequential.py index 667a16f8a..6c38596ff 100644 --- a/baybe/recommenders/meta/sequential.py +++ b/baybe/recommenders/meta/sequential.py @@ -67,11 +67,11 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - to_string("Initial recommender", [self.initial_recommender]), - to_string("Recommender", [self.recommender]), - to_string("Switch after", [self.switch_after], single_line=True), + to_string("Initial recommender", self.initial_recommender), + to_string("Recommender", self.recommender), + to_string("Switch after", self.switch_after, single_line=True), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @define @@ -175,10 +175,10 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - to_string("Recommenders", [self.recommenders]), - to_string("Mode", [self.mode], single_line=True), + to_string("Recommenders", self.recommenders), + to_string("Mode", self.mode, single_line=True), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @define @@ -260,9 +260,9 @@ def select_recommender( # noqa: D102 def __str__(self) -> str: fields = [ - to_string("Recommenders", [self.recommenders]), + to_string("Recommenders", self.recommenders), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) # The recommender iterable cannot be serialized diff --git a/baybe/recommenders/pure/bayesian/botorch.py b/baybe/recommenders/pure/bayesian/botorch.py index a123bf4ec..2ec317dce 100644 --- a/baybe/recommenders/pure/bayesian/botorch.py +++ b/baybe/recommenders/pure/bayesian/botorch.py @@ -299,17 +299,17 @@ def _recommend_hybrid( def __str__(self) -> str: fields = [ - to_string("Surrogate", [self.surrogate_model]), + to_string("Surrogate", self.surrogate_model), to_string( - "Acquisition function", [self.acquisition_function], single_line=True + "Acquisition function", self.acquisition_function, single_line=True ), - to_string("Compatibility", [self.compatibility], single_line=True), + to_string("Compatibility", self.compatibility, single_line=True), to_string( - "Sequential continuous", [self.sequential_continuous], single_line=True + "Sequential continuous", self.sequential_continuous, single_line=True ), - to_string("Hybrid sampler", [self.hybrid_sampler], single_line=True), + to_string("Hybrid sampler", self.hybrid_sampler, single_line=True), to_string( - "Sampling percentage", [self.sampling_percentage], single_line=True + "Sampling percentage", self.sampling_percentage, single_line=True ), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) diff --git a/baybe/recommenders/pure/nonpredictive/clustering.py b/baybe/recommenders/pure/nonpredictive/clustering.py index 49e62911a..70d228496 100644 --- a/baybe/recommenders/pure/nonpredictive/clustering.py +++ b/baybe/recommenders/pure/nonpredictive/clustering.py @@ -128,15 +128,15 @@ def _recommend_discrete( def __str__(self) -> str: fields = [ - to_string("Compatibility", [self.compatibility], single_line=True), + to_string("Compatibility", self.compatibility, single_line=True), to_string( "Name of clustering parameter", - [self.model_cluster_num_parameter_name], + self.model_cluster_num_parameter_name, single_line=True, ), - to_string("Model parameters", [self.model_params], single_line=True), + to_string("Model parameters", self.model_params, single_line=True), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @define diff --git a/baybe/recommenders/pure/nonpredictive/sampling.py b/baybe/recommenders/pure/nonpredictive/sampling.py index f88157332..3811e65f1 100644 --- a/baybe/recommenders/pure/nonpredictive/sampling.py +++ b/baybe/recommenders/pure/nonpredictive/sampling.py @@ -47,8 +47,8 @@ def _recommend_hybrid( return pd.concat([disc_random, cont_random], axis=1) def __str__(self) -> str: - fields = [to_string("Compatibility", [self.compatibility], single_line=True)] - return to_string(self.__class__.__name__, fields) + fields = [to_string("Compatibility", self.compatibility, single_line=True)] + return to_string(self.__class__.__name__, *fields) class FPSRecommender(NonPredictiveRecommender): @@ -77,5 +77,5 @@ def _recommend_discrete( return candidates_comp.index[ilocs] def __str__(self) -> str: - fields = [to_string("Compatibility", [self.compatibility], single_line=True)] - return to_string(self.__class__.__name__, fields) + fields = [to_string("Compatibility", self.compatibility, single_line=True)] + return to_string(self.__class__.__name__, *fields) diff --git a/baybe/searchspace/continuous.py b/baybe/searchspace/continuous.py index d2ca4265b..02d319f29 100644 --- a/baybe/searchspace/continuous.py +++ b/baybe/searchspace/continuous.py @@ -87,13 +87,13 @@ def __str__(self) -> str: nonlinear_df = pd.DataFrame(nonlin_constraints_list) fields = [ - to_string("Continuous Parameters:", [pretty_print_df(param_df)]), - to_string("Linear Equality Constraints:", [pretty_print_df(lin_eq_df)]), - to_string("Linear Inequality Constraints:", [pretty_print_df(lin_ineq_df)]), - to_string("Non-linear Constraints:", [pretty_print_df(nonlinear_df)]), + to_string("Continuous Parameters:", pretty_print_df(param_df)), + to_string("Linear Equality Constraints:", pretty_print_df(lin_eq_df)), + to_string("Linear Inequality Constraints:", pretty_print_df(lin_ineq_df)), + to_string("Non-linear Constraints:", pretty_print_df(nonlinear_df)), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @property def constraints_cardinality(self) -> tuple[ContinuousCardinalityConstraint, ...]: diff --git a/baybe/searchspace/core.py b/baybe/searchspace/core.py index 2210f966d..55c0b6f4c 100644 --- a/baybe/searchspace/core.py +++ b/baybe/searchspace/core.py @@ -68,13 +68,13 @@ class SearchSpace(SerialMixin): def __str__(self) -> str: fields = [ - to_string("Search Space Type", [self.type.name], single_line=True), + to_string("Search Space Type", self.type.name, single_line=True), ] if not self.discrete.is_empty: fields.append(str(self.discrete)) if not self.continuous.is_empty: fields.append(str(self.continuous)) - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) def __attrs_post_init__(self): """Perform validation and record telemetry values.""" diff --git a/baybe/searchspace/discrete.py b/baybe/searchspace/discrete.py index dbb112a1c..27bf049a5 100644 --- a/baybe/searchspace/discrete.py +++ b/baybe/searchspace/discrete.py @@ -136,28 +136,28 @@ def __str__(self) -> str: metadata_fields = [ to_string( f"{_METADATA_COLUMNS[0]}", - [f"{was_recommended_count}/{metadata_count}"], + f"{was_recommended_count}/{metadata_count}", single_line=True, ), to_string( f"{_METADATA_COLUMNS[1]}", - [f"{was_measured_count}/{metadata_count}"], + f"{was_measured_count}/{metadata_count}", single_line=True, ), to_string( f"{_METADATA_COLUMNS[2]}", - [f"{dont_recommend_count}/{metadata_count}"], + f"{dont_recommend_count}/{metadata_count}", single_line=True, ), ] fields = [ - to_string("Discrete Parameters", [pretty_print_df(param_df)]), - to_string("Experimental Representation", [pretty_print_df(self.exp_rep)]), - to_string("Meta Data", metadata_fields), - to_string("Constraints", [pretty_print_df(constraints_df)]), - to_string("Computational Representation", [pretty_print_df(self.comp_rep)]), + to_string("Discrete Parameters", pretty_print_df(param_df)), + to_string("Experimental Representation", pretty_print_df(self.exp_rep)), + to_string("Meta Data", *metadata_fields), + to_string("Constraints", pretty_print_df(constraints_df)), + to_string("Computational Representation", pretty_print_df(self.comp_rep)), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @exp_rep.validator def _validate_exp_rep( # noqa: DOC101, DOC103 diff --git a/baybe/surrogates/base.py b/baybe/surrogates/base.py index b4f0e4fef..6f891faba 100644 --- a/baybe/surrogates/base.py +++ b/baybe/surrogates/base.py @@ -317,11 +317,11 @@ def __str__(self) -> str: fields = [ to_string( "Supports Transfer Learning", - [self.supports_transfer_learning], + self.supports_transfer_learning, single_line=True, ), ] - return to_string(self.__class__.__name__, fields) + return to_string(self.__class__.__name__, *fields) @define diff --git a/baybe/surrogates/custom.py b/baybe/surrogates/custom.py index f59be4aee..b96ebdf16 100644 --- a/baybe/surrogates/custom.py +++ b/baybe/surrogates/custom.py @@ -140,10 +140,6 @@ def validate_compatibility(cls, searchspace: SearchSpace) -> None: ) def __str__(self) -> str: - output_str = super().__str__() - # Replace first line which contains the incorrect name of the parent class - output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [ - to_string("ONNX input name", [self.onnx_input_name], single_line=True) - ] - return to_string(output_str, fields) + # Make a super call to get the representation of the parent class. + fields = [to_string("ONNX input name", self.onnx_input_name, single_line=True)] + return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/gaussian_process/core.py b/baybe/surrogates/gaussian_process/core.py index 472f1c159..dcc189f34 100644 --- a/baybe/surrogates/gaussian_process/core.py +++ b/baybe/surrogates/gaussian_process/core.py @@ -214,10 +214,8 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: botorch.fit.fit_gpytorch_mll(mll) def __str__(self) -> str: - output_str = super().__str__() - # Replace first line which contains the incorrect name of the parent class - output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] + # Make a super call to get the representation of the parent class. fields = [ - to_string("Kernel factory", [self.kernel_factory], single_line=True), + to_string("Kernel factory", self.kernel_factory, single_line=True), ] - return to_string(output_str, fields) + return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/linear.py b/baybe/surrogates/linear.py index 24a91377a..13e8ab2fb 100644 --- a/baybe/surrogates/linear.py +++ b/baybe/surrogates/linear.py @@ -61,8 +61,6 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model.fit(train_x, train_y.ravel()) def __str__(self) -> str: - output_str = super().__str__() - # Replace first line which contains the incorrect name of the parent class - output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [to_string("Model Params", [self.model_params], single_line=True)] - return to_string(output_str, fields) + # Make a super call to get the representation of the parent class. + fields = [to_string("Model Params", self.model_params, single_line=True)] + return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/ngboost.py b/baybe/surrogates/ngboost.py index e2ea7ff00..b415049a7 100644 --- a/baybe/surrogates/ngboost.py +++ b/baybe/surrogates/ngboost.py @@ -85,8 +85,6 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model = NGBRegressor(**(self.model_params)).fit(train_x, train_y.ravel()) def __str__(self) -> str: - output_str = super().__str__() - # Replace first line which contains the incorrect name of the parent class - output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [to_string("Model Params", [self.model_params], single_line=True)] - return to_string(output_str, fields) + # Make a super call to get the representation of the parent class. + fields = [to_string("Model Params", self.model_params, single_line=True)] + return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/random_forest.py b/baybe/surrogates/random_forest.py index 4092d5df8..23afb82c7 100644 --- a/baybe/surrogates/random_forest.py +++ b/baybe/surrogates/random_forest.py @@ -107,8 +107,6 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model.fit(train_x.numpy(), train_y.numpy().ravel()) def __str__(self) -> str: - output_str = super().__str__() - # Replace first line which contains the incorrect name of the parent class - output_str = self.__class__.__name__ + output_str[output_str.find("\n") :] - fields = [to_string("Model Params", [self.model_params], single_line=True)] - return to_string(output_str, fields) + # Make a super call to get the representation of the parent class. + fields = [to_string("Model Params", self.model_params, single_line=True)] + return to_string(super().__str__(), *fields) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index cbeca8e41..39424cb5d 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -148,12 +148,12 @@ def indent(text: str, amount: int = 3, ch: str = " ") -> str: return "".join(padding + line for line in text.splitlines(keepends=True)) -def to_string(header: str, fields: list[Any], *, single_line: bool = False) -> str: +def to_string(header: str, *fields: Any, single_line: bool = False) -> str: """Create a nested string representation. Args: header: The header, typically the name of a class. - fields: Fields to be printed with an indentation. + *fields: Fields to be printed with an indentation. single_line: If ``True``, print the representation on a single line. Only applicable when given a single field. From dbd7ba4351e7b156a20ab0d9ea5843d108142d12 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 5 Sep 2024 17:24:18 +0200 Subject: [PATCH 20/24] Remove check for : in header --- baybe/utils/plotting.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 39424cb5d..578f99ab1 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -164,10 +164,6 @@ def to_string(header: str, *fields: Any, single_line: bool = False) -> str: Returns: The string representation with indented fields. """ - # Add a ":" to header if it does not end with a ":" - if not header.endswith(":"): - header += ":" - if single_line: if len(fields) > 1: raise ValueError( From cfec79bf5998db3a1862210d149932dd1d8c4f54 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Thu, 5 Sep 2024 17:24:36 +0200 Subject: [PATCH 21/24] Remove : in str creation --- baybe/campaign.py | 2 +- baybe/searchspace/continuous.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/baybe/campaign.py b/baybe/campaign.py index e8d216cfb..233d62312 100644 --- a/baybe/campaign.py +++ b/baybe/campaign.py @@ -89,7 +89,7 @@ def __str__(self) -> str: to_string("Batches done", self.n_batches_done, single_line=True), to_string("Fits done", self.n_fits_done, single_line=True), ] - metadata = to_string("Meta Data:", *metadata_fields) + metadata = to_string("Meta Data", *metadata_fields) fields = [metadata, self.searchspace, self.objective, self.recommender] return to_string(self.__class__.__name__, *fields) diff --git a/baybe/searchspace/continuous.py b/baybe/searchspace/continuous.py index 02d319f29..5e4438397 100644 --- a/baybe/searchspace/continuous.py +++ b/baybe/searchspace/continuous.py @@ -87,10 +87,10 @@ def __str__(self) -> str: nonlinear_df = pd.DataFrame(nonlin_constraints_list) fields = [ - to_string("Continuous Parameters:", pretty_print_df(param_df)), - to_string("Linear Equality Constraints:", pretty_print_df(lin_eq_df)), - to_string("Linear Inequality Constraints:", pretty_print_df(lin_ineq_df)), - to_string("Non-linear Constraints:", pretty_print_df(nonlinear_df)), + to_string("Continuous Parameters", pretty_print_df(param_df)), + to_string("Linear Equality Constraints", pretty_print_df(lin_eq_df)), + to_string("Linear Inequality Constraints", pretty_print_df(lin_ineq_df)), + to_string("Non-linear Constraints", pretty_print_df(nonlinear_df)), ] return to_string(self.__class__.__name__, *fields) From 4fce9f96661834b33e2997a04e7b223232b23b40 Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Fri, 6 Sep 2024 10:17:09 +0200 Subject: [PATCH 22/24] Remove redundant comments regarding super call --- baybe/surrogates/custom.py | 1 - baybe/surrogates/gaussian_process/core.py | 1 - baybe/surrogates/linear.py | 1 - baybe/surrogates/ngboost.py | 1 - baybe/surrogates/random_forest.py | 1 - 5 files changed, 5 deletions(-) diff --git a/baybe/surrogates/custom.py b/baybe/surrogates/custom.py index b96ebdf16..0153d3ad5 100644 --- a/baybe/surrogates/custom.py +++ b/baybe/surrogates/custom.py @@ -140,6 +140,5 @@ def validate_compatibility(cls, searchspace: SearchSpace) -> None: ) def __str__(self) -> str: - # Make a super call to get the representation of the parent class. fields = [to_string("ONNX input name", self.onnx_input_name, single_line=True)] return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/gaussian_process/core.py b/baybe/surrogates/gaussian_process/core.py index dcc189f34..89a759090 100644 --- a/baybe/surrogates/gaussian_process/core.py +++ b/baybe/surrogates/gaussian_process/core.py @@ -214,7 +214,6 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: botorch.fit.fit_gpytorch_mll(mll) def __str__(self) -> str: - # Make a super call to get the representation of the parent class. fields = [ to_string("Kernel factory", self.kernel_factory, single_line=True), ] diff --git a/baybe/surrogates/linear.py b/baybe/surrogates/linear.py index 13e8ab2fb..a4196f04c 100644 --- a/baybe/surrogates/linear.py +++ b/baybe/surrogates/linear.py @@ -61,6 +61,5 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model.fit(train_x, train_y.ravel()) def __str__(self) -> str: - # Make a super call to get the representation of the parent class. fields = [to_string("Model Params", self.model_params, single_line=True)] return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/ngboost.py b/baybe/surrogates/ngboost.py index b415049a7..6a79fb345 100644 --- a/baybe/surrogates/ngboost.py +++ b/baybe/surrogates/ngboost.py @@ -85,6 +85,5 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model = NGBRegressor(**(self.model_params)).fit(train_x, train_y.ravel()) def __str__(self) -> str: - # Make a super call to get the representation of the parent class. fields = [to_string("Model Params", self.model_params, single_line=True)] return to_string(super().__str__(), *fields) diff --git a/baybe/surrogates/random_forest.py b/baybe/surrogates/random_forest.py index 23afb82c7..570feecbc 100644 --- a/baybe/surrogates/random_forest.py +++ b/baybe/surrogates/random_forest.py @@ -107,6 +107,5 @@ def _fit(self, train_x: Tensor, train_y: Tensor) -> None: self._model.fit(train_x.numpy(), train_y.numpy().ravel()) def __str__(self) -> str: - # Make a super call to get the representation of the parent class. fields = [to_string("Model Params", self.model_params, single_line=True)] return to_string(super().__str__(), *fields) From 6e1cac962c57d950cede8981b0071c2b7b176b1c Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Fri, 6 Sep 2024 13:39:01 +0200 Subject: [PATCH 23/24] Add __str__ function for BetaBernoulliMAB-Surrogate --- baybe/surrogates/bandit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/baybe/surrogates/bandit.py b/baybe/surrogates/bandit.py index a90e64b2b..cf8c47291 100644 --- a/baybe/surrogates/bandit.py +++ b/baybe/surrogates/bandit.py @@ -13,6 +13,7 @@ from baybe.searchspace.core import SearchSpace from baybe.surrogates.base import Surrogate from baybe.targets.binary import _FAILURE_VALUE_COMP, _SUCCESS_VALUE_COMP +from baybe.utils.plotting import to_string from baybe.utils.random import temporary_seed if TYPE_CHECKING: @@ -160,3 +161,7 @@ def _fit(self, train_x: Tensor, train_y: Tensor, _: Any = None) -> None: wins = (train_x * (train_y == float(_SUCCESS_VALUE_COMP))).sum(dim=0) losses = (train_x * (train_y == float(_FAILURE_VALUE_COMP))).sum(dim=0) self._win_lose_counts = torch.vstack([wins, losses]).to(torch.int) + + def __str__(self) -> str: + fields = [to_string("Prior", self.prior, single_line=True)] + return to_string(super().__str__(), *fields) From 646fb95c105cd180771f984a3b7dd8ad1ed3eb7e Mon Sep 17 00:00:00 2001 From: "Alexander V. Hopp" Date: Fri, 6 Sep 2024 13:57:09 +0200 Subject: [PATCH 24/24] Add colon at the end of single line headers --- baybe/utils/plotting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/baybe/utils/plotting.py b/baybe/utils/plotting.py index 578f99ab1..a73ea6b50 100644 --- a/baybe/utils/plotting.py +++ b/baybe/utils/plotting.py @@ -169,6 +169,8 @@ def to_string(header: str, *fields: Any, single_line: bool = False) -> str: raise ValueError( "``single_line`` is only applicable when given a single field." ) + # Since single line headers look ugly without a ":", we add it manually + header = header if header.endswith(":") else header + ":" return f"{header} {str(fields[0])}" return "\n".join([header] + [indent(str(f)) for f in fields])