From 7ec4f190e53a30202564f066442c6338ac20d575 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Fri, 14 Aug 2026 00:06:37 -0700 Subject: [PATCH 1/8] modernize type annotations --- src/paretobench/containers.py | 135 +++++++++++++++++----------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/src/paretobench/containers.py b/src/paretobench/containers.py index 94a5241..8a53697 100644 --- a/src/paretobench/containers.py +++ b/src/paretobench/containers.py @@ -1,7 +1,7 @@ from datetime import datetime, timezone from functools import reduce from pydantic import BaseModel, Field, field_validator, ConfigDict, model_validator -from typing import List, Dict, Union, Optional, Literal, Tuple, TYPE_CHECKING +from typing import Literal, TYPE_CHECKING import h5py import numpy as np import random @@ -37,10 +37,11 @@ class Population(BaseModel): # Total number of function evaluations performed during optimization after this population was completed fevals: int + # Optional lists of names for decision variables, objectives, and constraints - names_x: Optional[List[str]] = None - names_f: Optional[List[str]] = None - names_g: Optional[List[str]] = None + names_x: list[str] | None = None + names_f: list[str] | None = None + names_g: list[str] | None = None # Configuration of objectives/constraints (minimization or maximization problem, direction of and target of constraint) obj_directions: str # '+' means maximize, '-' means minimize @@ -257,7 +258,7 @@ def __add__(self, other: "Population") -> "Population": constraint_targets=self.constraint_targets, ) - def __getitem__(self, idx: Union[slice, np.ndarray, List[int]]) -> "Population": + def __getitem__(self, idx: slice | np.ndarray | list[int]) -> "Population": """ Indexing operator to select along the batch dimension in the arrays. @@ -448,19 +449,19 @@ def plot_obj_scatter( domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", show_points: bool = True, - problem: Optional[Union[str, "Problem"]] = None, + problem: "str | Problem | None" = None, n_pf: int = 1000, - pf_objectives: Optional[np.ndarray] = None, + pf_objectives: np.ndarray | None = None, show_attainment: bool = False, show_dominated_area: bool = False, - dominated_area_zorder: Optional[int] = -2, - ref_point: Optional[Tuple[float, float]] = None, + dominated_area_zorder: int | None = -2, + ref_point: tuple[float, float] | None = None, ref_point_padding: float = 0.05, - label: Optional[str] = None, - legend_loc: Optional[str] = None, + label: str | None = None, + legend_loc: str | None = None, show_names: bool = True, - color: Optional[str] = None, - scale: Optional[np.ndarray] = None, + color: str | None = None, + scale: np.ndarray | None = None, flip_objs: bool = False, ): """ @@ -491,7 +492,7 @@ def plot_obj_scatter( Plots the dominated region towards the larger values of each decision var dominated_area_zorder : int, optional What "zorder" to draw dominated region at. Mostly used internally to correctly show dominated area in history plots. - ref_point : Union[str, Tuple[float, float]], optional + ref_point : str | tuple[float, float], optional Where to stop plotting the dominated region / attainment surface. Must be a point to the upper right (increasing value of objectives in 3D) of all plotted points. By default, will set to right of max of each objective plus padding. @@ -543,18 +544,18 @@ def plot_obj_scatter( def plot_dvar_pairs( self, - dvars: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, + dvars: int | slice | list[int] | tuple[int, int] | None = None, fig=None, axes=None, domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", - hist_bins: Optional[int] = None, + hist_bins: int | None = None, show_names: bool = True, - problem: Optional[Union[str, "Problem"]] = None, - lower_bounds: Optional[np.ndarray] = None, - upper_bounds: Optional[np.ndarray] = None, - color: Optional[str] = None, - scale: Optional[np.ndarray] = None, + problem: "str | Problem | None" = None, + lower_bounds: np.ndarray | None = None, + upper_bounds: np.ndarray | None = None, + color: str | None = None, + scale: np.ndarray | None = None, ): """ Creates a pairs plot (scatter matrix) showing correlations between decision variables @@ -562,7 +563,7 @@ def plot_dvar_pairs( Parameters ---------- - dvars : int, slice, List[int], or Tuple[int, int], optional + dvars : int, slice, list[int], or tuple[int, int], optional Specifies which decision variables to plot. See `selection_to_indices` for more details. fig : matplotlib.figure.Figure, optional Figure to plot on. If None and axes is None, creates a new figure. @@ -624,9 +625,9 @@ class History(BaseModel): - Objective/constraint settings and names, if used, must be consistent across populations """ - reports: List[Population] + reports: list[Population] problem: str - metadata: Dict[str, Union[str, int, float, bool]] = Field(default_factory=dict) + metadata: dict[str, str | int | float | bool] = Field(default_factory=dict) @model_validator(mode="after") def validate_consistent_populations(self): @@ -883,27 +884,27 @@ def pf_reduce(a, b): def plot_obj_scatter( self, - reports: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, + reports: int | slice | list[int] | tuple[int, int] | None = None, fig=None, ax=None, domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", show_points: bool = True, n_pf: int = 1000, - pf_objectives: Optional[np.ndarray] = None, + pf_objectives: np.ndarray | None = None, show_attainment: bool = False, show_dominated_area: bool = False, - ref_point: Optional[Tuple[float, float]] = None, + ref_point: tuple[float, float] | None = None, ref_point_padding: float = 0.05, - legend_loc: Optional[str] = None, - scale: Optional[np.ndarray] = None, + legend_loc: str | None = None, + scale: np.ndarray | None = None, flip_objs: bool = False, show_names: bool = True, show_pf: bool = False, colormap: str = "viridis", - cmap_label: Optional[str] = None, + cmap_label: str | None = None, generation_mode: Literal["cmap", "cumulative"] = "cmap", - single_color: Optional[str] = None, + single_color: str | None = None, label_mode: Literal["index", "fevals"] = "index", ): """ @@ -912,7 +913,7 @@ def plot_obj_scatter( Parameters ---------- - reports : int, slice, List[int], or Tuple[int, int], optional + reports : int, slice, list[int], or tuple[int, int], optional Specifies which generations to plot. See `selection_to_indices` for more details. fig : matplotlib figure, optional Figure to plot on, by default None @@ -933,7 +934,7 @@ def plot_obj_scatter( Whether to plot the attainment surface, by default False show_dominated_area : bool, optional Plots the dominated region towards the larger values of each decision var - ref_point : Union[str, Tuple[float, float]], optional + ref_point : str | tuple[float, float], optional Where to stop plotting the dominated region / attainment surface. Must be a point to the upper right (increasing value of objectives in 3D) of all plotted points. By default, will set to right of max of each objective plus padding. @@ -952,13 +953,13 @@ def plot_obj_scatter( Whether to plot the Pareto front, by default True colormap : str, optional Name of the colormap to use for generation colors, by default 'viridis' - cmap_label: Optional[str] = "Generation" + cmap_label: str | None = "Generation" Label for colorbar (only used when generation_mode is 'cmap') generation_mode: Literal['cmap', 'cumulative'] = 'cmap' How to handle multiple generations: 'cmap': Plot each generation separately with colors from colormap 'cumulative': Merge all selected generations into single population - single_color: Optional[str] = None + single_color: str | None = None Color to use when generation_mode is 'cumulative'. If None, uses default color from matplotlib. label_mode: Literal['index', 'fevals'] = 'index' Whether to use report index or function evaluations (fevals) for labels @@ -998,21 +999,21 @@ def plot_obj_scatter( def plot_dvar_pairs( self, - reports: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, - dvars: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, + reports: int | slice | list[int] | tuple[int, int] | None = None, + dvars: int | slice | list[int] | tuple[int, int] | None = None, fig=None, axes=None, domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", - hist_bins: Optional[int] = None, + hist_bins: int | None = None, show_names: bool = True, - lower_bounds: Optional[np.ndarray] = None, - upper_bounds: Optional[np.ndarray] = None, - scale: Optional[np.ndarray] = None, + lower_bounds: np.ndarray | None = None, + upper_bounds: np.ndarray | None = None, + scale: np.ndarray | None = None, colormap: str = "viridis", - cmap_label: Optional[str] = None, + cmap_label: str | None = None, generation_mode: Literal["cmap", "cumulative"] = "cmap", - single_color: Optional[str] = None, + single_color: str | None = None, plot_bounds: bool = False, label_mode: Literal["index", "fevals"] = "index", ): @@ -1022,9 +1023,9 @@ def plot_dvar_pairs( Parameters ---------- - reports : int, slice, List[int], or Tuple[int, int], optional + reports : int, slice, list[int], or tuple[int, int], optional Specifies which generations to plot. See `selection_to_indices` for more details. - dvars : int, slice, List[int], or Tuple[int, int], optional + dvars : int, slice, list[int], or tuple[int, int], optional Which decision vars to plot. See `population_dvar_pairs` docstring for more details. fig : matplotlib figure, optional Figure to plot on, by default None @@ -1047,13 +1048,13 @@ def plot_dvar_pairs( If None, no scaling is applied. colormap : str, optional Name of the colormap to use for generation colors, by default 'viridis' - cmap_label: Optional[str] = "Generation" + cmap_label: str | None = "Generation" Label for colorbar (only used when generation_mode is 'cmap') generation_mode: Literal['cmap', 'cumulative'] = 'cmap' How to handle multiple generations: 'cmap': Plot each generation separately with colors from colormap 'cumulative': Merge all selected generations into single population - single_color: Optional[str] = None + single_color: str | None = None Color to use when generation_mode is 'cumulative'. If None, uses default color from matplotlib. plot_bounds: bool = False Whether to plot bounds for the problem @@ -1090,23 +1091,23 @@ def plot_dvar_pairs( def plot_obj_animation( self, - reports: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, + reports: int | slice | list[int] | tuple[int, int] | None = None, interval: int = 200, domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", show_points: bool = True, n_pf: int = 1000, - pf_objectives: Optional[np.ndarray] = None, + pf_objectives: np.ndarray | None = None, show_attainment: bool = False, show_dominated_area: bool = False, - ref_point: Optional[Tuple[float, float]] = None, + ref_point: tuple[float, float] | None = None, ref_point_padding: float = 0.05, - legend_loc: Optional[str] = "upper right", - scale: Optional[np.ndarray] = None, + legend_loc: str | None = "upper right", + scale: np.ndarray | None = None, flip_objs: bool = False, show_names: bool = True, show_pf: bool = False, - single_color: Optional[str] = None, + single_color: str | None = None, dynamic_scaling: bool = False, cumulative: bool = False, scale_padding: float = 0.05, @@ -1116,7 +1117,7 @@ def plot_obj_animation( Parameters ---------- - reports : int, slice, List[int], or Tuple[int, int], optional + reports : int, slice, list[int], or tuple[int, int], optional Specifies which generations to animate. See `selection_to_indices` for more details. interval : int, optional Delay between frames in milliseconds, by default 200 @@ -1135,7 +1136,7 @@ def plot_obj_animation( Whether to plot the attainment surface, by default False show_dominated_area : bool, optional Plots the dominated region towards the larger values of each decision var - ref_point : Union[str, Tuple[float, float]], optional + ref_point : str | tuple[float, float], optional Where to stop plotting the dominated region / attainment surface. Must be a point to the upper right (increasing value of objectives in 3D) of all plotted points. By default, will set to right of max of each objective plus padding. @@ -1152,7 +1153,7 @@ def plot_obj_animation( Whether to show the names of the objectives if provided by population show_pf : bool, optional Whether to plot the Pareto front, by default True - single_color: Optional[str] = None + single_color: str | None = None Color to use when generation_mode is 'cumulative'. If None, uses default color from matplotlib. dynamic_scaling : bool, optional If True, axes limits will update based on each frame's data. @@ -1196,17 +1197,17 @@ def plot_obj_animation( def plot_dvar_animation( self, - reports: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, - dvars: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, + reports: int | slice | list[int] | tuple[int, int] | None = None, + dvars: int | slice | list[int] | tuple[int, int] | None = None, interval: int = 200, domination_filt: Literal["all", "dominated", "non-dominated"] = "all", feasibility_filt: Literal["all", "feasible", "infeasible"] = "all", - hist_bins: Optional[int] = None, + hist_bins: int | None = None, show_names: bool = True, - lower_bounds: Optional[np.ndarray] = None, - upper_bounds: Optional[np.ndarray] = None, - scale: Optional[np.ndarray] = None, - single_color: Optional[str] = None, + lower_bounds: np.ndarray | None = None, + upper_bounds: np.ndarray | None = None, + scale: np.ndarray | None = None, + single_color: str | None = None, plot_bounds: bool = False, dynamic_scaling: bool = False, cumulative: bool = False, @@ -1217,9 +1218,9 @@ def plot_dvar_animation( Parameters ---------- - reports : int, slice, List[int], or Tuple[int, int], optional + reports : int, slice, list[int], or tuple[int, int], optional Specifies which generations to animate. See `selection_to_indices` for more details. - dvars : int, slice, List[int], or Tuple[int, int], optional + dvars : int, slice, list[int], or tuple[int, int], optional Which decision vars to plot. See `population_dvar_pairs` docstring for more details. interval : int, optional Delay between frames in milliseconds, by default 200 @@ -1238,7 +1239,7 @@ def plot_dvar_animation( scale : array-like, optional Scale factors for each variable. Must have the same length as the number of decision vars. If None, no scaling is applied. - single_color: Optional[str] = None + single_color: str | None = None Color to use when generation_mode is 'cumulative'. If None, uses default color from matplotlib. plot_bounds: bool = False Whether to plot bounds for the problem @@ -1335,7 +1336,7 @@ class Experiment(BaseModel): used to save the data. """ - runs: List[History] + runs: list[History] name: str author: str = "" software: str = "" From 1ae66a50e74c9d51d3e009d397306960b0beddae Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Fri, 14 Aug 2026 00:33:19 -0700 Subject: [PATCH 2/8] add bounds to population objects --- src/paretobench/containers.py | 122 ++++++++++++++++++++++--- src/paretobench/misc.py | 2 +- src/paretobench/plotting/population.py | 3 +- src/paretobench/problem.py | 47 ++++++++-- src/paretobench/utils.py | 30 ------ tests/test_containers.py | 56 ++++++++++++ 6 files changed, 209 insertions(+), 51 deletions(-) diff --git a/src/paretobench/containers.py b/src/paretobench/containers.py index 8a53697..5f4c59b 100644 --- a/src/paretobench/containers.py +++ b/src/paretobench/containers.py @@ -15,6 +15,34 @@ from .problem import Problem +def _validate_1d_float_array(value, name: str, expected_len: int, expected_len_desc: str): + """ + Checks that a field is a 1D float64 numpy array of the expected length. + + Parameters + ---------- + value : Any + The value of the field being validated. + name : str + Name of the field, used in the error messages. + expected_len : int + The length the array must have. + expected_len_desc : str + Human readable description of where the expected length comes from, used in the error message. + """ + if not isinstance(value, np.ndarray): + raise ValueError(f"{name} must be a numpy array, got: {type(value)}") + if len(value.shape) != 1: + raise ValueError(f"{name} must be 1D, shape was: {value.shape}") + if len(value) != expected_len: + raise ValueError( + f"Length of {name} must match {expected_len_desc}. Got {len(value)} elements but " + f"{expected_len_desc} is {expected_len}" + ) + if value.dtype != np.float64: + raise ValueError(f"{name} dtype must be {np.float64}. Got {value.dtype}") + + class Population(BaseModel): """ Stores the individuals in a population for one reporting interval in a genetic algorithm. Conventional names are used for @@ -28,6 +56,9 @@ class Population(BaseModel): to an objectve and '+' means maximize with '-' meaning minimize. The constraints are configured by the string of directions `constraint_directions` and the numpy array of targets `constraint_targets`. The string should contain either the '<' or '>' character for the constraint at that index to be satisfied when it is less than or greater than the target respectively. + + The rectangular bounds the decision variables were drawn from are recorded in `var_lower_bounds` and `var_upper_bounds`. + When they are not specified, the variables are treated as unbounded and the arrays are filled with -inf and +inf. """ # The decision vars, objectives, and constraints @@ -50,6 +81,10 @@ class Population(BaseModel): ) constraint_targets: np.ndarray + # Rectangular bounds of the decision variables (-inf / +inf when unbounded) + var_lower_bounds: np.ndarray + var_upper_bounds: np.ndarray + # Pydantic config model_config = ConfigDict(arbitrary_types_allowed=True, validate_assignment=True) @@ -59,7 +94,8 @@ def set_default_vals(cls, values): """ Handles automatic setting of `x`, `f`, `g`, `fevals` when some are not specified. The arrays are set to an empty array with a zero length non-batch dimension. The number of function evaluations (`fevals`) is set to the number of individuals - in the population (assuming here that each was evaluated to get to this point). + in the population (assuming here that each was evaluated to get to this point). Unspecified decision variable bounds + are set to -inf / +inf. """ # Determine the batch size from the first non-None array batch_size = next( @@ -85,6 +121,12 @@ def set_default_vals(cls, values): if values.get("constraint_targets") is None: values["constraint_targets"] = np.zeros(values["g"].shape[1], dtype=float) + # Treat the decision variables as unbounded if no bounds were given + if values.get("var_lower_bounds") is None: + values["var_lower_bounds"] = np.full(values["x"].shape[1], -np.inf) + if values.get("var_upper_bounds") is None: + values["var_upper_bounds"] = np.full(values["x"].shape[1], np.inf) + # Set fevals to number of individuals if not included if values.get("fevals") is None: values["fevals"] = batch_size @@ -141,19 +183,29 @@ def validate_constraint_directions(self): @model_validator(mode="after") def validate_constraint_targets(self): - # Check the targets - attr = "constraint_targets" - if not isinstance(getattr(self, attr), np.ndarray): - raise ValueError(f"{attr} must be a numpy array, got: {type(getattr(self, attr))}") - if len(getattr(self, attr).shape) != 1: - raise ValueError(f"{attr} must be 1D, shape was: {getattr(self, attr).shape}") - if len(getattr(self, attr)) != self.g.shape[1]: + _validate_1d_float_array( + self.constraint_targets, "constraint_targets", self.g.shape[1], "number of constraints in g" + ) + return self + + @model_validator(mode="after") + def validate_var_bounds(self): + """ + Checks that the decision variable bounds are correctly sized and that each lower bound is at or below its + corresponding upper bound. + """ + _validate_1d_float_array( + self.var_lower_bounds, "var_lower_bounds", self.x.shape[1], "number of decision variables in x" + ) + _validate_1d_float_array( + self.var_upper_bounds, "var_upper_bounds", self.x.shape[1], "number of decision variables in x" + ) + bad_idx = np.nonzero(self.var_lower_bounds > self.var_upper_bounds)[0] + if bad_idx.size: raise ValueError( - f"Length of {attr} must match number of constraints in g. Got {len(getattr(self, attr))} " - f"elements and {self.g.shape[1]} constraints from g" + f"var_lower_bounds must be less than or equal to var_upper_bounds. Got lower > upper at indices " + f"{bad_idx.tolist()}" ) - if getattr(self, attr).dtype != np.float64: - raise ValueError(f"{attr} dtype must be {np.float64}. Got {getattr(self, attr).dtype}") return self @field_validator("x", "f", "g") @@ -190,6 +242,8 @@ def __eq__(self, other): and self.obj_directions == other.obj_directions and self.constraint_directions == other.constraint_directions and np.array_equal(self.constraint_targets, other.constraint_targets) + and np.array_equal(self.var_lower_bounds, other.var_lower_bounds) + and np.array_equal(self.var_upper_bounds, other.var_upper_bounds) ) @property @@ -229,6 +283,10 @@ def __add__(self, other: "Population") -> "Population": raise ValueError("constraint_directions are inconsistent between populations") if not np.array_equal(self.constraint_targets, other.constraint_targets): raise ValueError("constraint_targets are inconsistent between populations") + if not np.array_equal(self.var_lower_bounds, other.var_lower_bounds): + raise ValueError("var_lower_bounds are inconsistent between populations") + if not np.array_equal(self.var_upper_bounds, other.var_upper_bounds): + raise ValueError("var_upper_bounds are inconsistent between populations") # Concatenate the arrays along the batch dimension (axis=0) new_x = np.concatenate((self.x, other.x), axis=0) @@ -256,6 +314,8 @@ def __add__(self, other: "Population") -> "Population": obj_directions=self.obj_directions, constraint_directions=self.constraint_directions, constraint_targets=self.constraint_targets, + var_lower_bounds=self.var_lower_bounds, + var_upper_bounds=self.var_upper_bounds, ) def __getitem__(self, idx: slice | np.ndarray | list[int]) -> "Population": @@ -283,6 +343,8 @@ def __getitem__(self, idx: slice | np.ndarray | list[int]) -> "Population": obj_directions=self.obj_directions, constraint_directions=self.constraint_directions, constraint_targets=self.constraint_targets, + var_lower_bounds=self.var_lower_bounds, + var_upper_bounds=self.var_upper_bounds, ) def get_nondominated_indices(self): @@ -309,6 +371,7 @@ def from_random( fevals: int = 0, generate_names: bool = False, generate_obj_constraint_settings: bool = False, + generate_bounds: bool = False, ) -> "Population": """ Generate a randomized instance of the Population class. @@ -329,6 +392,8 @@ def from_random( Whether to include names for the decision variables, objectives, and constraints, by default False. generate_obj_constraint_settings : bool, optional Randomize the objective and constraint settings, default to minimization problem and g >= 0 constraint + generate_bounds : bool, optional + Randomize the decision variable bounds, by default the variables are left unbounded Returns ------- @@ -372,6 +437,14 @@ def from_random( constraint_directions = None constraint_targets = None + # Create randomized bounds which contain the decision variables + if generate_bounds: + var_lower_bounds = -np.random.rand(n_decision_vars) + var_upper_bounds = 1.0 + np.random.rand(n_decision_vars) + else: + var_lower_bounds = None + var_upper_bounds = None + return cls( x=x, f=f, @@ -383,6 +456,8 @@ def from_random( obj_directions=obj_directions, constraint_directions=constraint_directions, constraint_targets=constraint_targets, + var_lower_bounds=var_lower_bounds, + var_upper_bounds=var_upper_bounds, ) def __len__(self): @@ -669,6 +744,14 @@ def validate_consistent_populations(self): if constraint_targets and len(set(constraint_targets)) != 1: raise ValueError(f"Inconsistent constraint_targets in reports: {constraint_targets}") + # Check the decision variable bounds + var_lower_bounds = [tuple(x.var_lower_bounds) for x in self.reports] + var_upper_bounds = [tuple(x.var_upper_bounds) for x in self.reports] + if var_lower_bounds and len(set(var_lower_bounds)) != 1: + raise ValueError(f"Inconsistent var_lower_bounds in reports: {var_lower_bounds}") + if var_upper_bounds and len(set(var_upper_bounds)) != 1: + raise ValueError(f"Inconsistent var_upper_bounds in reports: {var_upper_bounds}") + return self def __eq__(self, other): @@ -686,6 +769,7 @@ def from_random( pop_size: int, generate_names: bool = False, generate_obj_constraint_settings: bool = False, + generate_bounds: bool = False, ) -> "History": """ Generate a randomized instance of the History class, including random problem name and metadata. @@ -706,6 +790,8 @@ def from_random( Whether to include names for the decision variables, objectives, and constraints, by default False. generate_obj_constraint_settings : bool, optional Randomize the objective and constraint settings, default to minimization problem and g >= 0 constraint + generate_bounds : bool, optional + Randomize the decision variable bounds, by default the variables are left unbounded Returns ------- @@ -749,6 +835,14 @@ def from_random( report.constraint_directions = constraint_directions report.constraint_targets = constraint_targets + # Create randomized decision variable bounds (must be consistent between objects) + if generate_bounds: + var_lower_bounds = -np.random.rand(n_decision_vars) + var_upper_bounds = 1.0 + np.random.rand(n_decision_vars) + for report in reports: + report.var_lower_bounds = var_lower_bounds + report.var_upper_bounds = var_upper_bounds + return cls(reports=reports, problem=problem, metadata=metadata) def _to_h5py_group(self, g: h5py.Group): @@ -1385,6 +1479,7 @@ def from_random( pop_size: int, generate_names: bool = False, generate_obj_constraint_settings: bool = False, + generate_bounds: bool = False, ) -> "Experiment": """ Generate a randomized instance of the Experiment class. @@ -1407,6 +1502,8 @@ def from_random( Whether to include names for the decision variables, objectives, and constraints, by default False. generate_obj_constraint_settings : bool, optional Randomize the objective and constraint settings, default to minimization problem and g >= 0 constraint + generate_bounds : bool, optional + Randomize the decision variable bounds, by default the variables are left unbounded Returns ------- @@ -1423,6 +1520,7 @@ def from_random( pop_size, generate_names=generate_names, generate_obj_constraint_settings=generate_obj_constraint_settings, + generate_bounds=generate_bounds, ) for _ in range(n_histories) ] diff --git a/src/paretobench/misc.py b/src/paretobench/misc.py index 184ac37..f79a80c 100644 --- a/src/paretobench/misc.py +++ b/src/paretobench/misc.py @@ -204,7 +204,7 @@ def var_lower_bounds(self): @property def var_upper_bounds(self): - return np.array([[1.0, 5.0]]) + return np.array([1.0, 5.0]) @property def reference(self): diff --git a/src/paretobench/plotting/population.py b/src/paretobench/plotting/population.py index fbd8ccd..242be20 100644 --- a/src/paretobench/plotting/population.py +++ b/src/paretobench/plotting/population.py @@ -8,8 +8,7 @@ from ..containers import Population from ..exceptions import EmptyPopulationError, NoDecisionVarsError, NoObjectivesError -from ..problem import Problem, ProblemWithFixedPF, ProblemWithPF -from ..utils import get_problem_from_obj_or_str +from ..problem import Problem, ProblemWithFixedPF, ProblemWithPF, get_problem_from_obj_or_str from .attainment import compute_attainment_surface_2d, compute_attainment_surface_3d from .utils import get_per_point_settings_population, alpha_scatter, selection_to_indices diff --git a/src/paretobench/problem.py b/src/paretobench/problem.py index 2ce5fb3..ed39105 100644 --- a/src/paretobench/problem.py +++ b/src/paretobench/problem.py @@ -1,6 +1,7 @@ import numpy as np from pydantic import BaseModel +from .containers import Population from .exceptions import DeserializationError, InputError from .factory import create_problem from .simple_serialize import dumps, loads @@ -45,8 +46,7 @@ def __call__(self, x: np.ndarray, check_bounds=True): raise InputError(msg) if check_bounds and ((x > self.var_upper_bounds).all() or (x < self.var_lower_bounds).all()): raise InputError("Input lies outside of problem bounds.") - pop = self._call(x[None, :]) - pop.x = np.reshape(x, (1, -1)) + x = np.reshape(x, (1, -1)) # If batched input is used elif len(x.shape) == 2: @@ -57,15 +57,23 @@ def __call__(self, x: np.ndarray, check_bounds=True): raise InputError(msg) if check_bounds and ((x > self.var_upper_bounds).all() or (x < self.var_lower_bounds).all()): raise InputError("Input lies outside of problem bounds.") - pop = self._call(x) - pop.x = x # If user provided something not usable else: raise ValueError(f"Incompatible shape of input array x: {x.shape}") - # Set the decision variables - return pop + # Attach the decision variables and the problem's bounds to the evaluated population. These must be set together + # because the bounds are validated against the number of decision variables, so assigning them one at a time + # leaves the population in a state which does not validate. + pop = self._call(x) + return Population.model_validate( + { + **dict(pop), + "x": x, + "var_lower_bounds": np.asarray(self.var_lower_bounds, dtype=np.float64), + "var_upper_bounds": np.asarray(self.var_upper_bounds, dtype=np.float64), + } + ) def _call(self, x: np.ndarray): """ @@ -193,6 +201,33 @@ def __str__(self): return self.__repr__() +def get_problem_from_obj_or_str(obj_or_str: "str | Problem") -> "Problem": + """Convert input to Problem instance. + + Parameters + ---------- + obj_or_str : Problem or str + Input to convert. If already a Problem instance, returns as-is. + If string, creates Problem from line format. + + Returns + ------- + Problem + The resulting Problem instance. + + Raises + ------ + ValueError + If input is neither Problem nor str type. + """ + if isinstance(obj_or_str, Problem): + return obj_or_str + elif isinstance(obj_or_str, str): + return Problem.from_line_fmt(obj_or_str) + else: + raise ValueError(f"Unrecognized input type: {type(obj_or_str)}") + + class ProblemWithPF: """ Mixin class for problems with a defined Pareto front where you can request a certain number of points from it. diff --git a/src/paretobench/utils.py b/src/paretobench/utils.py index 0bd135d..a722e56 100644 --- a/src/paretobench/utils.py +++ b/src/paretobench/utils.py @@ -1,10 +1,7 @@ from itertools import combinations, chain, count from math import comb -from typing import Union import numpy as np -from .problem import Problem - def get_betas(m, p): """ @@ -171,33 +168,6 @@ def weighted_chunk_sizes(n, weights): return ns -def get_problem_from_obj_or_str(obj_or_str: Union[str, Problem]) -> Problem: - """Convert input to Problem instance. - - Parameters - ---------- - obj_or_str : Problem or str - Input to convert. If already a Problem instance, returns as-is. - If string, creates Problem from line format. - - Returns - ------- - Problem - The resulting Problem instance. - - Raises - ------ - ValueError - If input is neither Problem nor str type. - """ - if isinstance(obj_or_str, Problem): - return obj_or_str - elif isinstance(obj_or_str, str): - return Problem.from_line_fmt(obj_or_str) - else: - raise ValueError(f"Unrecognized input type: {type(obj_or_str)}") - - def binary_str_to_numpy(ss, pos_char, neg_char): """ Convert the characters of the string ss into a numpy array with +1 being wherever diff --git a/tests/test_containers.py b/tests/test_containers.py index b8d7438..f2184fa 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -294,6 +294,62 @@ def test_field_assignment_validation(): pop.x = np.random.random((2)) +def test_default_var_bounds(): + """ + Populations created without bounds treat the decision variables as unbounded. + """ + pop = Population(x=np.random.random((16, 4)), f=np.random.random((16, 2))) + np.testing.assert_array_equal(pop.var_lower_bounds, np.full(4, -np.inf)) + np.testing.assert_array_equal(pop.var_upper_bounds, np.full(4, np.inf)) + + # The default bounds must survive the operations which pass them along + assert (pop + pop).var_lower_bounds.shape == (4,) + np.testing.assert_array_equal(pop[:4].var_upper_bounds, np.full(4, np.inf)) + assert pop == pop[:] + + +def test_var_bounds_validation(): + """ + Bounds must be sized to the decision variables and ordered lower <= upper. + """ + x = np.random.random((16, 3)) + + with pytest.raises(ValidationError, match="Length of var_lower_bounds must match number of decision variables"): + Population(x=x, var_lower_bounds=np.zeros(2)) + + with pytest.raises(ValidationError, match="var_upper_bounds must be 1D"): + Population(x=x, var_upper_bounds=np.ones((1, 3))) + + with pytest.raises(ValidationError, match=r"lower > upper at indices \[1\]"): + Population(x=x, var_lower_bounds=np.array([0.0, 1.0, 0.0]), var_upper_bounds=np.array([1.0, 0.0, 1.0])) + + # Equal lower and upper bounds are allowed (degenerate variable) + Population(x=x, var_lower_bounds=np.zeros(3), var_upper_bounds=np.zeros(3)) + + +def test_var_bounds_consistency(): + """ + Adding populations requires matching bounds and carries them into the result. + """ + kwargs = dict(f=np.random.random((16, 2)), var_lower_bounds=np.zeros(3), var_upper_bounds=np.ones(3)) + pop1 = Population(x=np.random.random((16, 3)), **kwargs) + pop2 = Population(x=np.random.random((16, 3)), **kwargs) + np.testing.assert_array_equal((pop1 + pop2).var_upper_bounds, np.ones(3)) + + pop3 = Population( + x=np.random.random((16, 3)), + f=np.random.random((16, 2)), + var_lower_bounds=np.zeros(3), + var_upper_bounds=2 * np.ones(3), + ) + with pytest.raises(ValueError, match="var_upper_bounds are inconsistent between populations"): + pop1 + pop3 + + # Histories must also have consistent bounds across their reports + with pytest.raises(ValidationError, match="Inconsistent var_upper_bounds in reports"): + History(reports=[pop1, pop3], problem="") + + def test_overwrite(): # Create a randomized Experiment object experiment1 = Experiment.from_random( From d957c3ac9d0e3c6b6a9ac4a3eb4b8d3ba0a690fd Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Fri, 14 Aug 2026 00:37:32 -0700 Subject: [PATCH 3/8] add bounds to hdf5 output --- src/paretobench/containers.py | 12 +++++-- tests/test_containers.py | 60 ++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/paretobench/containers.py b/src/paretobench/containers.py index 5f4c59b..6391aaa 100644 --- a/src/paretobench/containers.py +++ b/src/paretobench/containers.py @@ -885,6 +885,8 @@ def _to_h5py_group(self, g: h5py.Group): # Save the configuration data if self.reports: + g["x"].attrs["lower_bounds"] = self.reports[0].var_lower_bounds + g["x"].attrs["upper_bounds"] = self.reports[0].var_upper_bounds g["f"].attrs["directions"] = self.reports[0].obj_directions g["g"].attrs["directions"] = self.reports[0].constraint_directions g["g"].attrs["targets"] = self.reports[0].constraint_targets @@ -914,6 +916,10 @@ def _from_h5py_group(cls, grp: h5py.Group, file_version: str): constraint_directions = grp["g"].attrs.get("directions", None) constraint_targets = grp["g"].attrs.get("targets", None) + # Files written before version 1.2.0 have no decision variable bounds and are treated as unbounded + var_lower_bounds = grp["x"].attrs.get("lower_bounds", None) + var_upper_bounds = grp["x"].attrs.get("upper_bounds", None) + # Before file version 1.1.0 which introduced explicit constraint directions, # the default constraint type was g(x) >= 0.0 if file_version == "1.0.0": @@ -940,6 +946,8 @@ def _from_h5py_group(cls, grp: h5py.Group, file_version: str): obj_directions=obj_directions, constraint_directions=constraint_directions, constraint_targets=constraint_targets, + var_lower_bounds=var_lower_bounds, + var_upper_bounds=var_upper_bounds, ) ) start_idx += pop_size @@ -1437,7 +1445,7 @@ class Experiment(BaseModel): software_version: str = "" comment: str = "" creation_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) - file_version: str = "1.1.0" + file_version: str = "1.2.0" def __eq__(self, other): if not isinstance(other, Experiment): @@ -1560,7 +1568,7 @@ def save(self, fname): f.attrs["software_version"] = self.software_version f.attrs["comment"] = self.comment f.attrs["creation_time"] = self.creation_time.isoformat() - f.attrs["file_version"] = "1.1.0" + f.attrs["file_version"] = "1.2.0" f.attrs["file_format"] = "ParetoBench Multi-Objective Optimization Data" # Calculate the necessary zero padding based on the number of runs diff --git a/tests/test_containers.py b/tests/test_containers.py index f2184fa..1edd3d2 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,4 +1,5 @@ from pydantic import ValidationError +import h5py import numpy as np import os import pytest @@ -36,7 +37,8 @@ def test_load_legacy_files(test_file): @pytest.mark.parametrize("generate_names", [False, True]) -def test_experiment_save_load(generate_names): +@pytest.mark.parametrize("generate_bounds", [False, True]) +def test_experiment_save_load(generate_names, generate_bounds): """ Make a randomized experiment, save it to disk, load it, and then confirm everything matches. """ @@ -50,6 +52,7 @@ def test_experiment_save_load(generate_names): pop_size=50, generate_names=generate_names, generate_obj_constraint_settings=True, + generate_bounds=generate_bounds, ) # Use a temporary directory to save the file @@ -294,6 +297,61 @@ def test_field_assignment_validation(): pop.x = np.random.random((2)) +def test_save_load_var_bounds_backwards_compatible(): + """ + Files written before the bounds were added (file version 1.2.0) load as unbounded populations. + """ + experiment = Experiment.from_random( + n_histories=2, + n_populations=3, + n_objectives=2, + n_decision_vars=4, + n_constraints=1, + pop_size=8, + generate_bounds=True, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + file_path = os.path.join(tmpdir, "test.h5") + experiment.save(file_path) + + # Strip the bounds attributes to imitate a file written by an older version of ParetoBench + with h5py.File(file_path, mode="r+") as f: + f.attrs["file_version"] = "1.1.0" + for run_grp in [f[k] for k in f if k.startswith("run_")]: + del run_grp["x"].attrs["lower_bounds"] + del run_grp["x"].attrs["upper_bounds"] + + loaded_experiment = Experiment.load(file_path) + assert loaded_experiment.file_version == "1.1.0" + for run in loaded_experiment.runs: + for report in run.reports: + np.testing.assert_array_equal(report.var_lower_bounds, np.full(report.n, -np.inf)) + np.testing.assert_array_equal(report.var_upper_bounds, np.full(report.n, np.inf)) + + +def test_save_load_no_decision_vars(): + """ + Populations without decision variables load with empty bounds arrays. + """ + experiment = Experiment.from_random( + n_histories=1, + n_populations=2, + n_objectives=2, + n_decision_vars=0, + n_constraints=1, + pop_size=8, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + file_path = os.path.join(tmpdir, "test.h5") + experiment.save(file_path) + loaded_experiment = Experiment.load(file_path) + + assert experiment == loaded_experiment + assert loaded_experiment.runs[0].reports[0].var_lower_bounds.shape == (0,) + + def test_default_var_bounds(): """ Populations created without bounds treat the decision variables as unbounded. From de5b3c81da4ec376c052b0d2749c4acc41ddd780 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Fri, 14 Aug 2026 00:49:50 -0700 Subject: [PATCH 4/8] use bounds in plotting; grab bounds from Xopt imports --- src/paretobench/containers.py | 8 ++- src/paretobench/ext/xopt.py | 23 +++++++- src/paretobench/plotting/history.py | 3 ++ src/paretobench/plotting/population.py | 59 +++++++++++++++------ tests/ext/test_xopt.py | 5 ++ tests/test_plotting.py | 73 ++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 19 deletions(-) diff --git a/src/paretobench/containers.py b/src/paretobench/containers.py index 6391aaa..bdbe082 100644 --- a/src/paretobench/containers.py +++ b/src/paretobench/containers.py @@ -631,6 +631,7 @@ def plot_dvar_pairs( upper_bounds: np.ndarray | None = None, color: str | None = None, scale: np.ndarray | None = None, + plot_bounds: bool = True, ): """ Creates a pairs plot (scatter matrix) showing correlations between decision variables @@ -656,14 +657,16 @@ def plot_dvar_pairs( problem : str/Problem, optional The problem for plotting decision variable bounds lower_bounds : array-like, optional - Lower bounds for each decision variable + Lower bounds for each decision variable. Defaults to the bounds carried by the population. upper_bounds : array-like, optional - Upper bounds for each decision variable + Upper bounds for each decision variable. Defaults to the bounds carried by the population. color : str, optional What color should we use for the points. Defaults to selecting from matplotlib color cycler scale : array-like, optional Scale factors for each variable. Must have the same length as the number of decision vars. If None, no scaling is applied. + plot_bounds : bool, optional + Whether to plot the decision variable bounds, by default True. Infinite bounds are not plotted. Returns ------- @@ -687,6 +690,7 @@ def plot_dvar_pairs( upper_bounds=upper_bounds, color=color, scale=scale, + plot_bounds=plot_bounds, ) diff --git a/src/paretobench/ext/xopt.py b/src/paretobench/ext/xopt.py index 0527a0a..58fc7e2 100644 --- a/src/paretobench/ext/xopt.py +++ b/src/paretobench/ext/xopt.py @@ -15,11 +15,24 @@ # Handle xopt 2.x and 3.x constraint/objective accessor styles try: from xopt.vocs import GreaterThanConstraint - from gest_api.vocs import LessThanConstraint, MaximizeObjective, MinimizeObjective + from gest_api.vocs import ( + ContinuousVariable, + DiscreteVariable, + LessThanConstraint, + MaximizeObjective, + MinimizeObjective, + ) def _constraint_value(c): return c.value + def _variable_bounds(var): + if isinstance(var, ContinuousVariable): + return var.domain[0], var.domain[1] + elif isinstance(var, DiscreteVariable): + raise ValueError("DiscreteVariable is currently not supported by ParetoBench") + raise ValueError(f"Unrecognized variable type: {type(var)}") + def _constraint_direction(c): if isinstance(c, GreaterThanConstraint): return ">" @@ -39,6 +52,9 @@ def _objective_direction(obj): def _constraint_value(c): return c[1] + def _variable_bounds(var): + return var[0], var[1] + def _constraint_direction(c): if c[0] == "GREATER_THAN": return ">" @@ -160,6 +176,9 @@ def population_from_dataframe(df: pd.DataFrame, vocs: VOCS, errors_as_constraint Population Population object with the loaded data """ + # Get the decision variable bounds. Note that vocs.bounds is not used here as its shape changed between xopt 2.x and 3.x + var_bounds = [_variable_bounds(vocs.variables[name]) for name in vocs.variable_names] + # Get base constraints if they exist g = df[vocs.constraint_names].to_numpy() if vocs.constraints else None names_g = vocs.constraint_names @@ -190,6 +209,8 @@ def population_from_dataframe(df: pd.DataFrame, vocs: VOCS, errors_as_constraint obj_directions="".join([_objective_direction(vocs.objectives[name]) for name in vocs.objective_names]), constraint_directions="".join(constraint_directions), constraint_targets=np.array(constraint_targets), + var_lower_bounds=np.array([b[0] for b in var_bounds], dtype=np.float64), + var_upper_bounds=np.array([b[1] for b in var_bounds], dtype=np.float64), ) diff --git a/src/paretobench/plotting/history.py b/src/paretobench/plotting/history.py index dd9746e..b65cfaa 100644 --- a/src/paretobench/plotting/history.py +++ b/src/paretobench/plotting/history.py @@ -298,6 +298,7 @@ def history_dvar_pairs( hist_bins=hist_bins, show_names=show_names, scale=scale, + plot_bounds=plot_bounds, ) if generation_mode == "cumulative": @@ -336,12 +337,14 @@ def history_dvar_pairs( # Only plot bounds on the last iteration if requested if plot_idx == len(indices) - 1: + plot_settings["plot_bounds"] = plot_bounds if plot_bounds and user_specified_bounds: plot_settings["lower_bounds"] = lower_bounds plot_settings["upper_bounds"] = upper_bounds elif plot_bounds and history.problem is not None: plot_settings["problem"] = history.problem else: + plot_settings["plot_bounds"] = False plot_settings["problem"] = None plot_settings["lower_bounds"] = None plot_settings["upper_bounds"] = None diff --git a/src/paretobench/plotting/population.py b/src/paretobench/plotting/population.py index 242be20..4a0b6f2 100644 --- a/src/paretobench/plotting/population.py +++ b/src/paretobench/plotting/population.py @@ -290,6 +290,28 @@ def population_obj_scatter( return fig, ax +def _draw_bound(line_fn, bounds, idx, scale, props): + """ + Draws one decision variable bound onto an axis. Unset and infinite bounds are skipped as they have no location + on the plot and would ruin the axis limits. + + Parameters + ---------- + line_fn : callable + The axis method used to draw the line (`ax.axvline` or `ax.axhline`). + bounds : array-like or None + The lower or upper bounds of all decision variables. + idx : int + Index of the decision variable being drawn. + scale : float + Scale factor applied to this decision variable. + props : dict + Line properties passed through to `line_fn`. + """ + if bounds is not None and np.isfinite(bounds[idx]): + line_fn(scale * bounds[idx], **props) + + def population_dvar_pairs( population: Population, dvars: Optional[Union[int, slice, List[int], Tuple[int, int]]] = None, @@ -304,6 +326,7 @@ def population_dvar_pairs( upper_bounds: Optional[np.ndarray] = None, color: Optional[str] = None, scale: Optional[np.ndarray] = None, + plot_bounds: bool = True, ): """ Creates a pairs plot (scatter matrix) showing correlations between decision variables @@ -331,14 +354,16 @@ def population_dvar_pairs( problem : str/Problem, optional The problem for plotting decision variable bounds lower_bounds : array-like, optional - Lower bounds for each decision variable + Lower bounds for each decision variable. Defaults to the bounds carried by the population. upper_bounds : array-like, optional - Upper bounds for each decision variable + Upper bounds for each decision variable. Defaults to the bounds carried by the population. color : str, optional What color should we use for the points. Defaults to selecting from matplotlib color cycler scale : array-like, optional Scale factors for each variable. Must have the same length as the number of decision vars. If None, no scaling is applied. + plot_bounds : bool, optional + Whether to plot the decision variable bounds, by default True. Infinite bounds are not plotted. Returns ------- @@ -366,10 +391,6 @@ def population_dvar_pairs( var_indices = np.array(selection_to_indices(dvars, population.n)) n_vars = len(var_indices) - # Default, don't show bounds - lower_bounds = None - upper_bounds = None - # Handle user specified problem if problem is not None: if (lower_bounds is not None) or (upper_bounds is not None): @@ -382,6 +403,16 @@ def population_dvar_pairs( lower_bounds = problem.var_lower_bounds upper_bounds = problem.var_upper_bounds + # Fall back onto the bounds carried by the population itself + elif plot_bounds and (lower_bounds is None) and (upper_bounds is None): + lower_bounds = population.var_lower_bounds + upper_bounds = population.var_upper_bounds + + # The bounds are not plotted when the user asks us not to + if not plot_bounds: + lower_bounds = None + upper_bounds = None + # Validate and convert bounds to numpy arrays if provided if lower_bounds is not None: lower_bounds = np.asarray(lower_bounds) @@ -483,10 +514,8 @@ def population_dvar_pairs( base_color = patches[0].get_facecolor() # Add vertical bound lines to histograms - if lower_bounds is not None: - ax.axvline(scale[var_indices[i]] * lower_bounds[var_indices[i]], **bound_props) - if upper_bounds is not None: - ax.axvline(scale[var_indices[i]] * upper_bounds[var_indices[i]], **bound_props) + _draw_bound(ax.axvline, lower_bounds, var_indices[i], scale[var_indices[i]], bound_props) + _draw_bound(ax.axvline, upper_bounds, var_indices[i], scale[var_indices[i]], bound_props) # Off-diagonal plots (scatter plots) else: @@ -504,12 +533,10 @@ def population_dvar_pairs( base_color = scatter.get_facecolor()[0] # Get the color that matplotlib assigned # Add bound lines to scatter plots - if lower_bounds is not None: - ax.axvline(scale[var_indices[j]] * lower_bounds[var_indices[j]], **bound_props) # x-axis bound - ax.axhline(scale[var_indices[i]] * lower_bounds[var_indices[i]], **bound_props) # y-axis bound - if upper_bounds is not None: - ax.axvline(scale[var_indices[j]] * upper_bounds[var_indices[j]], **bound_props) # x-axis bound - ax.axhline(scale[var_indices[i]] * upper_bounds[var_indices[i]], **bound_props) # y-axis bound + _draw_bound(ax.axvline, lower_bounds, var_indices[j], scale[var_indices[j]], bound_props) + _draw_bound(ax.axhline, lower_bounds, var_indices[i], scale[var_indices[i]], bound_props) + _draw_bound(ax.axvline, upper_bounds, var_indices[j], scale[var_indices[j]], bound_props) + _draw_bound(ax.axhline, upper_bounds, var_indices[i], scale[var_indices[i]], bound_props) if i == n_vars - 1: ax.set_xlabel(var_names[j]) if j == 0: diff --git a/tests/ext/test_xopt.py b/tests/ext/test_xopt.py index ed1427d..0efd0d5 100644 --- a/tests/ext/test_xopt.py +++ b/tests/ext/test_xopt.py @@ -182,6 +182,11 @@ def df_comp(df1, df2): assert all(tp.constraint_targets == [0.0, 0.0]) assert tp.fevals == (idx + 1) * population_size + # Confirm the decision variable bounds came from the VOCS + vocs_bounds = [_variable_bounds(tnk_vocs.variables[name]) for name in tnk_vocs.variable_names] + np.testing.assert_allclose(tp.var_lower_bounds, [b[0] for b in vocs_bounds]) + np.testing.assert_allclose(tp.var_upper_bounds, [b[1] for b in vocs_bounds]) + # Confirm data is correct df_comp(rx, pd.DataFrame(tp.x, columns=tp.names_x)) rf.columns = [x.removeprefix("objective_") for x in rf.columns] diff --git a/tests/test_plotting.py b/tests/test_plotting.py index d8381cb..04d0922 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -231,6 +231,79 @@ def test_population_dvar_pairs_selection(): plt.close(fig) +def get_bound_lines(ax): + """ + Returns the x locations of the vertical bound lines drawn onto one of the histogram axes of a dvar pairs plot. + """ + return sorted(line.get_xdata()[0] for line in ax.get_lines() if line.get_linestyle() == "--") + + +def test_population_dvar_pairs_bounds(): + """ + The population's own bounds are used for the bound lines unless the caller overrides or disables them. + """ + pop = Population(x=np.random.random((5, 3)), f=np.random.random((5, 2))) + bounded = Population(x=pop.x, f=pop.f, var_lower_bounds=-np.ones(3), var_upper_bounds=2 * np.ones(3)) + + # The unbounded population draws nothing since its bounds are infinite + fig, axes = population_dvar_pairs(pop) + assert get_bound_lines(axes[0, 0]) == [] + plt.close(fig) + + # The bounded population draws its own bounds + fig, axes = population_dvar_pairs(bounded) + assert get_bound_lines(axes[0, 0]) == [-1.0, 2.0] + plt.close(fig) + + # Which can be turned off + fig, axes = population_dvar_pairs(bounded, plot_bounds=False) + assert get_bound_lines(axes[0, 0]) == [] + plt.close(fig) + + # User specified bounds take precedence over the population's + fig, axes = population_dvar_pairs(bounded, lower_bounds=np.zeros(3), upper_bounds=np.ones(3)) + assert get_bound_lines(axes[0, 0]) == [0.0, 1.0] + plt.close(fig) + + # A problem may also be used as the source of bounds + fig, axes = population_dvar_pairs(bounded, problem="ZDT1 (n=3)") + assert get_bound_lines(axes[0, 0]) == [0.0, 1.0] + plt.close(fig) + + # Only one source of bounds may be specified + with pytest.raises(ValueError, match="Only specify one of problem or the upper/lower bounds"): + population_dvar_pairs(bounded, problem="ZDT1 (n=3)", lower_bounds=np.zeros(3)) + + +def test_population_dvar_pairs_partial_bounds(): + """ + Bound lines are skipped for the individual variables which are unbounded. + """ + pop = Population( + x=np.random.random((5, 2)), + f=np.random.random((5, 2)), + var_lower_bounds=np.array([-1.0, -np.inf]), + var_upper_bounds=np.array([2.0, np.inf]), + ) + + fig, axes = population_dvar_pairs(pop) + assert get_bound_lines(axes[0, 0]) == [-1.0, 2.0] + assert get_bound_lines(axes[1, 1]) == [] + plt.close(fig) + + +def test_history_dvar_pairs_bounds(): + hist = History.from_random(3, 2, 3, 1, 10, generate_bounds=True) + + fig, axes = hist.plot_dvar_pairs() + assert get_bound_lines(axes[0, 0]) == [] + plt.close(fig) + + fig, axes = hist.plot_dvar_pairs(plot_bounds=True, lower_bounds=np.zeros(3), upper_bounds=np.ones(3)) + assert get_bound_lines(axes[0, 0]) == [0.0, 1.0] + plt.close(fig) + + def test_population_dvar_pairs_errors(): """Test error cases""" pop = Population.from_random(n_objectives=2, n_decision_vars=3, n_constraints=1, pop_size=5) From 2942861829b8edd969cd12efe3373b866f9ba552 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Sat, 15 Aug 2026 12:24:59 -0700 Subject: [PATCH 5/8] add option to redo all manifest files --- tests/generate_file_version_data.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/generate_file_version_data.py b/tests/generate_file_version_data.py index 18b38a9..d0d1561 100644 --- a/tests/generate_file_version_data.py +++ b/tests/generate_file_version_data.py @@ -84,12 +84,19 @@ def main(): ) parser.add_argument("--out-dir", type=Path, default=DEFAULT_OUT_DIR, help="directory to save the file into") parser.add_argument("--force", action="store_true", help="overwrite an existing file for this version") - parser.add_argument("--manifest", type=Path, help="rewrite the manifest of an existing file and exit") + parser.add_argument( + "--refresh-manifests", + type=Path, + nargs="*", + help="rewrite the manifests of files which already exist, defaulting to every file in the output directory", + ) args = parser.parse_args() - # Refresh the manifest of a file which already exists (used to bootstrap files saved by older versions) - if args.manifest is not None: - print(f"Wrote {write_manifest(args.manifest)}") + # Rewrite the manifests of files which already exist. Needed whenever the contents of a manifest change, such as + # when a new field is added to the containers, and to bootstrap the manifests of files saved by older versions. + if args.refresh_manifests is not None: + for path in args.refresh_manifests or sorted(args.out_dir.glob("*.h5")): + print(f"Wrote {write_manifest(path)}") return # Save the data, then name the file after the version which actually ended up in it From 9b1928977bdd1022a11a5a319132042dbc95f9c6 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Sat, 15 Aug 2026 12:26:56 -0700 Subject: [PATCH 6/8] add bounds, refresh files --- .../paretobench_file_format_v1.0.0.json | 672 ++++++++++++++++++ .../paretobench_file_format_v1.1.0.json | 672 ++++++++++++++++++ tests/utils.py | 2 + 3 files changed, 1346 insertions(+) diff --git a/tests/test_data/file_versions/paretobench_file_format_v1.0.0.json b/tests/test_data/file_versions/paretobench_file_format_v1.0.0.json index e6fccae..5848279 100644 --- a/tests/test_data/file_versions/paretobench_file_format_v1.0.0.json +++ b/tests/test_data/file_versions/paretobench_file_format_v1.0.0.json @@ -23,6 +23,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "3b2d74eed2b6600433de939b181f2856fd70cff92d1e32909ac7ac88a1f77c28" }, { @@ -39,6 +53,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "119795280e0e1fb558ecef619d4b6083f4aecbcc34cee074fce3fc6954aeae39" }, { @@ -55,6 +83,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "b37443ac45a3bde81bca6686a6944783486812472181ac3619e2ee855dec6d59" }, { @@ -71,6 +113,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1630ebf46c0d910fff487a13b95c3072fd90f0ff1fe24b97c36efc63bca838b0" }, { @@ -87,6 +143,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "dd1e5b71e3d6215173745aae9e64219982ff4062d5a8345c977c20e649b5a5cb" }, { @@ -103,6 +173,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "be66f8a208a6bf59dc4478761cfac501d598fe3871587234d3cdeb1b275732e5" }, { @@ -119,6 +203,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "f991d73ee8b4949445a5f91f3dadde91552dd973a3f67bf833031a3b144241d2" }, { @@ -135,6 +233,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "09ab4b8ea994852ca3fa95538e26ee13239892b70c3c985ba1976950ee710cc9" } ] @@ -157,6 +269,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5129b12bc092720878f65ea8e8e64c9c8e10bc6525b860065f50f205af1aaa30" }, { @@ -173,6 +299,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c80488e2e77c8c7fb07e192a8f9cd13b42f72f11d0fa163d719bc88f2bf1d4b7" }, { @@ -189,6 +329,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "ee1f2f7ef29d5187d13ee669f998237cafc28ea07ed456c5aa3a0d0bdf515e8c" }, { @@ -205,6 +359,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "3ece64d9ccb0fb86003eaafa4f6a7856af7be2adb4e245d9ea2c853f3d71e3d8" }, { @@ -221,6 +389,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c0c5551a141602e30a80a6796623b695849388591c00fc82c8b34af0520fb661" }, { @@ -237,6 +419,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "42625c5e8f003a3d4a1b93de25a9b8fd26b3e2ab9886fa3a97269bcaf8bd02a2" }, { @@ -253,6 +449,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "d86d9f3cb6b714bf93d17bd33f7ee0a74498b52698e921a2f0438cf3a07c2df7" }, { @@ -269,6 +479,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "40aaaf70ad4952885313cd34311f415c24465187959559f82fe86f3aab5b825f" } ] @@ -291,6 +515,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "6bbc30fdb0e0c1d7ec230cf6dc41d161fd6865f24c6df5df9aee491f36051a67" }, { @@ -307,6 +545,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8651c618c24b08877aa5dcc7ef1ec4cf8d445b25c1dccc640bbe92ad7f4a16c9" }, { @@ -323,6 +575,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1ceb5e1607a108f9ab4b93837b9901ddede725dda4d8502d713dd706dbbb6825" }, { @@ -339,6 +605,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8ab0082734025e52fbfe729d76979a91b9c83e39c31320dbb264176f90e38514" }, { @@ -355,6 +635,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "22837213bf2a410f53800c2a28a20a6e650916366556d5f5bd47f2dfd13e7853" }, { @@ -371,6 +665,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "0acad307f3e412fbffc9c901a205f8931d569841749089bc8956539d7c24a281" }, { @@ -387,6 +695,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c2be26ae7b20916e87308eaaab9fffc0462d05f28fe74bdc6c61eac8b1532991" }, { @@ -403,6 +725,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "df3e9b06e4777cfbadd0412e78484785b22d667a11dc906b1659f49b79894f4e" } ] @@ -425,6 +761,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "240594fa871c1c47eaa65f8d603b06fad4a635e8a45ddc31a0029e2a6b2d4d1b" }, { @@ -441,6 +791,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5ff66c434ffbd1aa9b95d04dd3bd39ce9403d91d0f1034ed30b9cfef08de24b7" }, { @@ -457,6 +821,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "20d33df82efff7fbfcb1657e6fa3bfeac1282030eae63e31511481710a1b4926" }, { @@ -473,6 +851,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "d86606ca44168d2515236f57472e6b1ae3731eb10ef7c201567682043228db3b" }, { @@ -489,6 +881,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "6fdfff6603e039d843b46a1cfaf5b217b8c8d51c7978cbcfbbc541b1a5b8aa81" }, { @@ -505,6 +911,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "0a6febc59f3123b6f80343dc5b424dc154f8a53d8bb1948e0c2b56f5eff95c24" }, { @@ -521,6 +941,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c22b349b1a39dda308828bd41499db3b9fef874516ce53e0e08a4af716d9ff9c" }, { @@ -537,6 +971,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "270bcb920898d79b9fd67c9a5c89133bf40a76366641cf3d0227ed42259cef19" } ] @@ -559,6 +1007,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "936910b0844695c0bdb24f530781a2d6c2436c76d19958de2f391c13648a9da5" }, { @@ -575,6 +1037,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1d5b2eff103a96a2046b11f9e416750346ebb1359bd9e376ea1f24893be184b2" }, { @@ -591,6 +1067,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "55d78e8bf10287f6857049b720c1f83f8a81db87521dc44a8a3b14a165150e7f" }, { @@ -607,6 +1097,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5172e91510bc1f18b8f47edc1e13604ba0be6d7e2e7f2bad3a49efec6d9fbe51" }, { @@ -623,6 +1127,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "de77677aa32987444792688788dec6c1bc7d3c2509397e1fa9766627625877fb" }, { @@ -639,6 +1157,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "733ce3a1c45dcd1f511cd507cdf708d53b2809be50e60cf3a1b0260d47fd3f54" }, { @@ -655,6 +1187,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "a1362da78318ba8f67e36d6cd22d3e50b2345721cc6d308a6dc08e690cc7f8db" }, { @@ -671,6 +1217,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "ef9d2bb572962c77dd566adf13ae1ef7d32400b064adbce3f8b023f14c612225" } ] @@ -693,6 +1253,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "e6b663e84e7955e5ddf753f9b0d55d89b31305e5c5eb17ae0bdb10dec010e709" }, { @@ -709,6 +1283,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "45edf82f5a101789bfa222ed329243abcc781ce1a21bbd403ead41260543a9a6" }, { @@ -725,6 +1313,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "fab599d313a85a247de1647da245c6a43b36833cb171776ea21afcbf1bf0d585" }, { @@ -741,6 +1343,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8466b5596f0214a13d971f6552ef11bf90295c22e4c40dd631c659660fe95597" }, { @@ -757,6 +1373,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "9a395681bfa39ffdf8de35f84efff9f7aea3e78922fc83a4d592b426894eb3f1" }, { @@ -773,6 +1403,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c19e3f2d09a32777ce4dd264d88d9668d9c3a4d6213f8685b1a75651bf0d8eba" }, { @@ -789,6 +1433,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1103dbced7246f9385555edbd1058382a322f2e52bf2a5909543ac5233193c3b" }, { @@ -805,6 +1463,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "29b0adcb029240572c1254f6184dc30a4a447ee011c62a9fc065af4d420fb715" } ] diff --git a/tests/test_data/file_versions/paretobench_file_format_v1.1.0.json b/tests/test_data/file_versions/paretobench_file_format_v1.1.0.json index bfbb218..5966078 100644 --- a/tests/test_data/file_versions/paretobench_file_format_v1.1.0.json +++ b/tests/test_data/file_versions/paretobench_file_format_v1.1.0.json @@ -23,6 +23,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "3b2d74eed2b6600433de939b181f2856fd70cff92d1e32909ac7ac88a1f77c28" }, { @@ -39,6 +53,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "119795280e0e1fb558ecef619d4b6083f4aecbcc34cee074fce3fc6954aeae39" }, { @@ -55,6 +83,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "b37443ac45a3bde81bca6686a6944783486812472181ac3619e2ee855dec6d59" }, { @@ -71,6 +113,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1630ebf46c0d910fff487a13b95c3072fd90f0ff1fe24b97c36efc63bca838b0" }, { @@ -87,6 +143,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "dd1e5b71e3d6215173745aae9e64219982ff4062d5a8345c977c20e649b5a5cb" }, { @@ -103,6 +173,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "be66f8a208a6bf59dc4478761cfac501d598fe3871587234d3cdeb1b275732e5" }, { @@ -119,6 +203,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "f991d73ee8b4949445a5f91f3dadde91552dd973a3f67bf833031a3b144241d2" }, { @@ -135,6 +233,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "09ab4b8ea994852ca3fa95538e26ee13239892b70c3c985ba1976950ee710cc9" } ] @@ -157,6 +269,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5129b12bc092720878f65ea8e8e64c9c8e10bc6525b860065f50f205af1aaa30" }, { @@ -173,6 +299,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c80488e2e77c8c7fb07e192a8f9cd13b42f72f11d0fa163d719bc88f2bf1d4b7" }, { @@ -189,6 +329,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "ee1f2f7ef29d5187d13ee669f998237cafc28ea07ed456c5aa3a0d0bdf515e8c" }, { @@ -205,6 +359,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "3ece64d9ccb0fb86003eaafa4f6a7856af7be2adb4e245d9ea2c853f3d71e3d8" }, { @@ -221,6 +389,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c0c5551a141602e30a80a6796623b695849388591c00fc82c8b34af0520fb661" }, { @@ -237,6 +419,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "42625c5e8f003a3d4a1b93de25a9b8fd26b3e2ab9886fa3a97269bcaf8bd02a2" }, { @@ -253,6 +449,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "d86d9f3cb6b714bf93d17bd33f7ee0a74498b52698e921a2f0438cf3a07c2df7" }, { @@ -269,6 +479,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "40aaaf70ad4952885313cd34311f415c24465187959559f82fe86f3aab5b825f" } ] @@ -291,6 +515,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "6bbc30fdb0e0c1d7ec230cf6dc41d161fd6865f24c6df5df9aee491f36051a67" }, { @@ -307,6 +545,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8651c618c24b08877aa5dcc7ef1ec4cf8d445b25c1dccc640bbe92ad7f4a16c9" }, { @@ -323,6 +575,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1ceb5e1607a108f9ab4b93837b9901ddede725dda4d8502d713dd706dbbb6825" }, { @@ -339,6 +605,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8ab0082734025e52fbfe729d76979a91b9c83e39c31320dbb264176f90e38514" }, { @@ -355,6 +635,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "22837213bf2a410f53800c2a28a20a6e650916366556d5f5bd47f2dfd13e7853" }, { @@ -371,6 +665,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "0acad307f3e412fbffc9c901a205f8931d569841749089bc8956539d7c24a281" }, { @@ -387,6 +695,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c2be26ae7b20916e87308eaaab9fffc0462d05f28fe74bdc6c61eac8b1532991" }, { @@ -403,6 +725,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "df3e9b06e4777cfbadd0412e78484785b22d667a11dc906b1659f49b79894f4e" } ] @@ -425,6 +761,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "240594fa871c1c47eaa65f8d603b06fad4a635e8a45ddc31a0029e2a6b2d4d1b" }, { @@ -441,6 +791,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5ff66c434ffbd1aa9b95d04dd3bd39ce9403d91d0f1034ed30b9cfef08de24b7" }, { @@ -457,6 +821,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "20d33df82efff7fbfcb1657e6fa3bfeac1282030eae63e31511481710a1b4926" }, { @@ -473,6 +851,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "d86606ca44168d2515236f57472e6b1ae3731eb10ef7c201567682043228db3b" }, { @@ -489,6 +881,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "6fdfff6603e039d843b46a1cfaf5b217b8c8d51c7978cbcfbbc541b1a5b8aa81" }, { @@ -505,6 +911,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "0a6febc59f3123b6f80343dc5b424dc154f8a53d8bb1948e0c2b56f5eff95c24" }, { @@ -521,6 +941,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c22b349b1a39dda308828bd41499db3b9fef874516ce53e0e08a4af716d9ff9c" }, { @@ -537,6 +971,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "270bcb920898d79b9fd67c9a5c89133bf40a76366641cf3d0227ed42259cef19" } ] @@ -559,6 +1007,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "936910b0844695c0bdb24f530781a2d6c2436c76d19958de2f391c13648a9da5" }, { @@ -575,6 +1037,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1d5b2eff103a96a2046b11f9e416750346ebb1359bd9e376ea1f24893be184b2" }, { @@ -591,6 +1067,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "55d78e8bf10287f6857049b720c1f83f8a81db87521dc44a8a3b14a165150e7f" }, { @@ -607,6 +1097,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "5172e91510bc1f18b8f47edc1e13604ba0be6d7e2e7f2bad3a49efec6d9fbe51" }, { @@ -623,6 +1127,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "de77677aa32987444792688788dec6c1bc7d3c2509397e1fa9766627625877fb" }, { @@ -639,6 +1157,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "733ce3a1c45dcd1f511cd507cdf708d53b2809be50e60cf3a1b0260d47fd3f54" }, { @@ -655,6 +1187,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "a1362da78318ba8f67e36d6cd22d3e50b2345721cc6d308a6dc08e690cc7f8db" }, { @@ -671,6 +1217,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "ef9d2bb572962c77dd566adf13ae1ef7d32400b064adbce3f8b023f14c612225" } ] @@ -693,6 +1253,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "e6b663e84e7955e5ddf753f9b0d55d89b31305e5c5eb17ae0bdb10dec010e709" }, { @@ -709,6 +1283,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "45edf82f5a101789bfa222ed329243abcc781ce1a21bbd403ead41260543a9a6" }, { @@ -725,6 +1313,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "fab599d313a85a247de1647da245c6a43b36833cb171776ea21afcbf1bf0d585" }, { @@ -741,6 +1343,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "8466b5596f0214a13d971f6552ef11bf90295c22e4c40dd631c659660fe95597" }, { @@ -757,6 +1373,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "9a395681bfa39ffdf8de35f84efff9f7aea3e78922fc83a4d592b426894eb3f1" }, { @@ -773,6 +1403,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "c19e3f2d09a32777ce4dd264d88d9668d9c3a4d6213f8685b1a75651bf0d8eba" }, { @@ -789,6 +1433,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "1103dbced7246f9385555edbd1058382a322f2e52bf2a5909543ac5233193c3b" }, { @@ -805,6 +1463,20 @@ "names_x": null, "obj_directions": "--", "pop_size": 50, + "var_lower_bounds": [ + -Infinity, + -Infinity, + -Infinity, + -Infinity, + -Infinity + ], + "var_upper_bounds": [ + Infinity, + Infinity, + Infinity, + Infinity, + Infinity + ], "x_sha256": "29b0adcb029240572c1254f6184dc30a4a447ee011c62a9fc065af4d420fb715" } ] diff --git a/tests/utils.py b/tests/utils.py index 779a43a..1e6da95 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -79,6 +79,8 @@ def experiment_to_manifest(exp): "obj_directions": report.obj_directions, "constraint_directions": report.constraint_directions, "constraint_targets": report.constraint_targets.tolist(), + "var_lower_bounds": report.var_lower_bounds.tolist(), + "var_upper_bounds": report.var_upper_bounds.tolist(), "x_sha256": array_digest(report.x), "f_sha256": array_digest(report.f), "g_sha256": array_digest(report.g), From 14e1c35ec1ab1d7f3a97ab0deb6594ec41709301 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Sat, 15 Aug 2026 12:28:54 -0700 Subject: [PATCH 7/8] new file checkpoint --- tests/generate_file_version_data.py | 1 + .../paretobench_file_format_v1.2.0.h5 | Bin 0 -> 41264 bytes .../paretobench_file_format_v1.2.0.json | 560 ++++++++++++++++++ 3 files changed, 561 insertions(+) create mode 100644 tests/test_data/file_versions/paretobench_file_format_v1.2.0.h5 create mode 100644 tests/test_data/file_versions/paretobench_file_format_v1.2.0.json diff --git a/tests/generate_file_version_data.py b/tests/generate_file_version_data.py index d0d1561..1ebdce7 100644 --- a/tests/generate_file_version_data.py +++ b/tests/generate_file_version_data.py @@ -39,6 +39,7 @@ def make_experiment(): pop_size=25, generate_names=True, generate_obj_constraint_settings=True, + generate_bounds=True, ) run.problem = problem runs.append(run) diff --git a/tests/test_data/file_versions/paretobench_file_format_v1.2.0.h5 b/tests/test_data/file_versions/paretobench_file_format_v1.2.0.h5 new file mode 100644 index 0000000000000000000000000000000000000000..81496aaa7457488c11ac2f52726e2acb67abc94f GIT binary patch literal 41264 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 literal 0 HcmV?d00001 diff --git a/tests/test_data/file_versions/paretobench_file_format_v1.2.0.json b/tests/test_data/file_versions/paretobench_file_format_v1.2.0.json new file mode 100644 index 0000000..e595d38 --- /dev/null +++ b/tests/test_data/file_versions/paretobench_file_format_v1.2.0.json @@ -0,0 +1,560 @@ +{ + "author": "ParetoBench test suite", + "comment": "Synthetic data used by the file format regression tests", + "creation_time": "2025-01-01T00:00:00+00:00", + "file_version": "1.2.0", + "name": "file_format_regression", + "runs": [ + { + "metadata": { + "author": "1PJSO", + "date": "2022-08-21", + "description": "Randomly generated metadata", + "version": 2.567597178069545 + }, + "problem": "ZDT1 (n=4)", + "reports": [ + { + "constraint_directions": ">>", + "constraint_targets": [ + 0.26461952968972313, + 0.575533888310394 + ], + "f_sha256": "45a284d7ff1f7adf3bf783a60a9cbaa018eab491be73ae29faaec2a470ff4b1b", + "fevals": 25, + "g_sha256": "bceb32f24b52ee5ef02939578c8cca0739053dbbcedf665b5b4510ff3292b010", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+--", + "pop_size": 25, + "var_lower_bounds": [ + -0.19490100382675557, + -0.7412316659101856, + -0.9369278798585663, + -0.6592678664279883 + ], + "var_upper_bounds": [ + 1.7249940940432136, + 1.1644884116937722, + 1.7482239682739769, + 1.4973568393326229 + ], + "x_sha256": "299d795b83d31661d68ced7b2da76e9c82025cb50a1542e448fdd669dedfa0a6" + }, + { + "constraint_directions": ">>", + "constraint_targets": [ + 0.26461952968972313, + 0.575533888310394 + ], + "f_sha256": "3599839cff2cf01168f040c60c81b1fb4c21b23b850b7a2926a1f9a5fee4e29f", + "fevals": 50, + "g_sha256": "7ed800375c3714ce6675e458a21a6bc95a465304cbc5dfcb8ad8595e98ccb6e1", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+--", + "pop_size": 25, + "var_lower_bounds": [ + -0.19490100382675557, + -0.7412316659101856, + -0.9369278798585663, + -0.6592678664279883 + ], + "var_upper_bounds": [ + 1.7249940940432136, + 1.1644884116937722, + 1.7482239682739769, + 1.4973568393326229 + ], + "x_sha256": "f4079004496a41db3fbb31a7444767cebcc5e4e8f31d35b88f18446a11f891a4" + }, + { + "constraint_directions": ">>", + "constraint_targets": [ + 0.26461952968972313, + 0.575533888310394 + ], + "f_sha256": "e9d0ce4478f37e940a9d9c1e0ad10eaf8150e33c5b6be0f6da37f300a934e251", + "fevals": 75, + "g_sha256": "1f263edc7fcc99491744a795b6c87206f335d050148f9b8e634c5e8c8f862ae1", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+--", + "pop_size": 25, + "var_lower_bounds": [ + -0.19490100382675557, + -0.7412316659101856, + -0.9369278798585663, + -0.6592678664279883 + ], + "var_upper_bounds": [ + 1.7249940940432136, + 1.1644884116937722, + 1.7482239682739769, + 1.4973568393326229 + ], + "x_sha256": "0794101e8ae726b2e44c5c7b36bcdde3a56b71176709d581decc47b4573cc521" + }, + { + "constraint_directions": ">>", + "constraint_targets": [ + 0.26461952968972313, + 0.575533888310394 + ], + "f_sha256": "51d5c48f6e3811959e703f3b12820de79a906ca308374d6f31f1843e6aac4120", + "fevals": 100, + "g_sha256": "22042afb2278d9a5e6407d031bfb6f9a37fdea6f6fd106450aae48bd65ee8615", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+--", + "pop_size": 25, + "var_lower_bounds": [ + -0.19490100382675557, + -0.7412316659101856, + -0.9369278798585663, + -0.6592678664279883 + ], + "var_upper_bounds": [ + 1.7249940940432136, + 1.1644884116937722, + 1.7482239682739769, + 1.4973568393326229 + ], + "x_sha256": "680dd84014c285805436d17cf40b17f2150326c745189599f103163a01ce7222" + } + ] + }, + { + "metadata": { + "author": "SK1WJ", + "date": "2024-03-19", + "description": "Randomly generated metadata", + "version": 2.81949251193648 + }, + "problem": "CTP1 (n=4)", + "reports": [ + { + "constraint_directions": "<>", + "constraint_targets": [ + 0.607741847385851, + 0.4877645594977067 + ], + "f_sha256": "19a5a9571032626303fc6d1c9c1480c348ed10fa9dbf898174586f2137127af2", + "fevals": 25, + "g_sha256": "52f0669483cf200f107551b33af830e70c9b0c25f85df49aea6560cc86a2a707", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.01331611324646731, + -0.6062619062265414, + -0.9890880753568537, + -0.818101050024029 + ], + "var_upper_bounds": [ + 1.340604643884352, + 1.1520470250444221, + 1.784058614467154, + 1.7439378206317415 + ], + "x_sha256": "c720a83b3a64c9cecc82fd4e11210e1b61550a76af11ec996a0e89e0020e3833" + }, + { + "constraint_directions": "<>", + "constraint_targets": [ + 0.607741847385851, + 0.4877645594977067 + ], + "f_sha256": "37219e5328d5b2d206078ea266e8c3306bb1bb95047bc262bdbd1dbb5899a8e4", + "fevals": 50, + "g_sha256": "6f22bedd12aafd66accb8108bc64c62edb0f322e3bcac50567761a67144812ca", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.01331611324646731, + -0.6062619062265414, + -0.9890880753568537, + -0.818101050024029 + ], + "var_upper_bounds": [ + 1.340604643884352, + 1.1520470250444221, + 1.784058614467154, + 1.7439378206317415 + ], + "x_sha256": "0a13930c21ef2b7ae02ef6e38b896efc62b59a49eebe7fb9b9e419b829cd8df7" + }, + { + "constraint_directions": "<>", + "constraint_targets": [ + 0.607741847385851, + 0.4877645594977067 + ], + "f_sha256": "3d03ac0b6ee6325de60f74b06c819a01019317bd8f339c6a1437afe19c707856", + "fevals": 75, + "g_sha256": "97b2a7d7bdf621396c6d9998087d1d6d1e3e94c3ccd3918cc36840370f523d94", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.01331611324646731, + -0.6062619062265414, + -0.9890880753568537, + -0.818101050024029 + ], + "var_upper_bounds": [ + 1.340604643884352, + 1.1520470250444221, + 1.784058614467154, + 1.7439378206317415 + ], + "x_sha256": "2fe7b02c23b60d22fbd9f6a1f0f13a4d21b28e362a58cff23ab9c75b89120e18" + }, + { + "constraint_directions": "<>", + "constraint_targets": [ + 0.607741847385851, + 0.4877645594977067 + ], + "f_sha256": "d2bd488ca7322083b2c96575440115c537388ea5a6ec0543b39d5b0c1f77630f", + "fevals": 100, + "g_sha256": "d848fa2f9da8c622a3ebcc1153f83510248deeb313eae197bcb7bd2fc22bb197", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.01331611324646731, + -0.6062619062265414, + -0.9890880753568537, + -0.818101050024029 + ], + "var_upper_bounds": [ + 1.340604643884352, + 1.1520470250444221, + 1.784058614467154, + 1.7439378206317415 + ], + "x_sha256": "cb2ae15fa3453a2abd76a0ccad796d590d29aff7b38bd222b01c46d0752ba079" + } + ] + }, + { + "metadata": { + "author": "06YQD", + "date": "2024-04-27", + "description": "Randomly generated metadata", + "version": 1.8683436709075674 + }, + "problem": "TNK", + "reports": [ + { + "constraint_directions": "<<", + "constraint_targets": [ + 0.6771508915552242, + 0.12116145688982427 + ], + "f_sha256": "73c33b07b4370567b7eeaea24bb48613637d7b42ed3f49faafba14bbccd82c8c", + "fevals": 25, + "g_sha256": "231dac39841834d56c527e33db4e7058814c8720667e7c6ee15ae36e1cfa247f", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.17943296290248112, + -0.9701078696904671, + -0.6268266868350327, + -0.25397189765420736 + ], + "var_upper_bounds": [ + 1.8997467379749975, + 1.3495577757673076, + 1.1547233189353576, + 1.9803220340371652 + ], + "x_sha256": "0776fd1a019e7ec4076ebd849e60871fb4de035d43605ddf698dd54f4e7cb50a" + }, + { + "constraint_directions": "<<", + "constraint_targets": [ + 0.6771508915552242, + 0.12116145688982427 + ], + "f_sha256": "11761959216f04859bd6025b44d475ee56f5e0162b645553068f9b3c9effa10d", + "fevals": 50, + "g_sha256": "65d89d3192c392d4080e35ac9f2926ff2c9c30546378d2f1d60550311157b3f6", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.17943296290248112, + -0.9701078696904671, + -0.6268266868350327, + -0.25397189765420736 + ], + "var_upper_bounds": [ + 1.8997467379749975, + 1.3495577757673076, + 1.1547233189353576, + 1.9803220340371652 + ], + "x_sha256": "3626650e225175e2d4091065ec88524683da20ff67eb19fd84998ea8942931d2" + }, + { + "constraint_directions": "<<", + "constraint_targets": [ + 0.6771508915552242, + 0.12116145688982427 + ], + "f_sha256": "3e733579029c985a702dbfa5e75131b5b5ffc2400c314788526a86c77945197e", + "fevals": 75, + "g_sha256": "b59ff3007c59639b5ac7f78e6052ebf653ff26101de32ceebe5f8d4dfbc160aa", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.17943296290248112, + -0.9701078696904671, + -0.6268266868350327, + -0.25397189765420736 + ], + "var_upper_bounds": [ + 1.8997467379749975, + 1.3495577757673076, + 1.1547233189353576, + 1.9803220340371652 + ], + "x_sha256": "7dcdec8bda5bceedaeeef36cad1b640e9561388816c6042ecf4115750f4206b1" + }, + { + "constraint_directions": "<<", + "constraint_targets": [ + 0.6771508915552242, + 0.12116145688982427 + ], + "f_sha256": "dc3ca3cdce31564a610fc2e4c046fe3d5700fd5ab04a296570031d71db3b843b", + "fevals": 100, + "g_sha256": "0a05f1539664d7add8fd6276dca68b26027554563d0fb48a2ac0b7df03bf5451", + "m": 3, + "n": 4, + "n_constraints": 2, + "names_f": [ + "f1", + "f2", + "f3" + ], + "names_g": [ + "g1", + "g2" + ], + "names_x": [ + "x1", + "x2", + "x3", + "x4" + ], + "obj_directions": "+++", + "pop_size": 25, + "var_lower_bounds": [ + -0.17943296290248112, + -0.9701078696904671, + -0.6268266868350327, + -0.25397189765420736 + ], + "var_upper_bounds": [ + 1.8997467379749975, + 1.3495577757673076, + 1.1547233189353576, + 1.9803220340371652 + ], + "x_sha256": "12ede9a8819bfd99ab2f50e67b13d3a7bcd888ae36768cbad2a580c839efab1f" + } + ] + } + ], + "software": "ParetoBench", + "software_version": "0.0.0" +} \ No newline at end of file From 185a7a7557a30eb79200117b408d5ad45306609a Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Sat, 15 Aug 2026 12:35:20 -0700 Subject: [PATCH 8/8] fixes for ci/cd --- .github/workflows/python_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python_tests.yml b/.github/workflows/python_tests.yml index 6e48461..979ccce 100644 --- a/.github/workflows/python_tests.yml +++ b/.github/workflows/python_tests.yml @@ -27,10 +27,11 @@ jobs: pip install ".[test]" - name: Run the tests (Linux/macOS) if: runner.os != 'Windows' + shell: bash run: | echo -e '## Pytest Results\n\n' >> "$GITHUB_STEP_SUMMARY" echo -e '```' >> "$GITHUB_STEP_SUMMARY" - pytest -v --cov=beamfit/ tests 2>&1 | tee -a "$GITHUB_STEP_SUMMARY" + pytest -v --cov=paretobench tests 2>&1 | tee -a "$GITHUB_STEP_SUMMARY" echo -e '```' >> "$GITHUB_STEP_SUMMARY" - name: Run the tests (Windows) if: runner.os == 'Windows'