From fa96b4c4d074bdf9f3a89d7dd9830341de90c5d4 Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 22 Jul 2025 16:45:27 -0500 Subject: [PATCH 01/58] Implement VOCS for simple generators * Internals maintain Optimas data structures * Update sampling tests --- .github/workflows/unix-noax.yml | 1 + .github/workflows/unix-openmpi.yml | 1 + .github/workflows/unix.yml | 1 + optimas/generators/base.py | 84 +++++++++++++++++++-------- optimas/generators/grid_sampling.py | 20 ++----- optimas/generators/line_sampling.py | 31 ++++------ optimas/generators/random_sampling.py | 25 +++----- tests/test_grid_sampling.py | 18 +++--- tests/test_line_sampling.py | 55 ++++++++++-------- tests/test_random_sampling.py | 36 ++++++------ 10 files changed, 144 insertions(+), 128 deletions(-) diff --git a/.github/workflows/unix-noax.yml b/.github/workflows/unix-noax.yml index dd2b7309..57715288 100644 --- a/.github/workflows/unix-noax.yml +++ b/.github/workflows/unix-noax.yml @@ -31,6 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] + pip install git+https://github.com/campa-consortium/generator_standard.git pip uninstall --yes ax-platform # Run without Ax - shell: bash -l {0} name: Run unit tests without Ax diff --git a/.github/workflows/unix-openmpi.yml b/.github/workflows/unix-openmpi.yml index 782c6023..68370041 100644 --- a/.github/workflows/unix-openmpi.yml +++ b/.github/workflows/unix-openmpi.yml @@ -31,6 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py openmpi=5.* pip install .[test] + pip install git+https://github.com/campa-consortium/generator_standard.git - shell: bash -l {0} name: Run unit tests with openMPI run: | diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 6f9cf08a..44455d3a 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -31,6 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] + pip install git+https://github.com/campa-consortium/generator_standard.git - shell: bash -l {0} name: Run unit tests with MPICH run: | diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 5a979714..e3dfe175 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -20,6 +20,7 @@ TrialParameter, TrialStatus, ) +from generator_standard.vocs import VOCS logger = get_logger(__name__) @@ -29,16 +30,8 @@ class Generator: Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. - constraints : list of Parameter, optional - [Not yet implemented] List of optimization constraints. By default - ``None``. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. + vocs : VOCS + VOCS object specifying variables, objectives, constraints, and observables. use_cuda : bool, optional Whether to allow the generator to run on a CUDA GPU. By default ``False``. @@ -73,10 +66,7 @@ class Generator: def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], - constraints: Optional[List[Parameter]] = None, - analyzed_parameters: Optional[List[Parameter]] = None, + vocs: VOCS, use_cuda: Optional[bool] = False, gpu_id: Optional[int] = 0, dedicated_resources: Optional[bool] = False, @@ -87,16 +77,15 @@ def __init__( allow_fixed_parameters: Optional[bool] = False, allow_updating_parameters: Optional[bool] = False, ) -> None: - if objectives is None: - objectives = [Objective()] - # Store copies to prevent unexpected behavior if parameters are changed - # externally. - self._varying_parameters = deepcopy(varying_parameters) - self._objectives = deepcopy(objectives) - self._constraints = constraints - self._analyzed_parameters = ( - [] if analyzed_parameters is None else analyzed_parameters - ) + # Store VOCS object + self._vocs = deepcopy(vocs) + + # Convert VOCS to optimas internal format for backward compatibility + self._varying_parameters = self._convert_vocs_variables_to_varying_parameters() + self._objectives = self._convert_vocs_objectives_to_objectives() + self._constraints = self._convert_vocs_constraints_to_constraints() + self._analyzed_parameters = self._convert_vocs_observables_to_parameters() + self._save_model = save_model self._model_save_period = model_save_period self._model_history_dir = model_history_dir @@ -115,6 +104,53 @@ def __init__( self._trial_count = 0 self._check_parameters(self._varying_parameters) + def _convert_vocs_variables_to_varying_parameters(self) -> List[VaryingParameter]: + """Convert VOCS variables to optimas VaryingParameter objects.""" + varying_parameters = [] + for var_name, var_spec in self._vocs.variables.items(): + # Only handle ContinuousVariable for now + vp = VaryingParameter( + name=var_name, + lower_bound=var_spec.domain[0], + upper_bound=var_spec.domain[1], + default_value=var_spec.default_value + ) + varying_parameters.append(vp) + return varying_parameters + + def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: + """Convert VOCS objectives to optimas Objective objects.""" + objectives = [] + for obj_name, obj_type in self._vocs.objectives.items(): + minimize = obj_type == "MINIMIZE" + obj = Objective(name=obj_name, minimize=minimize) + objectives.append(obj) + return objectives + + def _convert_vocs_constraints_to_constraints(self) -> Optional[List[Parameter]]: + """Convert VOCS constraints to optimas Parameter objects.""" + if not self._vocs.constraints: + return None + constraints = [] + for const_name, const_spec in self._vocs.constraints.items(): + # For now, create a basic Parameter - constraint handling needs more work + param = Parameter(name=const_name) + constraints.append(param) + return constraints + + def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: + """Convert VOCS observables to optimas Parameter objects.""" + parameters = [] + for obs_name in self._vocs.observables: + param = Parameter(name=obs_name) + parameters.append(param) + return parameters + + @property + def vocs(self) -> VOCS: + """Get the VOCS object.""" + return self._vocs + @property def varying_parameters(self) -> List[VaryingParameter]: """Get the list of varying parameters.""" diff --git a/optimas/generators/grid_sampling.py b/optimas/generators/grid_sampling.py index 36dd7abc..2a0a1a0a 100644 --- a/optimas/generators/grid_sampling.py +++ b/optimas/generators/grid_sampling.py @@ -5,6 +5,7 @@ import numpy as np from optimas.core import Objective, Trial, VaryingParameter, Parameter +from generator_standard.vocs import VOCS from .base import Generator @@ -19,30 +20,19 @@ class GridSamplingGenerator(Generator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. + vocs : VOCS + VOCS object specifying variables, objectives, constraints, and observables. n_steps : list of int Number of grid steps along each direction. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. """ def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, n_steps: List[int], - analyzed_parameters: Optional[List[Parameter]] = None, ) -> None: - super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, - ) + super().__init__(vocs=vocs) self._n_steps = n_steps if n_steps is np.ndarray else np.array(n_steps) self._create_configurations() diff --git a/optimas/generators/line_sampling.py b/optimas/generators/line_sampling.py index 4253aa12..c3dac56b 100644 --- a/optimas/generators/line_sampling.py +++ b/optimas/generators/line_sampling.py @@ -5,6 +5,7 @@ import numpy as np from optimas.core import Objective, Trial, VaryingParameter, Parameter +from generator_standard.vocs import VOCS from .base import Generator @@ -24,51 +25,39 @@ class LineSamplingGenerator(Generator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. + vocs : VOCS + VOCS object specifying variables, objectives, constraints, and observables. n_steps : ndarray or list of int A 1D array or list with the number of steps along each direction. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. """ def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, n_steps: Union[np.ndarray, List[int]], - analyzed_parameters: Optional[List[Parameter]] = None, ) -> None: - super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, - ) - self._check_inputs(varying_parameters, objectives, n_steps) + super().__init__(vocs=vocs) + self._check_inputs(vocs, n_steps) self._n_steps = n_steps if n_steps is np.ndarray else np.array(n_steps) self._create_configurations() def _check_inputs( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, n_steps: int, ) -> None: """Check that the generator inputs are valid.""" # Check as many n_steps as varying_parameters are provided. assert len(n_steps) == len( - varying_parameters + self.varying_parameters ), "Length of `n_steps` ({}) and ".format( len(n_steps) ) + "`varying_parameters` ({}) do not match.".format( - len(varying_parameters) + len(self.varying_parameters) ) # Check that all varying parameters have a default value. - for var in varying_parameters: + for var in self.varying_parameters: assert ( var.default_value is not None ), "Parameter {} does not have a default value.".format(var.name) diff --git a/optimas/generators/random_sampling.py b/optimas/generators/random_sampling.py index 9c88646c..00aeb398 100644 --- a/optimas/generators/random_sampling.py +++ b/optimas/generators/random_sampling.py @@ -5,6 +5,7 @@ import numpy as np from optimas.core import Objective, Trial, VaryingParameter, Parameter +from generator_standard.vocs import VOCS from .base import Generator @@ -16,10 +17,8 @@ class RandomSamplingGenerator(Generator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. + vocs : VOCS + VOCS object specifying variables, objectives, constraints, and observables. distribution : {'uniform', 'normal'}, optional The random distribution to use. The ``'uniform'`` option draws samples from a uniform distribution within the lower :math:`l_b` and upper @@ -29,30 +28,21 @@ class RandomSamplingGenerator(Generator): :math:`\sigma = u_b - c`. By default, ``'uniform'``. seed : int, optional Seed to initialize the random generator. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. """ def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, distribution: Optional[str] = "uniform", seed: Optional[int] = None, - analyzed_parameters: Optional[List[Parameter]] = None, ) -> None: - super().__init__( - varying_parameters, - objectives, - analyzed_parameters=analyzed_parameters, - ) + super().__init__(vocs=vocs) self._generate_sampling = { "uniform": self._generate_uniform_sampling, "normal": self._generate_normal_sampling, } - self._check_inputs(varying_parameters, objectives, distribution) + self._check_inputs(vocs, distribution) self._distribution = distribution self._rng = np.random.default_rng(seed) self._define_generator_parameters() @@ -70,8 +60,7 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: def _check_inputs( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, distribution: str, ) -> None: """Check that the generator inputs are valid.""" diff --git a/tests/test_grid_sampling.py b/tests/test_grid_sampling.py index c6a35850..e4f1ad54 100644 --- a/tests/test_grid_sampling.py +++ b/tests/test_grid_sampling.py @@ -1,4 +1,5 @@ import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import GridSamplingGenerator @@ -18,24 +19,23 @@ def test_grid_sampling(): """Test that grid sampling generates the expected configurations.""" # Create varying parameters. - names = ["x0", "x1"] lower_bounds = [-3.0, 2.0] upper_bounds = [1.0, 5.0] - vars = [] n_steps = [7, 15] - for name, lb, ub in zip(names, lower_bounds, upper_bounds): - vars.append(VaryingParameter(name, lb, ub)) # Set number of evaluations. n_evals = np.prod(n_steps) - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [lower_bounds[0], upper_bounds[0]], + "x1": [lower_bounds[1], upper_bounds[1]] + }, + objectives={"f": "MAXIMIZE"} + ) # Create generator and run exploration. - gen = GridSamplingGenerator( - varying_parameters=vars, objectives=[obj], n_steps=n_steps - ) + gen = GridSamplingGenerator(vocs=vocs, n_steps=n_steps) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( generator=gen, diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index 1b09bf89..645f57d5 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import LineSamplingGenerator @@ -20,25 +21,28 @@ def eval_func(input_params, output_params): def test_line_sampling(): """Test that line sampling generates the expected configurations.""" - # Create varying parameters. - names = ["x0", "x1"] + # Define test parameters lower_bounds = [-3.0, 2.0] upper_bounds = [1.0, 5.0] defaults = [0, 0] n_steps = [7, 15] - vars = [] - for name, lb, ub, dv in zip(names, lower_bounds, upper_bounds, defaults): - vars.append(VaryingParameter(name, lb, ub, default_value=dv)) # Set number of evaluations. n_evals = np.sum(n_steps) - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [lower_bounds[0], upper_bounds[0]], + "x1": [lower_bounds[1], upper_bounds[1]] + }, + objectives={"f": "MAXIMIZE"} + ) + vocs.variables["x0"].default_value = defaults[0] + vocs.variables["x1"].default_value = defaults[1] # Create generator and run exploration. gen = LineSamplingGenerator( - varying_parameters=vars, objectives=[obj], n_steps=n_steps + vocs=vocs, n_steps=n_steps ) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( @@ -74,27 +78,32 @@ def test_line_sampling(): def test_line_sampling_errors(): """Test that the line sampling raises the correct exceptions.""" - # Create varying parameters with missing default value. - var1 = VaryingParameter("x0", -3, 1) - var2 = VaryingParameter("x0", -3, 1) - - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-3, 1], + "x1": [-3, 1] + }, + objectives={"f": "MAXIMIZE"} + ) # Check that an exception is raised when default values are missing. with pytest.raises( AssertionError, match="Parameter x0 does not have a default value." ): gen = LineSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj], n_steps=[3, 5] + vocs=vocs, n_steps=[3, 5] ) - # Create varying parameters. - var1 = VaryingParameter("x0", -3, 1, default_value=0.0) - var2 = VaryingParameter("x0", -3, 1, default_value=0.0) - - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-3, 1], + "x1": [-3, 1] + }, + objectives={"f": "MAXIMIZE"} + ) + # Set default values + vocs.variables["x0"].default_value = 0.0 + vocs.variables["x1"].default_value = 0.0 # Check that an exception is raised when n_steps is not correct. with pytest.raises( @@ -104,9 +113,7 @@ def test_line_sampling_errors(): " `varying_parameters` (2) do not match." ), ): - gen = LineSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj], n_steps=[3] - ) + LineSamplingGenerator(vocs=vocs, n_steps=[3]) if __name__ == "__main__": diff --git a/tests/test_random_sampling.py b/tests/test_random_sampling.py index 69c23be8..5668a540 100644 --- a/tests/test_random_sampling.py +++ b/tests/test_random_sampling.py @@ -1,4 +1,5 @@ import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -22,23 +23,23 @@ def test_uniform_sampling(): seed = 1 # Create varying parameters. - names = ["x0", "x1"] lower_bounds = [-3.0, 2.0] upper_bounds = [1.0, 5.0] - vars = [] - for name, lb, ub in zip(names, lower_bounds, upper_bounds): - vars.append(VaryingParameter(name, lb, ub)) # Set number of evaluations. n_evals = 10 - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [lower_bounds[0], upper_bounds[0]], + "x1": [lower_bounds[1], upper_bounds[1]] + }, + objectives={"f": "MAXIMIZE"} + ) # Create generator and run exploration. gen = RandomSamplingGenerator( - varying_parameters=vars, - objectives=[obj], + vocs=vocs, distribution="uniform", seed=1, ) @@ -60,7 +61,7 @@ def test_uniform_sampling(): # Generate expected points. rng = np.random.default_rng(seed=seed) - configs = rng.uniform(lower_bounds, upper_bounds, (n_evals, len(vars))) + configs = rng.uniform(lower_bounds, upper_bounds, (n_evals, len(lower_bounds))) x0_test = configs[:, 0] x1_test = configs[:, 1] @@ -77,22 +78,23 @@ def test_normal_sampling(): seed = 1 # Create varying parameters. - names = ["x0", "x1"] center = [0.0, 0.0] sigma = [1.0, 5.0] - vars = [] - for name, c, s in zip(names, center, sigma): - vars.append(VaryingParameter(name, c - s, c + s)) # Set number of evaluations. n_evals = 10 - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [center[0] - sigma[0], center[0] + sigma[0]], + "x1": [center[1] - sigma[1], center[1] + sigma[1]] + }, + objectives={"f": "MAXIMIZE"} + ) # Create generator and run exploration. gen = RandomSamplingGenerator( - varying_parameters=vars, objectives=[obj], distribution="normal", seed=1 + vocs=vocs, distribution="normal", seed=1 ) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( @@ -112,7 +114,7 @@ def test_normal_sampling(): # Generate expected points. rng = np.random.default_rng(seed=seed) - configs = rng.normal(center, sigma, (n_evals, len(vars))) + configs = rng.normal(center, sigma, (n_evals, len(center))) x0_test = configs[:, 0] x1_test = configs[:, 1] From 31ba1169821b7e04eb238c4843eede06eecdfe1c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 21:48:57 +0000 Subject: [PATCH 02/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/base.py | 22 +++++++++++++++------- tests/test_grid_sampling.py | 4 ++-- tests/test_line_sampling.py | 24 ++++++------------------ tests/test_random_sampling.py | 16 ++++++++-------- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index e3dfe175..5a6ca9ae 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -79,13 +79,17 @@ def __init__( ) -> None: # Store VOCS object self._vocs = deepcopy(vocs) - + # Convert VOCS to optimas internal format for backward compatibility - self._varying_parameters = self._convert_vocs_variables_to_varying_parameters() + self._varying_parameters = ( + self._convert_vocs_variables_to_varying_parameters() + ) self._objectives = self._convert_vocs_objectives_to_objectives() self._constraints = self._convert_vocs_constraints_to_constraints() - self._analyzed_parameters = self._convert_vocs_observables_to_parameters() - + self._analyzed_parameters = ( + self._convert_vocs_observables_to_parameters() + ) + self._save_model = save_model self._model_save_period = model_save_period self._model_history_dir = model_history_dir @@ -104,7 +108,9 @@ def __init__( self._trial_count = 0 self._check_parameters(self._varying_parameters) - def _convert_vocs_variables_to_varying_parameters(self) -> List[VaryingParameter]: + def _convert_vocs_variables_to_varying_parameters( + self, + ) -> List[VaryingParameter]: """Convert VOCS variables to optimas VaryingParameter objects.""" varying_parameters = [] for var_name, var_spec in self._vocs.variables.items(): @@ -113,7 +119,7 @@ def _convert_vocs_variables_to_varying_parameters(self) -> List[VaryingParameter name=var_name, lower_bound=var_spec.domain[0], upper_bound=var_spec.domain[1], - default_value=var_spec.default_value + default_value=var_spec.default_value, ) varying_parameters.append(vp) return varying_parameters @@ -127,7 +133,9 @@ def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: objectives.append(obj) return objectives - def _convert_vocs_constraints_to_constraints(self) -> Optional[List[Parameter]]: + def _convert_vocs_constraints_to_constraints( + self, + ) -> Optional[List[Parameter]]: """Convert VOCS constraints to optimas Parameter objects.""" if not self._vocs.constraints: return None diff --git a/tests/test_grid_sampling.py b/tests/test_grid_sampling.py index e4f1ad54..d804fe3e 100644 --- a/tests/test_grid_sampling.py +++ b/tests/test_grid_sampling.py @@ -29,9 +29,9 @@ def test_grid_sampling(): vocs = VOCS( variables={ "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]] + "x1": [lower_bounds[1], upper_bounds[1]], }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) # Create generator and run exploration. diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index 645f57d5..0a3e049d 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -33,17 +33,15 @@ def test_line_sampling(): vocs = VOCS( variables={ "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]] + "x1": [lower_bounds[1], upper_bounds[1]], }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) vocs.variables["x0"].default_value = defaults[0] vocs.variables["x1"].default_value = defaults[1] # Create generator and run exploration. - gen = LineSamplingGenerator( - vocs=vocs, n_steps=n_steps - ) + gen = LineSamplingGenerator(vocs=vocs, n_steps=n_steps) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( generator=gen, @@ -79,27 +77,17 @@ def test_line_sampling_errors(): """Test that the line sampling raises the correct exceptions.""" vocs = VOCS( - variables={ - "x0": [-3, 1], - "x1": [-3, 1] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-3, 1], "x1": [-3, 1]}, objectives={"f": "MAXIMIZE"} ) # Check that an exception is raised when default values are missing. with pytest.raises( AssertionError, match="Parameter x0 does not have a default value." ): - gen = LineSamplingGenerator( - vocs=vocs, n_steps=[3, 5] - ) + gen = LineSamplingGenerator(vocs=vocs, n_steps=[3, 5]) vocs = VOCS( - variables={ - "x0": [-3, 1], - "x1": [-3, 1] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-3, 1], "x1": [-3, 1]}, objectives={"f": "MAXIMIZE"} ) # Set default values vocs.variables["x0"].default_value = 0.0 diff --git a/tests/test_random_sampling.py b/tests/test_random_sampling.py index 5668a540..b8b0d58f 100644 --- a/tests/test_random_sampling.py +++ b/tests/test_random_sampling.py @@ -32,9 +32,9 @@ def test_uniform_sampling(): vocs = VOCS( variables={ "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]] + "x1": [lower_bounds[1], upper_bounds[1]], }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) # Create generator and run exploration. @@ -61,7 +61,9 @@ def test_uniform_sampling(): # Generate expected points. rng = np.random.default_rng(seed=seed) - configs = rng.uniform(lower_bounds, upper_bounds, (n_evals, len(lower_bounds))) + configs = rng.uniform( + lower_bounds, upper_bounds, (n_evals, len(lower_bounds)) + ) x0_test = configs[:, 0] x1_test = configs[:, 1] @@ -87,15 +89,13 @@ def test_normal_sampling(): vocs = VOCS( variables={ "x0": [center[0] - sigma[0], center[0] + sigma[0]], - "x1": [center[1] - sigma[1], center[1] + sigma[1]] + "x1": [center[1] - sigma[1], center[1] + sigma[1]], }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) # Create generator and run exploration. - gen = RandomSamplingGenerator( - vocs=vocs, distribution="normal", seed=1 - ) + gen = RandomSamplingGenerator(vocs=vocs, distribution="normal", seed=1) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( generator=gen, From 7cd13c930e8a3a745440357854fe1e01b73b1df9 Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 22 Jul 2025 19:09:19 -0500 Subject: [PATCH 03/58] Convert further tests to vocs --- tests/test_analyzed_parameters.py | 45 +++++++++++++------------------ tests/test_chain_evaluator.py | 33 ++++++++++++----------- tests/test_comms.py | 15 ++++++----- tests/test_exploration_resume.py | 43 ++++++++++++++++------------- tests/test_grid_sampling_mpi.py | 18 ++++++------- tests/test_manual_exploration.py | 20 +++++++------- 6 files changed, 90 insertions(+), 84 deletions(-) diff --git a/tests/test_analyzed_parameters.py b/tests/test_analyzed_parameters.py index ea5945a1..cf31489f 100644 --- a/tests/test_analyzed_parameters.py +++ b/tests/test_analyzed_parameters.py @@ -1,4 +1,5 @@ import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -21,21 +22,17 @@ def test_analyzed_parameters(): Test that an exploration runs successfully when including not only an objective, but also a set of additional analyzed parameters. """ - # Define varying parameters. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - - # Define objective and other parameters to analyze. - obj = Objective("f", minimize=False) - par1 = Parameter("analyzed_parameter_1") - par2 = Parameter("analyzed_parameter_2") + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables=["analyzed_parameter_1", "analyzed_parameter_2"] + ) # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[par1, par2], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create function evaluator. ev = FunctionEvaluator(function=eval_func) @@ -77,21 +74,17 @@ def test_analyzed_parameters_from_history(): values of the analyzed parameters in the history file are correctly loaded back into the exploration. """ - # Define varying parameters. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - - # Define objective and other parameters to analyze. - obj = Objective("f", minimize=False) - par1 = Parameter("analyzed_parameter_1") - par2 = Parameter("analyzed_parameter_2") + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables=["analyzed_parameter_1", "analyzed_parameter_2"] + ) # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[par1, par2], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create function evaluator. ev = FunctionEvaluator(function=eval_func) diff --git a/tests/test_chain_evaluator.py b/tests/test_chain_evaluator.py index 0e2ce139..25765b42 100644 --- a/tests/test_chain_evaluator.py +++ b/tests/test_chain_evaluator.py @@ -1,6 +1,7 @@ import os import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -26,17 +27,17 @@ def analysis_func_2(sim_dir, output_params): def test_chain_evaluator(): # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - par1 = Parameter("result_1") - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables=["result_1"] + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[par1], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev1 = TemplateEvaluator( @@ -78,14 +79,16 @@ def test_chain_evaluator_only_final_analysis(): """Test a ChainEvaluator where only the final TemplateEvaluator has an analysis function.""" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj] - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev1 = TemplateEvaluator( diff --git a/tests/test_comms.py b/tests/test_comms.py index bf60f5db..d3e1bc63 100644 --- a/tests/test_comms.py +++ b/tests/test_comms.py @@ -1,4 +1,5 @@ import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -17,17 +18,19 @@ def eval_func(input_params, output_params): def test_libe_comms(): """Test local and local_threading communications.""" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) max_evals = 10 for comm in ["local", "local_threading"]: # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj] - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create function evaluator. ev = FunctionEvaluator(function=eval_func) diff --git a/tests/test_exploration_resume.py b/tests/test_exploration_resume.py index f84c397c..ab8e09cd 100644 --- a/tests/test_exploration_resume.py +++ b/tests/test_exploration_resume.py @@ -1,4 +1,5 @@ import os +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -17,14 +18,16 @@ def analysis_func(sim_dir, output_params): def test_exploration_in_steps(): """Test that an exploration runs correctly when doing so in several steps.""" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj] - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev = TemplateEvaluator( @@ -66,14 +69,16 @@ def test_exploration_in_steps_without_limit(): without a limit on the maximum number of evaluations. """ # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Define evaluator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj] - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev = TemplateEvaluator( @@ -108,14 +113,16 @@ def test_exploration_in_steps_without_limit(): def test_exploration_resume(): """Test that an exploration correctly resumes from a previous run.""" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj] - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev = TemplateEvaluator( diff --git a/tests/test_grid_sampling_mpi.py b/tests/test_grid_sampling_mpi.py index 5d8e1569..831780c0 100644 --- a/tests/test_grid_sampling_mpi.py +++ b/tests/test_grid_sampling_mpi.py @@ -1,5 +1,6 @@ import numpy as np import pytest +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import GridSamplingGenerator @@ -20,24 +21,23 @@ def test_grid_sampling(): """Test that grid sampling generates the expected configurations.""" # Create varying parameters. - names = ["x0", "x1"] lower_bounds = [-3.0, 2.0] upper_bounds = [1.0, 5.0] - vars = [] n_steps = [7, 15] - for name, lb, ub in zip(names, lower_bounds, upper_bounds): - vars.append(VaryingParameter(name, lb, ub)) # Set number of evaluations. n_evals = np.prod(n_steps) - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [lower_bounds[0], upper_bounds[0]], + "x1": [lower_bounds[1], upper_bounds[1]] + }, + objectives={"f": "MAXIMIZE"} + ) # Create generator and run exploration. - gen = GridSamplingGenerator( - varying_parameters=vars, objectives=[obj], n_steps=n_steps - ) + gen = GridSamplingGenerator(vocs=vocs, n_steps=n_steps) ev = FunctionEvaluator(function=eval_func) exploration = Exploration( generator=gen, diff --git a/tests/test_manual_exploration.py b/tests/test_manual_exploration.py index 1eb6de37..5a6f22cc 100644 --- a/tests/test_manual_exploration.py +++ b/tests/test_manual_exploration.py @@ -1,5 +1,6 @@ import numpy as np import pandas as pd +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -20,20 +21,21 @@ def test_manual_exploration(): """Tests methods for manually attaching trials and evaluations.""" # Create varying parameters. - names = ["x0", "x1"] lower_bounds = [-3.0, 2.0] upper_bounds = [1.0, 5.0] - vars = [] - for name, lb, ub in zip(names, lower_bounds, upper_bounds): - vars.append(VaryingParameter(name, lb, ub)) - par1 = Parameter("par1") # Set number of evaluations. n_evals = 10 n_evals_substep = 3 - # Define objective. - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [lower_bounds[0], upper_bounds[0]], + "x1": [lower_bounds[1], upper_bounds[1]] + }, + objectives={"f": "MAXIMIZE"}, + observables=["par1"] + ) # Trials to attach and evaluate manually. trials_to_attach = {"x0": [-2.3, 1.0, 0.5], "x1": [2.4, 1.0, 3.0]} @@ -55,9 +57,7 @@ def test_manual_exploration(): for i, (trials, evals) in enumerate(zip(test_trials, test_evals)): # Create generator and run exploration. gen = RandomSamplingGenerator( - varying_parameters=vars, - objectives=[obj], - analyzed_parameters=[par1], + vocs=vocs, distribution="uniform", seed=1, ) From b03f0f1cd905df87ac7fcb49a63e5c2cd78a3cce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 00:10:18 +0000 Subject: [PATCH 04/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_analyzed_parameters.py | 14 ++++---------- tests/test_chain_evaluator.py | 14 ++++---------- tests/test_comms.py | 7 ++----- tests/test_exploration_resume.py | 21 ++++++--------------- tests/test_grid_sampling_mpi.py | 4 ++-- tests/test_manual_exploration.py | 4 ++-- 6 files changed, 20 insertions(+), 44 deletions(-) diff --git a/tests/test_analyzed_parameters.py b/tests/test_analyzed_parameters.py index cf31489f..c39fa2da 100644 --- a/tests/test_analyzed_parameters.py +++ b/tests/test_analyzed_parameters.py @@ -23,12 +23,9 @@ def test_analyzed_parameters(): objective, but also a set of additional analyzed parameters. """ vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, - observables=["analyzed_parameter_1", "analyzed_parameter_2"] + observables=["analyzed_parameter_1", "analyzed_parameter_2"], ) # Create generator. @@ -75,12 +72,9 @@ def test_analyzed_parameters_from_history(): loaded back into the exploration. """ vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, - observables=["analyzed_parameter_1", "analyzed_parameter_2"] + observables=["analyzed_parameter_1", "analyzed_parameter_2"], ) # Create generator. diff --git a/tests/test_chain_evaluator.py b/tests/test_chain_evaluator.py index 25765b42..9a3d067e 100644 --- a/tests/test_chain_evaluator.py +++ b/tests/test_chain_evaluator.py @@ -28,12 +28,9 @@ def analysis_func_2(sim_dir, output_params): def test_chain_evaluator(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, - observables=["result_1"] + observables=["result_1"], ) # Define variables and objectives. @@ -80,11 +77,8 @@ def test_chain_evaluator_only_final_analysis(): analysis function.""" # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Define variables and objectives. diff --git a/tests/test_comms.py b/tests/test_comms.py index d3e1bc63..dcae8ac8 100644 --- a/tests/test_comms.py +++ b/tests/test_comms.py @@ -19,11 +19,8 @@ def test_libe_comms(): """Test local and local_threading communications.""" # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) max_evals = 10 diff --git a/tests/test_exploration_resume.py b/tests/test_exploration_resume.py index ab8e09cd..7d42eec5 100644 --- a/tests/test_exploration_resume.py +++ b/tests/test_exploration_resume.py @@ -19,11 +19,8 @@ def test_exploration_in_steps(): """Test that an exploration runs correctly when doing so in several steps.""" # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Define variables and objectives. @@ -70,11 +67,8 @@ def test_exploration_in_steps_without_limit(): """ # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Define evaluator. @@ -114,11 +108,8 @@ def test_exploration_resume(): """Test that an exploration correctly resumes from a previous run.""" # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Define variables and objectives. diff --git a/tests/test_grid_sampling_mpi.py b/tests/test_grid_sampling_mpi.py index 831780c0..12e31200 100644 --- a/tests/test_grid_sampling_mpi.py +++ b/tests/test_grid_sampling_mpi.py @@ -31,9 +31,9 @@ def test_grid_sampling(): vocs = VOCS( variables={ "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]] + "x1": [lower_bounds[1], upper_bounds[1]], }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) # Create generator and run exploration. diff --git a/tests/test_manual_exploration.py b/tests/test_manual_exploration.py index 5a6f22cc..997e5121 100644 --- a/tests/test_manual_exploration.py +++ b/tests/test_manual_exploration.py @@ -31,10 +31,10 @@ def test_manual_exploration(): vocs = VOCS( variables={ "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]] + "x1": [lower_bounds[1], upper_bounds[1]], }, objectives={"f": "MAXIMIZE"}, - observables=["par1"] + observables=["par1"], ) # Trials to attach and evaluate manually. From f973cfa9a87cd9a8140f3c2782507b1107f29201 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 23 Jul 2025 15:09:58 -0500 Subject: [PATCH 05/58] Make exploration_diagnostics work with vocs --- .../diagnostics/exploration_diagnostics.py | 24 ++++++++++++++---- tests/test_exploration_diagnostics.py | 25 +++++++++++-------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/optimas/diagnostics/exploration_diagnostics.py b/optimas/diagnostics/exploration_diagnostics.py index f7746f76..5a9eaa86 100644 --- a/optimas/diagnostics/exploration_diagnostics.py +++ b/optimas/diagnostics/exploration_diagnostics.py @@ -19,6 +19,7 @@ from optimas.explorations import Exploration from optimas.utils.other import get_df_with_selection from optimas.utils.ax import AxModelManager +from generator_standard.vocs import VOCS class ExplorationDiagnostics: @@ -102,12 +103,25 @@ def _create_exploration( analyzed_parameters.append(p) # Create exploration using dummy generator and evaluator. + # Create exploration using dummy generator and evaluator. + variables = {} + for vp in varying_parameters: + variables[vp.name] = [vp.lower_bound, vp.upper_bound] + + vocs_objectives = {} + for obj in objectives: + vocs_objectives[obj.name] = "MINIMIZE" if obj.minimize else "MAXIMIZE" + + observables = [param.name for param in analyzed_parameters] + + vocs = VOCS( + variables=variables, + objectives=vocs_objectives, + observables=observables + ) + return Exploration( - generator=Generator( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, - ), + generator=Generator(vocs=vocs), evaluator=Evaluator(sim_function=None), history=history_path, exploration_dir_path=exploration_dir_path, diff --git a/tests/test_exploration_diagnostics.py b/tests/test_exploration_diagnostics.py index c8b2edd7..7f33bebd 100644 --- a/tests/test_exploration_diagnostics.py +++ b/tests/test_exploration_diagnostics.py @@ -4,6 +4,7 @@ from matplotlib.gridspec import GridSpec import matplotlib.pyplot as plt import pytest +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -29,15 +30,19 @@ def test_exploration_diagnostics(): exploration_dir_path = "./tests_output/test_exploration_diagnostics" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f1", minimize=False) - obj2 = Objective("f2", minimize=True) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={ + "f1": "MAXIMIZE", + "f2": "MINIMIZE" + } + ) # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], objectives=[obj, obj2], seed=0 - ) + gen = RandomSamplingGenerator(vocs=vocs, seed=0) # Create template evaluator. ev = TemplateEvaluator( @@ -137,12 +142,12 @@ def test_exploration_diagnostics(): diags.print_best_evaluations(top=3, objective="f1") diags.print_evaluation(best_ev_f1["trial_index"].item()) - # Check that all 3 possible objective inputs give the same result. + # Check that all possible objective inputs give the same result. _, trace1 = diags.get_objective_trace() _, trace2 = diags.get_objective_trace("f1") - _, trace3 = diags.get_objective_trace(obj) + # _, trace3 = diags.get_objective_trace(obj) # Can be removed np.testing.assert_array_equal(trace1, trace2) - np.testing.assert_array_equal(trace1, trace3) + # np.testing.assert_array_equal(trace1, trace3) # Test making plot using the diagnostics API. fig, ax = plt.subplots() From e44319cf31d0a7ca063293ad6d20ea387f4db58a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 20:10:54 +0000 Subject: [PATCH 06/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/diagnostics/exploration_diagnostics.py | 14 ++++++++------ tests/test_exploration_diagnostics.py | 10 ++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/optimas/diagnostics/exploration_diagnostics.py b/optimas/diagnostics/exploration_diagnostics.py index 5a9eaa86..bf631214 100644 --- a/optimas/diagnostics/exploration_diagnostics.py +++ b/optimas/diagnostics/exploration_diagnostics.py @@ -107,19 +107,21 @@ def _create_exploration( variables = {} for vp in varying_parameters: variables[vp.name] = [vp.lower_bound, vp.upper_bound] - + vocs_objectives = {} for obj in objectives: - vocs_objectives[obj.name] = "MINIMIZE" if obj.minimize else "MAXIMIZE" - + vocs_objectives[obj.name] = ( + "MINIMIZE" if obj.minimize else "MAXIMIZE" + ) + observables = [param.name for param in analyzed_parameters] - + vocs = VOCS( variables=variables, objectives=vocs_objectives, - observables=observables + observables=observables, ) - + return Exploration( generator=Generator(vocs=vocs), evaluator=Evaluator(sim_function=None), diff --git a/tests/test_exploration_diagnostics.py b/tests/test_exploration_diagnostics.py index 7f33bebd..c3f11878 100644 --- a/tests/test_exploration_diagnostics.py +++ b/tests/test_exploration_diagnostics.py @@ -31,14 +31,8 @@ def test_exploration_diagnostics(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={ - "f1": "MAXIMIZE", - "f2": "MINIMIZE" - } + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f1": "MAXIMIZE", "f2": "MINIMIZE"}, ) # Create generator. From 0f8049d66a5a9a8924ee59ccf2bf814343690111 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 23 Jul 2025 16:55:21 -0500 Subject: [PATCH 07/58] Base generator inherits from standard --- optimas/generators/base.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 5a6ca9ae..ebfe0124 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -21,11 +21,12 @@ TrialStatus, ) from generator_standard.vocs import VOCS +from generator_standard.generator import Generator as StandardGenerator logger = get_logger(__name__) -class Generator: +class Generator(StandardGenerator): """Base class for all generators. Parameters @@ -77,7 +78,11 @@ def __init__( allow_fixed_parameters: Optional[bool] = False, allow_updating_parameters: Optional[bool] = False, ) -> None: - # Store VOCS object + # Initialize the standard generator which called validate_vocs + super().__init__(vocs) + + # Store copies to prevent unexpected behavior if parameters are changed + # externally. self._vocs = deepcopy(vocs) # Convert VOCS to optimas internal format for backward compatibility @@ -108,6 +113,13 @@ def __init__( self._trial_count = 0 self._check_parameters(self._varying_parameters) + def _validate_vocs(self, vocs: VOCS) -> None: + """This generator should have atleast one variable and one objective""" + if not vocs.variables: + raise ValueError("VOCS must define at least one variable.") + if not vocs.objectives: + raise ValueError("VOCS must define at least one objective.") + def _convert_vocs_variables_to_varying_parameters( self, ) -> List[VaryingParameter]: From a73735c55f5c3b3dfcfc9de7df5707a1b83827b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:57:11 +0000 Subject: [PATCH 08/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index ebfe0124..d07c6299 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -80,7 +80,7 @@ def __init__( ) -> None: # Initialize the standard generator which called validate_vocs super().__init__(vocs) - + # Store copies to prevent unexpected behavior if parameters are changed # externally. self._vocs = deepcopy(vocs) From 942a6929363e28b3f6bebc9b5f4cac0f6a03841d Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 23 Jul 2025 17:20:40 -0500 Subject: [PATCH 09/58] For line_sampling set default value on setup --- tests/test_line_sampling.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index 0a3e049d..c6f16f0c 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -2,7 +2,7 @@ import numpy as np import pytest -from generator_standard.vocs import VOCS +from generator_standard.vocs import VOCS, ContinuousVariable from optimas.explorations import Exploration from optimas.generators import LineSamplingGenerator @@ -32,13 +32,11 @@ def test_line_sampling(): vocs = VOCS( variables={ - "x0": [lower_bounds[0], upper_bounds[0]], - "x1": [lower_bounds[1], upper_bounds[1]], + "x0": ContinuousVariable(domain=[lower_bounds[0], upper_bounds[0]], default_value=defaults[0]), + "x1": ContinuousVariable(domain=[lower_bounds[1], upper_bounds[1]], default_value=defaults[1]), }, objectives={"f": "MAXIMIZE"}, ) - vocs.variables["x0"].default_value = defaults[0] - vocs.variables["x1"].default_value = defaults[1] # Create generator and run exploration. gen = LineSamplingGenerator(vocs=vocs, n_steps=n_steps) @@ -87,11 +85,12 @@ def test_line_sampling_errors(): gen = LineSamplingGenerator(vocs=vocs, n_steps=[3, 5]) vocs = VOCS( - variables={"x0": [-3, 1], "x1": [-3, 1]}, objectives={"f": "MAXIMIZE"} + variables={ + "x0": ContinuousVariable(domain=[-3, 1], default_value=0.0), + "x1": ContinuousVariable(domain=[-3, 1], default_value=0.0), + }, + objectives={"f": "MAXIMIZE"} ) - # Set default values - vocs.variables["x0"].default_value = 0.0 - vocs.variables["x1"].default_value = 0.0 # Check that an exception is raised when n_steps is not correct. with pytest.raises( From 6a51d00576a0eab1671665628489fc10185f2f3f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:21:36 +0000 Subject: [PATCH 10/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_line_sampling.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index c6f16f0c..7a537013 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -32,8 +32,14 @@ def test_line_sampling(): vocs = VOCS( variables={ - "x0": ContinuousVariable(domain=[lower_bounds[0], upper_bounds[0]], default_value=defaults[0]), - "x1": ContinuousVariable(domain=[lower_bounds[1], upper_bounds[1]], default_value=defaults[1]), + "x0": ContinuousVariable( + domain=[lower_bounds[0], upper_bounds[0]], + default_value=defaults[0], + ), + "x1": ContinuousVariable( + domain=[lower_bounds[1], upper_bounds[1]], + default_value=defaults[1], + ), }, objectives={"f": "MAXIMIZE"}, ) @@ -89,7 +95,7 @@ def test_line_sampling_errors(): "x0": ContinuousVariable(domain=[-3, 1], default_value=0.0), "x1": ContinuousVariable(domain=[-3, 1], default_value=0.0), }, - objectives={"f": "MAXIMIZE"} + objectives={"f": "MAXIMIZE"}, ) # Check that an exception is raised when n_steps is not correct. From 3293fde9fb634b6530e8027b6ecfa24c18b1c9bb Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 23 Jul 2025 17:37:39 -0500 Subject: [PATCH 11/58] Add validate_vocs to line sampler --- tests/test_line_sampling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index 7a537013..b2ee3978 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -86,7 +86,7 @@ def test_line_sampling_errors(): # Check that an exception is raised when default values are missing. with pytest.raises( - AssertionError, match="Parameter x0 does not have a default value." + ValueError, match="Variable 'x0' does not have a default value." ): gen = LineSamplingGenerator(vocs=vocs, n_steps=[3, 5]) From 684bf31e793e3ae2bff20167a4196cfa073197d3 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 23 Jul 2025 17:39:35 -0500 Subject: [PATCH 12/58] Actually add validate_vocs to line sampler --- optimas/generators/line_sampling.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/optimas/generators/line_sampling.py b/optimas/generators/line_sampling.py index c3dac56b..c1afc8fa 100644 --- a/optimas/generators/line_sampling.py +++ b/optimas/generators/line_sampling.py @@ -42,6 +42,14 @@ def __init__( self._n_steps = n_steps if n_steps is np.ndarray else np.array(n_steps) self._create_configurations() + def _validate_vocs(self, vocs: VOCS) -> None: + super()._validate_vocs(vocs) + for var_name, var_spec in vocs.variables.items(): + if var_spec.default_value is None: + raise ValueError( + f"Variable '{var_name}' does not have a default value. " + ) + def _check_inputs( self, vocs: VOCS, @@ -56,11 +64,6 @@ def _check_inputs( ) + "`varying_parameters` ({}) do not match.".format( len(self.varying_parameters) ) - # Check that all varying parameters have a default value. - for var in self.varying_parameters: - assert ( - var.default_value is not None - ), "Parameter {} does not have a default value.".format(var.name) def _create_configurations(self) -> None: """Create a list will all configurations to be evaluated.""" From 0b690ffa81e9c3b7d11456ff104ca286c17db7eb Mon Sep 17 00:00:00 2001 From: shudson Date: Thu, 24 Jul 2025 12:33:38 -0500 Subject: [PATCH 13/58] Use observable types and update env test --- .github/workflows/unix-noax.yml | 2 +- .github/workflows/unix-openmpi.yml | 2 +- .github/workflows/unix.yml | 2 +- optimas/generators/base.py | 18 +++++++++++++++--- tests/test_env_script.py | 19 ++++++++++--------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/.github/workflows/unix-noax.yml b/.github/workflows/unix-noax.yml index 57715288..04a643fe 100644 --- a/.github/workflows/unix-noax.yml +++ b/.github/workflows/unix-noax.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type pip uninstall --yes ax-platform # Run without Ax - shell: bash -l {0} name: Run unit tests without Ax diff --git a/.github/workflows/unix-openmpi.yml b/.github/workflows/unix-openmpi.yml index 68370041..86af3eb1 100644 --- a/.github/workflows/unix-openmpi.yml +++ b/.github/workflows/unix-openmpi.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py openmpi=5.* pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type - shell: bash -l {0} name: Run unit tests with openMPI run: | diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 44455d3a..34749727 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type - shell: bash -l {0} name: Run unit tests with MPICH run: | diff --git a/optimas/generators/base.py b/optimas/generators/base.py index d07c6299..232f9aa9 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -161,9 +161,21 @@ def _convert_vocs_constraints_to_constraints( def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: """Convert VOCS observables to optimas Parameter objects.""" parameters = [] - for obs_name in self._vocs.observables: - param = Parameter(name=obs_name) - parameters.append(param) + # Handle both old format (set of strings) and new format (dict with ObservableType) + if isinstance(self._vocs.observables, dict): + for obs_name, obs_spec in self._vocs.observables.items(): + if hasattr(obs_spec, 'dtype'): + # New format with ObservableType + param = Parameter(name=obs_name, dtype=obs_spec.dtype) + else: + # Old format or simple string + param = Parameter(name=obs_name) + parameters.append(param) + else: + # Old format: set of strings + for obs_name in self._vocs.observables: + param = Parameter(name=obs_name) + parameters.append(param) return parameters @property diff --git a/tests/test_env_script.py b/tests/test_env_script.py index 4c87eb43..7f69c496 100644 --- a/tests/test_env_script.py +++ b/tests/test_env_script.py @@ -1,6 +1,7 @@ import os import numpy as np +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -21,17 +22,17 @@ def analysis_func(sim_dir, output_params): def test_env_script(): # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - test_var = Parameter("test_var", dtype="U10") + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables={"test_var": "U10"} + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[test_var], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev = TemplateEvaluator( From 89323d0450ddfb1297f14c5a6008e7e322242290 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 17:34:12 +0000 Subject: [PATCH 14/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/base.py | 2 +- tests/test_env_script.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 232f9aa9..e8c5f64b 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -164,7 +164,7 @@ def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: # Handle both old format (set of strings) and new format (dict with ObservableType) if isinstance(self._vocs.observables, dict): for obs_name, obs_spec in self._vocs.observables.items(): - if hasattr(obs_spec, 'dtype'): + if hasattr(obs_spec, "dtype"): # New format with ObservableType param = Parameter(name=obs_name, dtype=obs_spec.dtype) else: diff --git a/tests/test_env_script.py b/tests/test_env_script.py index 7f69c496..74471e1d 100644 --- a/tests/test_env_script.py +++ b/tests/test_env_script.py @@ -23,12 +23,9 @@ def analysis_func(sim_dir, output_params): def test_env_script(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, - observables={"test_var": "U10"} + observables={"test_var": "U10"}, ) # Define variables and objectives. From f3130897fa95fa8062e8f2052c62b2598c5e374a Mon Sep 17 00:00:00 2001 From: shudson Date: Thu, 24 Jul 2025 17:18:18 -0500 Subject: [PATCH 15/58] Allow observable to be any type --- optimas/generators/base.py | 23 ++++++----------- tests/test_function_evaluator.py | 42 +++++++++++++++++--------------- tests/test_template_evaluator.py | 41 ++++++++++++++++--------------- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index e8c5f64b..d3b59c07 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -161,21 +161,14 @@ def _convert_vocs_constraints_to_constraints( def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: """Convert VOCS observables to optimas Parameter objects.""" parameters = [] - # Handle both old format (set of strings) and new format (dict with ObservableType) - if isinstance(self._vocs.observables, dict): - for obs_name, obs_spec in self._vocs.observables.items(): - if hasattr(obs_spec, "dtype"): - # New format with ObservableType - param = Parameter(name=obs_name, dtype=obs_spec.dtype) - else: - # Old format or simple string - param = Parameter(name=obs_name) - parameters.append(param) - else: - # Old format: set of strings - for obs_name in self._vocs.observables: - param = Parameter(name=obs_name) - parameters.append(param) + # Handle both set of strings and dict + for obs_name in self._vocs.observables: + if isinstance(self._vocs.observables, dict): + obs_spec = self._vocs.observables[obs_name] + else: + obs_spec = None + param = Parameter(name=obs_name, dtype=obs_spec) + parameters.append(param) return parameters @property diff --git a/tests/test_function_evaluator.py b/tests/test_function_evaluator.py index 2223aabc..00107930 100644 --- a/tests/test_function_evaluator.py +++ b/tests/test_function_evaluator.py @@ -4,6 +4,7 @@ import numpy as np import matplotlib.pyplot as plt import pytest +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -44,21 +45,23 @@ def test_function_evaluator(): for create_dirs in create_dirs_options: # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - # Test also more complex analyzed parameters. - p0 = Parameter("p0", dtype=(float, (2, 4))) - p1 = Parameter("p1", dtype="O") - p2 = Parameter("fig", dtype="O") + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables={ + "p0": (float, (2, 4)), + "p1": "O", + "fig": "O", + }, - # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[p0, p1, p2], ) + # Create generator. + gen = RandomSamplingGenerator(vocs=vocs) + # Create function evaluator. ev = FunctionEvaluator( function=eval_func, create_evaluation_dirs=create_dirs @@ -109,15 +112,16 @@ def test_function_evaluator_with_logs(): """Test a function evaluator with redirected stdout and stderr.""" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create function evaluator. ev = FunctionEvaluator( diff --git a/tests/test_template_evaluator.py b/tests/test_template_evaluator.py index 171ae7ff..921448bc 100644 --- a/tests/test_template_evaluator.py +++ b/tests/test_template_evaluator.py @@ -2,6 +2,7 @@ import numpy as np import matplotlib.pyplot as plt +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator @@ -24,20 +25,21 @@ def analysis_func(sim_dir, output_params): def test_template_evaluator(): # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - # Test also more complex analyzed parameters. - p0 = Parameter("p0", dtype=(float, (2, 4))) - p1 = Parameter("p1", dtype="O") - p2 = Parameter("fig", dtype="O") + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"}, + observables={ + "p0": (float, (2, 4)), + "p1": "O", + "fig": "O", + } + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[p0, p1, p2], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator. ev = TemplateEvaluator( @@ -87,15 +89,16 @@ def test_template_evaluator_timeout(): os.environ["OPTIMAS_TEST_SLEEP"] = "20" # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0] + }, + objectives={"f": "MAXIMIZE"} + ) # Define variables and objectives. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create template evaluator with 1s timeout. ev = TemplateEvaluator( From cca80ccd06949f1687080ea5cf106aa885d4621c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 22:22:04 +0000 Subject: [PATCH 16/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_function_evaluator.py | 13 +++---------- tests/test_template_evaluator.py | 14 ++++---------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/tests/test_function_evaluator.py b/tests/test_function_evaluator.py index 00107930..219016c2 100644 --- a/tests/test_function_evaluator.py +++ b/tests/test_function_evaluator.py @@ -46,17 +46,13 @@ def test_function_evaluator(): for create_dirs in create_dirs_options: # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, observables={ "p0": (float, (2, 4)), "p1": "O", "fig": "O", }, - ) # Create generator. @@ -113,11 +109,8 @@ def test_function_evaluator_with_logs(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Create generator. diff --git a/tests/test_template_evaluator.py b/tests/test_template_evaluator.py index 921448bc..ce05cf43 100644 --- a/tests/test_template_evaluator.py +++ b/tests/test_template_evaluator.py @@ -26,16 +26,13 @@ def analysis_func(sim_dir, output_params): def test_template_evaluator(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, observables={ "p0": (float, (2, 4)), "p1": "O", "fig": "O", - } + }, ) # Define variables and objectives. @@ -90,11 +87,8 @@ def test_template_evaluator_timeout(): # Define variables and objectives. vocs = VOCS( - variables={ - "x0": [-50.0, 5.0], - "x1": [-5.0, 15.0] - }, - objectives={"f": "MAXIMIZE"} + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) # Define variables and objectives. From 4b8d698e2e7ba78d50bb27a41c587e7ccee10509 Mon Sep 17 00:00:00 2001 From: shudson Date: Thu, 24 Jul 2025 17:27:56 -0500 Subject: [PATCH 17/58] Update exception test to vocs --- tests/test_exploration_run_exception.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_exploration_run_exception.py b/tests/test_exploration_run_exception.py index 96a3ebe7..d6c197f8 100644 --- a/tests/test_exploration_run_exception.py +++ b/tests/test_exploration_run_exception.py @@ -1,9 +1,9 @@ import os +from generator_standard.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): @@ -18,15 +18,13 @@ def test_exception_during_exploration_run(): even if an exception occurs. """ # Define variables and objectives. - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + ) # Create generator. - gen = RandomSamplingGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - ) + gen = RandomSamplingGenerator(vocs=vocs) # Create function evaluator. ev = FunctionEvaluator(function=eval_func, create_evaluation_dirs=True) From c7660e6fae5335bb6f6fa3aa207dc442cdeb4fd2 Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 25 Jul 2025 12:43:25 -0500 Subject: [PATCH 18/58] Update Ax base and service generators to vocs --- optimas/generators/ax/base.py | 18 +++++------------- optimas/generators/ax/service/base.py | 18 +++++------------- .../generators/ax/service/multi_fidelity.py | 19 ++++++------------- .../generators/ax/service/single_fidelity.py | 18 +++++------------- 4 files changed, 21 insertions(+), 52 deletions(-) diff --git a/optimas/generators/ax/base.py b/optimas/generators/ax/base.py index 63f8ac3a..a0eed5a6 100644 --- a/optimas/generators/ax/base.py +++ b/optimas/generators/ax/base.py @@ -7,6 +7,7 @@ from optimas.core import Objective, TrialParameter, VaryingParameter, Parameter from optimas.generators.base import Generator +from generator_standard.vocs import VOCS # Disable Ax loggers to get cleaner output. In principle, setting @@ -22,13 +23,8 @@ class AxGenerator(Generator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. + vocs : VOCS + VOCS object defining variables, objectives, constraints, and observables. use_cuda : bool, optional Whether to allow the generator to run on a CUDA GPU. By default ``False``. @@ -63,9 +59,7 @@ class AxGenerator(Generator): def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], - analyzed_parameters: Optional[List[Parameter]] = None, + vocs: VOCS, use_cuda: Optional[bool] = False, gpu_id: Optional[int] = 0, dedicated_resources: Optional[bool] = False, @@ -77,9 +71,7 @@ def __init__( allow_updating_parameters: Optional[bool] = False, ) -> None: super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, + vocs=vocs, use_cuda=use_cuda, gpu_id=gpu_id, dedicated_resources=dedicated_resources, diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index d55681d7..50db9335 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -30,6 +30,7 @@ convert_optimas_to_ax_parameters, convert_optimas_to_ax_objectives, ) +from generator_standard.vocs import VOCS class AxServiceGenerator(AxGenerator): @@ -37,13 +38,8 @@ class AxServiceGenerator(AxGenerator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. + vocs : VOCS + VOCS object defining variables, objectives, constraints, and observables. parameter_constraints : list of str, optional List of string representation of parameter constraints, such as ``"x3 >= x4"`` or ``"-x3 + 2*x4 - 3.5*x5 >= 2"``. @@ -92,9 +88,7 @@ class AxServiceGenerator(AxGenerator): def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], - analyzed_parameters: Optional[List[Parameter]] = None, + vocs: VOCS, parameter_constraints: Optional[List[str]] = None, outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, @@ -109,9 +103,7 @@ def __init__( model_history_dir: Optional[str] = "model_history", ) -> None: super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, + vocs=vocs, use_cuda=use_cuda, gpu_id=gpu_id, dedicated_resources=dedicated_resources, diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index 2c65e2f0..1701e722 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -11,6 +11,7 @@ from optimas.core import Objective, VaryingParameter, Parameter from .base import AxServiceGenerator +from generator_standard.vocs import VOCS class AxMultiFidelityGenerator(AxServiceGenerator): @@ -18,13 +19,9 @@ class AxMultiFidelityGenerator(AxServiceGenerator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. One them should be a fidelity. - objectives : list of Objective - List of optimization objectives. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. + vocs : VOCS + VOCS object defining variables, objectives, constraints, and observables. + One of the variables should be a fidelity parameter. outcome_constraints : list of str, optional List of string representation of outcome constraints (i.e., constraints on any of the ``analyzed_parameters``) of form @@ -72,9 +69,7 @@ class AxMultiFidelityGenerator(AxServiceGenerator): def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], - analyzed_parameters: Optional[List[Parameter]] = None, + vocs: VOCS, outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, enforce_n_init: Optional[bool] = False, @@ -90,9 +85,7 @@ def __init__( ) -> None: self.fidel_cost_intercept = fidel_cost_intercept super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, + vocs=vocs, outcome_constraints=outcome_constraints, n_init=n_init, enforce_n_init=enforce_n_init, diff --git a/optimas/generators/ax/service/single_fidelity.py b/optimas/generators/ax/service/single_fidelity.py index 6cbdef6c..2380b155 100644 --- a/optimas/generators/ax/service/single_fidelity.py +++ b/optimas/generators/ax/service/single_fidelity.py @@ -7,6 +7,7 @@ from optimas.core import Objective, VaryingParameter, Parameter from .base import AxServiceGenerator +from generator_standard.vocs import VOCS class AxSingleFidelityGenerator(AxServiceGenerator): @@ -23,13 +24,8 @@ class AxSingleFidelityGenerator(AxServiceGenerator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. - objectives : list of Objective - List of optimization objectives. - analyzed_parameters : list of Parameter, optional - List of parameters to analyze at each trial, but which are not - optimization objectives. By default ``None``. + vocs : VOCS + VOCS object defining variables, objectives, constraints, and observables. parameter_constraints : list of str, optional List of string representation of parameter constraints, such as ``"x3 >= x4"`` or ``"-x3 + 2*x4 - 3.5*x5 >= 2"``. @@ -93,9 +89,7 @@ class AxSingleFidelityGenerator(AxServiceGenerator): def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], - analyzed_parameters: Optional[List[Parameter]] = None, + vocs: VOCS, parameter_constraints: Optional[List[str]] = None, outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, @@ -112,9 +106,7 @@ def __init__( ) -> None: self._fully_bayesian = fully_bayesian super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, + vocs=vocs, parameter_constraints=parameter_constraints, outcome_constraints=outcome_constraints, n_init=n_init, From fa43bc0530484b30a71d304133aeb125fb44b4ef Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 25 Jul 2025 12:44:04 -0500 Subject: [PATCH 19/58] Update gpu resources test --- tests/test_gpu_resources.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/test_gpu_resources.py b/tests/test_gpu_resources.py index a7931550..4dff5609 100644 --- a/tests/test_gpu_resources.py +++ b/tests/test_gpu_resources.py @@ -4,7 +4,7 @@ set_start_method("spawn", force=True) import numpy as np -from optimas.core import VaryingParameter, Objective, Parameter +from generator_standard.vocs import VOCS from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -31,17 +31,16 @@ def create_exploration( gpu_id=0, exploration_dir_path="./exploration", ): - # Create varying parameters and objectives. - var_1 = VaryingParameter("x0", 0.0, 15.0) - var_2 = VaryingParameter("x1", 0.0, 15.0) - obj = Objective("f", minimize=True) - p0 = Parameter("cuda_visible_devices", dtype="U10") + # Create VOCS object + vocs = VOCS( + variables={"x0": [0.0, 15.0], "x1": [0.0, 15.0]}, + objectives={"f": "MINIMIZE"}, + observables={"cuda_visible_devices": "U10"}, + ) # Create generator. gen = AxSingleFidelityGenerator( - varying_parameters=[var_1, var_2], - objectives=[obj], - analyzed_parameters=[p0], + vocs=vocs, n_init=4, dedicated_resources=dedicated_resources, use_cuda=use_cuda, From 8e21bb572eac5a9cd25d0f9a6a23e73b733c2baa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:45:08 +0000 Subject: [PATCH 20/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimas/generators/ax/base.py b/optimas/generators/ax/base.py index a0eed5a6..0575986a 100644 --- a/optimas/generators/ax/base.py +++ b/optimas/generators/ax/base.py @@ -1,6 +1,6 @@ """Contains the definition of the base Ax generator.""" -from typing import List, Optional +from typing import Optional import logging import torch From deaeb6b22e628153cc7b8869631c50f760658c95 Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 25 Jul 2025 15:44:30 -0500 Subject: [PATCH 21/58] Handle constraints in service generators --- optimas/generators/ax/service/base.py | 35 +++++++++++++++---- .../generators/ax/service/multi_fidelity.py | 7 +--- .../generators/ax/service/single_fidelity.py | 2 -- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index 50db9335..676eafb8 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -30,7 +30,7 @@ convert_optimas_to_ax_parameters, convert_optimas_to_ax_objectives, ) -from generator_standard.vocs import VOCS +from generator_standard.vocs import VOCS, LessThanConstraint, GreaterThanConstraint, BoundsConstraint class AxServiceGenerator(AxGenerator): @@ -45,10 +45,7 @@ class AxServiceGenerator(AxGenerator): constraints, such as ``"x3 >= x4"`` or ``"-x3 + 2*x4 - 3.5*x5 >= 2"``. For the latter constraints, any number of arguments is accepted, and acceptable operators are ``<=`` and ``>=``. - outcome_constraints : list of str, optional - List of string representation of outcome constraints (i.e., constraints - on any of the ``analyzed_parameters``) of form - ``"metric_name >= bound"``, like ``"m1 <= 3."``. + n_init : int, optional Number of evaluations to perform during the initialization phase using Sobol sampling. If external data is attached to the exploration, the @@ -90,7 +87,6 @@ def __init__( self, vocs: VOCS, parameter_constraints: Optional[List[str]] = None, - outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, enforce_n_init: Optional[bool] = False, abandon_failed_trials: Optional[bool] = True, @@ -119,10 +115,35 @@ def __init__( self._fit_out_of_design = fit_out_of_design self._fixed_features = None self._parameter_constraints = parameter_constraints - self._outcome_constraints = outcome_constraints + self._outcome_constraints, constraint_parameters = self._convert_vocs_constraints_to_outcome_constraints() + # Add constraint parameters to analyzed parameters + if constraint_parameters: + if self._analyzed_parameters is None: + self._analyzed_parameters = [] + self._analyzed_parameters.extend(constraint_parameters) self._ax_client = self._create_ax_client() self._model = AxModelManager(self._ax_client) + def _convert_vocs_constraints_to_outcome_constraints(self) -> tuple[List[str], List[Parameter]]: + """Convert VOCS constraints to AX outcome constraints format and create analyzed parameters.""" + outcome_constraints = [] + constraint_parameters = [] + if hasattr(self._vocs, 'constraints') and self._vocs.constraints: + for constraint_name, constraint_spec in self._vocs.constraints.items(): + # Create analyzed parameter for this constraint + constraint_parameters.append(Parameter(constraint_name)) + + # Handle different constraint types + if isinstance(constraint_spec, LessThanConstraint): + outcome_constraints.append(f"{constraint_name} <= {constraint_spec.value}") + elif isinstance(constraint_spec, GreaterThanConstraint): + outcome_constraints.append(f"{constraint_name} >= {constraint_spec.value}") + elif isinstance(constraint_spec, BoundsConstraint): + lo, hi = constraint_spec.range + outcome_constraints.append(f"{constraint_name} >= {lo}") + outcome_constraints.append(f"{constraint_name} <= {hi}") + return outcome_constraints, constraint_parameters + @property def ax_client(self) -> AxClient: """Get the underlying AxClient.""" diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index 1701e722..7e624a09 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -22,10 +22,7 @@ class AxMultiFidelityGenerator(AxServiceGenerator): vocs : VOCS VOCS object defining variables, objectives, constraints, and observables. One of the variables should be a fidelity parameter. - outcome_constraints : list of str, optional - List of string representation of outcome constraints (i.e., constraints - on any of the ``analyzed_parameters``) of form - ``"metric_name >= bound"``, like ``"m1 <= 3."``. + n_init : int, optional Number of evaluations to perform during the initialization phase using Sobol sampling. If external data is attached to the exploration, the @@ -70,7 +67,6 @@ class AxMultiFidelityGenerator(AxServiceGenerator): def __init__( self, vocs: VOCS, - outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, enforce_n_init: Optional[bool] = False, abandon_failed_trials: Optional[bool] = True, @@ -86,7 +82,6 @@ def __init__( self.fidel_cost_intercept = fidel_cost_intercept super().__init__( vocs=vocs, - outcome_constraints=outcome_constraints, n_init=n_init, enforce_n_init=enforce_n_init, abandon_failed_trials=abandon_failed_trials, diff --git a/optimas/generators/ax/service/single_fidelity.py b/optimas/generators/ax/service/single_fidelity.py index 2380b155..af94a4e0 100644 --- a/optimas/generators/ax/service/single_fidelity.py +++ b/optimas/generators/ax/service/single_fidelity.py @@ -91,7 +91,6 @@ def __init__( self, vocs: VOCS, parameter_constraints: Optional[List[str]] = None, - outcome_constraints: Optional[List[str]] = None, n_init: Optional[int] = 4, enforce_n_init: Optional[bool] = False, abandon_failed_trials: Optional[bool] = True, @@ -108,7 +107,6 @@ def __init__( super().__init__( vocs=vocs, parameter_constraints=parameter_constraints, - outcome_constraints=outcome_constraints, n_init=n_init, enforce_n_init=enforce_n_init, abandon_failed_trials=abandon_failed_trials, From 68e2e13c3e33732b6f01ee473d5a141a531c2788 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 20:45:04 +0000 Subject: [PATCH 22/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/service/base.py | 32 ++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index 676eafb8..84eee595 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -30,7 +30,12 @@ convert_optimas_to_ax_parameters, convert_optimas_to_ax_objectives, ) -from generator_standard.vocs import VOCS, LessThanConstraint, GreaterThanConstraint, BoundsConstraint +from generator_standard.vocs import ( + VOCS, + LessThanConstraint, + GreaterThanConstraint, + BoundsConstraint, +) class AxServiceGenerator(AxGenerator): @@ -115,7 +120,9 @@ def __init__( self._fit_out_of_design = fit_out_of_design self._fixed_features = None self._parameter_constraints = parameter_constraints - self._outcome_constraints, constraint_parameters = self._convert_vocs_constraints_to_outcome_constraints() + self._outcome_constraints, constraint_parameters = ( + self._convert_vocs_constraints_to_outcome_constraints() + ) # Add constraint parameters to analyzed parameters if constraint_parameters: if self._analyzed_parameters is None: @@ -124,20 +131,29 @@ def __init__( self._ax_client = self._create_ax_client() self._model = AxModelManager(self._ax_client) - def _convert_vocs_constraints_to_outcome_constraints(self) -> tuple[List[str], List[Parameter]]: + def _convert_vocs_constraints_to_outcome_constraints( + self, + ) -> tuple[List[str], List[Parameter]]: """Convert VOCS constraints to AX outcome constraints format and create analyzed parameters.""" outcome_constraints = [] constraint_parameters = [] - if hasattr(self._vocs, 'constraints') and self._vocs.constraints: - for constraint_name, constraint_spec in self._vocs.constraints.items(): + if hasattr(self._vocs, "constraints") and self._vocs.constraints: + for ( + constraint_name, + constraint_spec, + ) in self._vocs.constraints.items(): # Create analyzed parameter for this constraint constraint_parameters.append(Parameter(constraint_name)) - + # Handle different constraint types if isinstance(constraint_spec, LessThanConstraint): - outcome_constraints.append(f"{constraint_name} <= {constraint_spec.value}") + outcome_constraints.append( + f"{constraint_name} <= {constraint_spec.value}" + ) elif isinstance(constraint_spec, GreaterThanConstraint): - outcome_constraints.append(f"{constraint_name} >= {constraint_spec.value}") + outcome_constraints.append( + f"{constraint_name} >= {constraint_spec.value}" + ) elif isinstance(constraint_spec, BoundsConstraint): lo, hi = constraint_spec.range outcome_constraints.append(f"{constraint_name} >= {lo}") From d4bb5dc4c8ecbe1c417780d5cfff780810b5d88e Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 25 Jul 2025 17:45:26 -0500 Subject: [PATCH 23/58] First pass at converting test_ax_generators to VOCS --- tests/test_ax_generators.py | 185 ++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 104 deletions(-) diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index fe1992a0..bced5cde 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -14,7 +14,8 @@ AxClientGenerator, ) from optimas.evaluators import FunctionEvaluator, MultitaskEvaluator -from optimas.core import VaryingParameter, Objective, Task, Parameter +from optimas.core import Task +from generator_standard.vocs import VOCS # Some tests will use threading (instead of multiprocessing) to be able to @@ -151,17 +152,15 @@ def test_ax_single_fidelity(): trial_count = 0 trials_to_fail = [2, 6] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - p1 = Parameter("p1") + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + constraints={"p1": ["LESS_THAN", 30.0]}, + ) gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[p1], + vocs=vocs, parameter_constraints=["x0 + x1 <= 10"], - outcome_constraints=["p1 <= 30"], ) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( @@ -190,7 +189,7 @@ def test_ax_single_fidelity(): assert all(history["x0"] + history["x1"] <= 10.0 + 1e-3) ocs = gen._ax_client.experiment.optimization_config.outcome_constraints assert len(ocs) == 1 - assert ocs[0].metric.name == p1.name + assert ocs[0].metric.name == "p1" # Save history for later restart test np.save("./tests_output/ax_sf_history", exploration._libe_history.H) @@ -213,17 +212,15 @@ def test_ax_single_fidelity_resume(): fit_out_of_design_vals = [False, True] for fit_out_of_design in fit_out_of_design_vals: - var1 = VaryingParameter("x0", 5.1, 6.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - p1 = Parameter("p1") + vocs = VOCS( + variables={"x0": [5.1, 6.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + constraints={"p1": ["LESS_THAN", 30.0]}, + ) gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[p1], + vocs=vocs, parameter_constraints=["x0 + x1 <= 10"], - outcome_constraints=["p1 <= 30"], fit_out_of_design=fit_out_of_design, ) ev = FunctionEvaluator(function=eval_func_sf) @@ -278,13 +275,12 @@ def test_ax_single_fidelity_int(): trial_count = 0 trials_to_fail = [2, 6] - var1 = VaryingParameter("x0", -50.0, 5.0, dtype=int) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], objectives=[obj] + vocs = VOCS( + variables={"x0": set(range(-50, 6)), "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -319,14 +315,12 @@ def test_ax_single_fidelity_moo(): trial_count = 0 trials_to_fail = [2, 6] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - obj2 = Objective("f2", minimize=False) - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], objectives=[obj, obj2] + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE", "f2": "MAXIMIZE"}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_sf_moo) exploration = Exploration( generator=gen, @@ -360,13 +354,12 @@ def test_ax_single_fidelity_fb(): trial_count = 0 trials_to_fail = [2, 6] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], objectives=[obj], fully_bayesian=True + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs, fully_bayesian=True) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -398,16 +391,12 @@ def test_ax_single_fidelity_moo_fb(): trial_count = 0 trials_to_fail = [2, 6] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - obj2 = Objective("f2", minimize=False) - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj, obj2], - fully_bayesian=True, + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE", "f2": "MAXIMIZE"}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs, fully_bayesian=True) ev = FunctionEvaluator(function=eval_func_sf_moo) exploration = Exploration( generator=gen, @@ -439,18 +428,18 @@ def test_ax_single_fidelity_updated_params(): trial_count = 0 trials_to_fail = [] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - - # Start with a fixed value of x0. - var1.fix_value(-10.0) + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + ) gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj], + vocs=vocs, fit_out_of_design=True, ) + + # Start with a fixed value of x0. + var1.fix_value(-10.0) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -505,20 +494,18 @@ def test_ax_multi_fidelity(): trial_count = 0 trials_to_fail = [2, 5] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - var3 = VaryingParameter( - "res", 1.0, 8.0, is_fidelity=True, fidelity_target_value=8.0 - ) - obj = Objective("f", minimize=False) - p1 = Parameter("p1") + # TODO: res needs is_fidelity=True, fidelity_target_value=8.0 + # var3 = VaryingParameter( + # "res", 1.0, 8.0, is_fidelity=True, fidelity_target_value=8.0 + # ) - gen = AxMultiFidelityGenerator( - varying_parameters=[var1, var2, var3], - objectives=[obj], - analyzed_parameters=[p1], - outcome_constraints=["p1 <= 30"], + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "res": [1.0, 8.0]}, + objectives={"f": "MAXIMIZE"}, + constraints={"p1": ["LESS_THAN", 30.0]}, ) + + gen = AxMultiFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, @@ -554,16 +541,16 @@ def test_ax_multi_fidelity(): def test_ax_multitask(): """Test that an exploration with a multitask generator runs""" - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + ) task1 = Task("task_1", n_init=2, n_opt=1) task2 = Task("task_2", n_init=5, n_opt=3) gen = AxMultitaskGenerator( - varying_parameters=[var1, var2], - objectives=[obj], + vocs=vocs, hifi_task=task1, lofi_task=task2, ) @@ -666,16 +653,13 @@ def test_ax_single_fidelity_with_history(): trial_count = 0 trials_to_fail = [] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) - p1 = Parameter("p1") - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj], - analyzed_parameters=[p1], + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + constraints={"p1": ["LESS_THAN", 30.0]}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -709,19 +693,14 @@ def test_ax_multi_fidelity_with_history(): trial_count = 0 trials_to_fail = [] - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - var3 = VaryingParameter( - "res", 1.0, 8.0, is_fidelity=True, fidelity_target_value=8.0 + # TODO: res needs is_fidelity=True, fidelity_target_value=8.0 + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "res": [1.0, 8.0]}, + objectives={"f": "MAXIMIZE"}, + constraints={"p1": ["LESS_THAN", 30.0]}, ) - obj = Objective("f", minimize=False) - p1 = Parameter("p1") - gen = AxMultiFidelityGenerator( - varying_parameters=[var1, var2, var3], - objectives=[obj], - analyzed_parameters=[p1], - ) + gen = AxMultiFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, @@ -749,16 +728,16 @@ def test_ax_multitask_with_history(): restarted from a history file """ - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + ) task1 = Task("task_1", n_init=2, n_opt=1) task2 = Task("task_2", n_init=5, n_opt=3) gen = AxMultitaskGenerator( - varying_parameters=[var1, var2], - objectives=[obj], + vocs=vocs, hifi_task=task1, lofi_task=task2, ) @@ -783,17 +762,16 @@ def test_ax_service_init(): or evaluations are given. """ - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - obj = Objective("f", minimize=False) + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + objectives={"f": "MAXIMIZE"}, + ) n_init = 2 n_external = 4 for i in range(n_external): - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], objectives=[obj], n_init=n_init - ) + gen = AxSingleFidelityGenerator(vocs=vocs, n_init=n_init) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -839,8 +817,7 @@ def test_ax_service_init(): # Test single case with `enforce_n_init=True` gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2], - objectives=[obj], + vocs=vocs, n_init=n_init, enforce_n_init=True, ) From cebffd1691e66d2685b15f1cfb87b5a2d0a10534 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:45:51 +0000 Subject: [PATCH 24/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_ax_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index bced5cde..34736be6 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -437,7 +437,7 @@ def test_ax_single_fidelity_updated_params(): vocs=vocs, fit_out_of_design=True, ) - + # Start with a fixed value of x0. var1.fix_value(-10.0) ev = FunctionEvaluator(function=eval_func_sf) From 36f6277b77d3d0354e51f9d40f79070124f14e7e Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 11:23:17 -0500 Subject: [PATCH 25/58] Make fix_value work --- optimas/generators/ax/service/base.py | 35 ++++++++++++++++++++++++++- tests/test_ax_generators.py | 25 +++++++++---------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index 84eee595..e4063561 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -50,12 +50,12 @@ class AxServiceGenerator(AxGenerator): constraints, such as ``"x3 >= x4"`` or ``"-x3 + 2*x4 - 3.5*x5 >= 2"``. For the latter constraints, any number of arguments is accepted, and acceptable operators are ``<=`` and ``>=``. - n_init : int, optional Number of evaluations to perform during the initialization phase using Sobol sampling. If external data is attached to the exploration, the number of initialization evaluations will be reduced by the same amount, unless `enforce_n_init=True`. By default, ``4``. + enforce_n_init : bool, optional Whether to enforce the generation of `n_init` Sobol trials, even if external data is supplied. By default, ``False``. @@ -381,3 +381,36 @@ def _mark_trial_as_failed(self, trial: Trial): ax_trial.mark_abandoned(unsafe=True) else: ax_trial.mark_failed(unsafe=True) + + def fix_value(self, var_name: str, value: float) -> None: + """Fix a parameter to a specific value.""" + var = None + for vp in self._varying_parameters: + if vp.name == var_name: + var = vp + break + + if var is None: + raise ValueError(f"Variable '{var_name}' not found in varying parameters") + + var.fix_value(value) + self._update_parameter(var) + + def free_value(self, var_name: str) -> None: + """Free a previously fixed parameter.""" + var = None + for vp in self._varying_parameters: + if vp.name == var_name: + var = vp + break + + if var is None: + raise ValueError(f"Variable '{var_name}' not found in varying parameters") + + if not var.is_fixed: + raise ValueError(f"Variable '{var_name}' was not previously fixed") + + var.free_value() + self._update_parameter(var) + + diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 34736be6..ab32c33e 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -439,7 +439,7 @@ def test_ax_single_fidelity_updated_params(): ) # Start with a fixed value of x0. - var1.fix_value(-10.0) + gen.fix_value("x0", -10.0) ev = FunctionEvaluator(function=eval_func_sf) exploration = Exploration( generator=gen, @@ -454,21 +454,21 @@ def test_ax_single_fidelity_updated_params(): assert all(exploration.history["x0"] == -10) # Free value of x0 and run 5 evals. - var1.free_value() - gen.update_parameter(var1) + gen.free_value("x0") exploration.run(n_evals=5) assert not all(exploration.history["x0"][-5:] == -10) - # Update range of x0 and run 10 evals. - var1.update_range(-20.0, 0.0) - gen.update_parameter(var1) - exploration.run(n_evals=10) - assert all(exploration.history["x0"][-10:] >= -20) - assert all(exploration.history["x0"][-10:] <= 0.0) + # SH TODO: Implement update_range in new interface + # Do we want update_domain function in VOCS? Or create new VOCS here? + # # Update range of x0 and run 10 evals. + # var1.update_range(-20.0, 0.0) + # gen.update_parameter(var1) + # exploration.run(n_evals=10) + # assert all(exploration.history["x0"][-10:] >= -20) + # assert all(exploration.history["x0"][-10:] <= 0.0) # Fix of x0 and run 5 evals. - var1.fix_value(-9) - gen.update_parameter(var1) + gen.fix_value("x0", -9) exploration.run(n_evals=5) assert all(exploration.history["x0"][-5:] == -9) @@ -477,8 +477,7 @@ def test_ax_single_fidelity_updated_params(): assert exploration.history["x0"].to_numpy()[-1] == -7 # Free value and run 3 evals. - var1.free_value() - gen.update_parameter(var1) + gen.free_value("x0") exploration.run(n_evals=3) assert all(exploration.history["x0"][-3:] != -9) From df8066b4f4c2eb4f6d9d725a162f77dc4dfbd2a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:23:33 +0000 Subject: [PATCH 26/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/service/base.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index e4063561..40f49a95 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -389,10 +389,12 @@ def fix_value(self, var_name: str, value: float) -> None: if vp.name == var_name: var = vp break - + if var is None: - raise ValueError(f"Variable '{var_name}' not found in varying parameters") - + raise ValueError( + f"Variable '{var_name}' not found in varying parameters" + ) + var.fix_value(value) self._update_parameter(var) @@ -403,14 +405,14 @@ def free_value(self, var_name: str) -> None: if vp.name == var_name: var = vp break - + if var is None: - raise ValueError(f"Variable '{var_name}' not found in varying parameters") - + raise ValueError( + f"Variable '{var_name}' not found in varying parameters" + ) + if not var.is_fixed: raise ValueError(f"Variable '{var_name}' was not previously fixed") - + var.free_value() self._update_parameter(var) - - From 9d9eaf7497a0158b3625d39b2f4502f6b360b400 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 11:47:02 -0500 Subject: [PATCH 27/58] Add update_range to work with vocs --- optimas/generators/ax/service/base.py | 31 +++++++++++++++++++++++++++ tests/test_ax_generators.py | 13 +++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index e4063561..0837b8f1 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -413,4 +413,35 @@ def free_value(self, var_name: str) -> None: var.free_value() self._update_parameter(var) + def update_range(self, var_name: str, lower_bound: float, upper_bound: float) -> None: + """Update the range of a parameter. + + Parameters + ---------- + var_name : str + Name of the variable to update. + lower_bound : float + New lower bound for the parameter. + upper_bound : float + New upper bound for the parameter. + """ + var = None + for vp in self._varying_parameters: + if vp.name == var_name: + var = vp + break + + if var is None: + raise ValueError(f"Variable '{var_name}' not found in varying parameters") + + # Update the VOCS variable domain + if var_name in self._vocs.variables: + self._vocs.variables[var_name].domain = [lower_bound, upper_bound] + + # Update the varying parameter + var.update_range(lower_bound, upper_bound) + + # Update the Ax client + self._update_parameter(var) + diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index ab32c33e..7c4fe8a1 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -458,14 +458,11 @@ def test_ax_single_fidelity_updated_params(): exploration.run(n_evals=5) assert not all(exploration.history["x0"][-5:] == -10) - # SH TODO: Implement update_range in new interface - # Do we want update_domain function in VOCS? Or create new VOCS here? - # # Update range of x0 and run 10 evals. - # var1.update_range(-20.0, 0.0) - # gen.update_parameter(var1) - # exploration.run(n_evals=10) - # assert all(exploration.history["x0"][-10:] >= -20) - # assert all(exploration.history["x0"][-10:] <= 0.0) + # Update range of x0 and run 10 evals. + gen.update_range("x0", -20.0, 0.0) + exploration.run(n_evals=10) + assert all(exploration.history["x0"][-10:] >= -20) + assert all(exploration.history["x0"][-10:] <= 0.0) # Fix of x0 and run 5 evals. gen.fix_value("x0", -9) From 4948cb557353ec8e1e07764dc6c392f0f0015a9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:53:42 +0000 Subject: [PATCH 28/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/service/base.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index 077113ae..858943f7 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -417,9 +417,11 @@ def free_value(self, var_name: str) -> None: var.free_value() self._update_parameter(var) - def update_range(self, var_name: str, lower_bound: float, upper_bound: float) -> None: + def update_range( + self, var_name: str, lower_bound: float, upper_bound: float + ) -> None: """Update the range of a parameter. - + Parameters ---------- var_name : str @@ -434,16 +436,18 @@ def update_range(self, var_name: str, lower_bound: float, upper_bound: float) -> if vp.name == var_name: var = vp break - + if var is None: - raise ValueError(f"Variable '{var_name}' not found in varying parameters") - + raise ValueError( + f"Variable '{var_name}' not found in varying parameters" + ) + # Update the VOCS variable domain if var_name in self._vocs.variables: self._vocs.variables[var_name].domain = [lower_bound, upper_bound] - + # Update the varying parameter var.update_range(lower_bound, upper_bound) - + # Update the Ax client self._update_parameter(var) From cf1211d71a9650bd37d2711179b0c57f13779739 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 13:04:00 -0500 Subject: [PATCH 29/58] Set fidelity parameter via generator --- .../generators/ax/service/multi_fidelity.py | 30 +++++++++++++++++++ tests/test_ax_generators.py | 10 ++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index 7e624a09..1ea4426a 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -130,3 +130,33 @@ def _create_generation_steps( ) return steps + + def set_fidelity_param( + self, var_name: str, is_fidelity: bool = True, fidelity_target_value: float = None + ) -> None: + """Set a parameter as the fidelity parameter for multi-fidelity optimization. + + Parameters + ---------- + var_name : str + Name of the variable to set as fidelity parameter. + is_fidelity : bool, optional + Whether this parameter is the fidelity parameter. Default True. + fidelity_target_value : float, optional + The target fidelity value for optimization. + """ + var = None + for vp in self._varying_parameters: + if vp.name == var_name: + var = vp + break + + if var is None: + raise ValueError(f"Variable '{var_name}' not found in varying parameters") + + var.is_fidelity = is_fidelity + if fidelity_target_value is not None: + var.fidelity_target_value = fidelity_target_value + + # Update the Ax client + self._update_parameter(var) diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 7c4fe8a1..259fa43d 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -490,11 +490,6 @@ def test_ax_multi_fidelity(): trial_count = 0 trials_to_fail = [2, 5] - # TODO: res needs is_fidelity=True, fidelity_target_value=8.0 - # var3 = VaryingParameter( - # "res", 1.0, 8.0, is_fidelity=True, fidelity_target_value=8.0 - # ) - vocs = VOCS( variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "res": [1.0, 8.0]}, objectives={"f": "MAXIMIZE"}, @@ -502,6 +497,7 @@ def test_ax_multi_fidelity(): ) gen = AxMultiFidelityGenerator(vocs=vocs) + gen.set_fidelity_param("res", is_fidelity=True, fidelity_target_value=8.0) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, @@ -522,7 +518,7 @@ def test_ax_multi_fidelity(): # Check constraints. ocs = gen._ax_client.experiment.optimization_config.outcome_constraints assert len(ocs) == 1 - assert ocs[0].metric.name == p1.name + assert ocs[0].metric.name == "p1" # Perform checks. check_run_ax_service(ax_client, gen, exploration, len(trials_to_fail)) @@ -689,7 +685,6 @@ def test_ax_multi_fidelity_with_history(): trial_count = 0 trials_to_fail = [] - # TODO: res needs is_fidelity=True, fidelity_target_value=8.0 vocs = VOCS( variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "res": [1.0, 8.0]}, objectives={"f": "MAXIMIZE"}, @@ -697,6 +692,7 @@ def test_ax_multi_fidelity_with_history(): ) gen = AxMultiFidelityGenerator(vocs=vocs) + gen.set_fidelity_param("res", is_fidelity=True, fidelity_target_value=8.0) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, From 9b60771061838e116d91fd1550fc36d74a301899 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 18:04:14 +0000 Subject: [PATCH 30/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/service/multi_fidelity.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index 1ea4426a..a9094a69 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -132,10 +132,13 @@ def _create_generation_steps( return steps def set_fidelity_param( - self, var_name: str, is_fidelity: bool = True, fidelity_target_value: float = None + self, + var_name: str, + is_fidelity: bool = True, + fidelity_target_value: float = None, ) -> None: """Set a parameter as the fidelity parameter for multi-fidelity optimization. - + Parameters ---------- var_name : str @@ -150,13 +153,15 @@ def set_fidelity_param( if vp.name == var_name: var = vp break - + if var is None: - raise ValueError(f"Variable '{var_name}' not found in varying parameters") - + raise ValueError( + f"Variable '{var_name}' not found in varying parameters" + ) + var.is_fidelity = is_fidelity if fidelity_target_value is not None: var.fidelity_target_value = fidelity_target_value - + # Update the Ax client self._update_parameter(var) From ce09d6b628684e15fb55866a767950caa4ccc6e7 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 15:27:40 -0500 Subject: [PATCH 31/58] Update multitask for vocs and apply id mapping --- optimas/generators/ax/developer/multitask.py | 52 ++++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index dc64beb6..d6cb4f88 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -67,6 +67,7 @@ TrialStatus, ) from .ax_metric import AxMetric +from generator_standard.vocs import VOCS # Define generator states. NOT_STARTED = "not_started" @@ -152,10 +153,8 @@ class AxMultitaskGenerator(AxGenerator): Parameters ---------- - varying_parameters : list of VaryingParameter - List of input parameters to vary. One them should be a fidelity. - objectives : list of Objective - List of optimization objectives. Only one objective is supported. + vocs : VOCS + VOCS object defining variables, objectives, constraints, and observables. lofi_task, hifi_task : Task The low- and high-fidelity tasks. analyzed_parameters : list of Parameter, optional @@ -184,11 +183,9 @@ class AxMultitaskGenerator(AxGenerator): def __init__( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, lofi_task: Task, hifi_task: Task, - analyzed_parameters: Optional[List[Parameter]] = None, use_cuda: Optional[bool] = False, gpu_id: Optional[int] = 0, dedicated_resources: Optional[bool] = False, @@ -200,15 +197,11 @@ def __init__( # Ax trial_index and arm toegther locate a point # Multiple points (Optimas trials) can share the same Ax trial_index custom_trial_parameters = [ - TrialParameter("arm_name", "ax_arm_name", dtype="U32"), TrialParameter("trial_type", "ax_trial_type", dtype="U32"), - TrialParameter("ax_trial_id", "ax_trial_index", dtype=int), ] - self._check_inputs(varying_parameters, objectives, lofi_task, hifi_task) + self._check_inputs(vocs, lofi_task, hifi_task) super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, + vocs=vocs, use_cuda=use_cuda, gpu_id=gpu_id, dedicated_resources=dedicated_resources, @@ -230,6 +223,10 @@ def __init__( self.current_trial = None self.gr_lofi = None self._experiment = self._create_experiment() + + # Internal mapping: _id -> (arm_name, ax_trial_id, trial_type) + self._id_mapping = {} + self._next_id = 0 def get_gen_specs( self, sim_workers: int, run_params: Dict, sim_max: int @@ -244,14 +241,13 @@ def get_gen_specs( def _check_inputs( self, - varying_parameters: List[VaryingParameter], - objectives: List[Objective], + vocs: VOCS, lofi_task: Task, hifi_task: Task, ) -> None: """Check that the generator inputs are valid.""" # Check that only one objective has been given. - n_objectives = len(objectives) + n_objectives = len(vocs.objectives) assert n_objectives == 1, ( "Multitask generator supports only a single objective. " "Objectives given: {}.".format(n_objectives) @@ -274,11 +270,16 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: var.name: arm.parameters.get(var.name) for var in self._varying_parameters } - # SH for VOCS standard these will need to be 'variables' - # For now much match the trial parameter names. - point["ax_trial_id"] = trial_index - point["arm_name"] = arm.name - point["trial_type"] = trial_type + # Generate unique _id and store mapping + current_id = self._next_id + self._id_mapping[current_id] = { + "arm_name": arm.name, + "ax_trial_id": trial_index, + "trial_type": trial_type + } + point["_id"] = current_id + point["trial_type"] = trial_type # Keep trial_type for now + self._next_id += 1 points.append(point) return points @@ -295,6 +296,15 @@ def ingest(self, results: List[dict]) -> None: custom_parameters=self._custom_trial_parameters, ) trials.append(trial) + + # Apply _id mapping to all trials before processing + for trial in trials: + if trial.gen_id is not None and trial.gen_id in self._id_mapping: + mapping = self._id_mapping[trial.gen_id] + trial.arm_name = mapping["arm_name"] + trial.ax_trial_id = mapping["ax_trial_id"] + # trial_type should already be in trial from custom_parameters + if self.gen_state == NOT_STARTED: self._incorporate_external_data(trials) else: From 07eee24334329436f12f5e81194b0d884de628d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:27:55 +0000 Subject: [PATCH 32/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/developer/multitask.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index d6cb4f88..64591829 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -223,7 +223,7 @@ def __init__( self.current_trial = None self.gr_lofi = None self._experiment = self._create_experiment() - + # Internal mapping: _id -> (arm_name, ax_trial_id, trial_type) self._id_mapping = {} self._next_id = 0 @@ -275,7 +275,7 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: self._id_mapping[current_id] = { "arm_name": arm.name, "ax_trial_id": trial_index, - "trial_type": trial_type + "trial_type": trial_type, } point["_id"] = current_id point["trial_type"] = trial_type # Keep trial_type for now @@ -296,7 +296,7 @@ def ingest(self, results: List[dict]) -> None: custom_parameters=self._custom_trial_parameters, ) trials.append(trial) - + # Apply _id mapping to all trials before processing for trial in trials: if trial.gen_id is not None and trial.gen_id in self._id_mapping: @@ -304,7 +304,7 @@ def ingest(self, results: List[dict]) -> None: trial.arm_name = mapping["arm_name"] trial.ax_trial_id = mapping["ax_trial_id"] # trial_type should already be in trial from custom_parameters - + if self.gen_state == NOT_STARTED: self._incorporate_external_data(trials) else: From 9df7f765fd17afb948bbc77b188c3ac51bbb3038 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 16:38:39 -0500 Subject: [PATCH 33/58] Specify multitask trial_type as discrete variable --- optimas/generators/ax/developer/multitask.py | 55 ++++++++++++++------ optimas/generators/base.py | 17 +++--- tests/test_ax_generators.py | 4 +- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index 64591829..846052bb 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -67,7 +67,7 @@ TrialStatus, ) from .ax_metric import AxMetric -from generator_standard.vocs import VOCS +from generator_standard.vocs import VOCS, DiscreteVariable # Define generator states. NOT_STARTED = "not_started" @@ -193,13 +193,11 @@ def __init__( model_save_period: Optional[int] = 5, model_history_dir: Optional[str] = "model_history", ) -> None: - # As trial parameters these get written to history array - # Ax trial_index and arm toegther locate a point - # Multiple points (Optimas trials) can share the same Ax trial_index - custom_trial_parameters = [ - TrialParameter("trial_type", "ax_trial_type", dtype="U32"), - ] self._check_inputs(vocs, lofi_task, hifi_task) + + # Convert discrete variables to trial parameters before calling super().__init__ + custom_trial_parameters = self._convert_discrete_variables_to_trial_parameters(vocs) + super().__init__( vocs=vocs, use_cuda=use_cuda, @@ -223,11 +221,22 @@ def __init__( self.current_trial = None self.gr_lofi = None self._experiment = self._create_experiment() - + # Internal mapping: _id -> (arm_name, ax_trial_id, trial_type) self._id_mapping = {} self._next_id = 0 + def _convert_discrete_variables_to_trial_parameters(self, vocs: VOCS) -> List[TrialParameter]: + """Convert discrete variables from VOCS to TrialParameter objects.""" + trial_parameters = [] + for var_name, var_spec in vocs.variables.items(): + if isinstance(var_spec, DiscreteVariable): + # Convert discrete variable to trial parameter + max_len = max(len(str(val)) for val in var_spec.values) + trial_param = TrialParameter(var_name, var_name, dtype=f"U{max_len}") + trial_parameters.append(trial_param) + return trial_parameters + def get_gen_specs( self, sim_workers: int, run_params: Dict, sim_max: int ) -> Dict: @@ -239,6 +248,23 @@ def get_gen_specs( gen_specs["out"].append(("task", str, max_length)) return gen_specs + def _validate_vocs(self, vocs: VOCS) -> None: + """Validate VOCS for multitask generator.""" + super()._validate_vocs(vocs) + # Check that only one objective has been given. + n_objectives = len(vocs.objectives) + assert n_objectives == 1, ( + "Multitask generator supports only a single objective. " + "Objectives given: {}.".format(n_objectives) + ) + # Check that there is a discrete variable called 'trial_type' + assert "trial_type" in vocs.variables, ( + "Multitask generator requires a discrete variable named 'trial_type'" + ) + assert isinstance(vocs.variables["trial_type"], DiscreteVariable), ( + "Variable 'trial_type' must be a discrete variable" + ) + def _check_inputs( self, vocs: VOCS, @@ -246,12 +272,6 @@ def _check_inputs( hifi_task: Task, ) -> None: """Check that the generator inputs are valid.""" - # Check that only one objective has been given. - n_objectives = len(vocs.objectives) - assert n_objectives == 1, ( - "Multitask generator supports only a single objective. " - "Objectives given: {}.".format(n_objectives) - ) # Check that the number of low-fidelity trials per iteration is larger # than that of high-fidelity trials. assert lofi_task.n_opt >= hifi_task.n_opt, ( @@ -270,6 +290,12 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: var.name: arm.parameters.get(var.name) for var in self._varying_parameters } + # SH We can use a discrete var here in vocs (converted for now to trial parameters) + # But unlike varying parameters the name refers to a fixed generator concept. + for trial_param in self._custom_trial_parameters: + if trial_param.name == "trial_type": + point[trial_param.name] = trial_type + # Generate unique _id and store mapping current_id = self._next_id self._id_mapping[current_id] = { @@ -278,7 +304,6 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: "trial_type": trial_type, } point["_id"] = current_id - point["trial_type"] = trial_type # Keep trial_type for now self._next_id += 1 points.append(point) return points diff --git a/optimas/generators/base.py b/optimas/generators/base.py index d3b59c07..51289608 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -20,7 +20,7 @@ TrialParameter, TrialStatus, ) -from generator_standard.vocs import VOCS +from generator_standard.vocs import VOCS, ContinuousVariable from generator_standard.generator import Generator as StandardGenerator logger = get_logger(__name__) @@ -127,13 +127,14 @@ def _convert_vocs_variables_to_varying_parameters( varying_parameters = [] for var_name, var_spec in self._vocs.variables.items(): # Only handle ContinuousVariable for now - vp = VaryingParameter( - name=var_name, - lower_bound=var_spec.domain[0], - upper_bound=var_spec.domain[1], - default_value=var_spec.default_value, - ) - varying_parameters.append(vp) + if isinstance(var_spec, ContinuousVariable): + vp = VaryingParameter( + name=var_name, + lower_bound=var_spec.domain[0], + upper_bound=var_spec.domain[1], + default_value=var_spec.default_value, + ) + varying_parameters.append(vp) return varying_parameters def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 259fa43d..8d2572ff 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -534,7 +534,7 @@ def test_ax_multitask(): """Test that an exploration with a multitask generator runs""" vocs = VOCS( - variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "trial_type": {"task_1", "task_2"}}, objectives={"f": "MAXIMIZE"}, ) @@ -721,7 +721,7 @@ def test_ax_multitask_with_history(): """ vocs = VOCS( - variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0]}, + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "trial_type": {"task_1", "task_2"}}, objectives={"f": "MAXIMIZE"}, ) From bbc23bc617d0d71e2729a9072e0c8d31ec1d81a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:38:53 +0000 Subject: [PATCH 34/58] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- optimas/generators/ax/developer/multitask.py | 32 ++++++++++++-------- tests/test_ax_generators.py | 12 ++++++-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index 846052bb..a53bb4f8 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -194,10 +194,12 @@ def __init__( model_history_dir: Optional[str] = "model_history", ) -> None: self._check_inputs(vocs, lofi_task, hifi_task) - + # Convert discrete variables to trial parameters before calling super().__init__ - custom_trial_parameters = self._convert_discrete_variables_to_trial_parameters(vocs) - + custom_trial_parameters = ( + self._convert_discrete_variables_to_trial_parameters(vocs) + ) + super().__init__( vocs=vocs, use_cuda=use_cuda, @@ -221,19 +223,23 @@ def __init__( self.current_trial = None self.gr_lofi = None self._experiment = self._create_experiment() - + # Internal mapping: _id -> (arm_name, ax_trial_id, trial_type) self._id_mapping = {} self._next_id = 0 - def _convert_discrete_variables_to_trial_parameters(self, vocs: VOCS) -> List[TrialParameter]: + def _convert_discrete_variables_to_trial_parameters( + self, vocs: VOCS + ) -> List[TrialParameter]: """Convert discrete variables from VOCS to TrialParameter objects.""" trial_parameters = [] for var_name, var_spec in vocs.variables.items(): if isinstance(var_spec, DiscreteVariable): # Convert discrete variable to trial parameter max_len = max(len(str(val)) for val in var_spec.values) - trial_param = TrialParameter(var_name, var_name, dtype=f"U{max_len}") + trial_param = TrialParameter( + var_name, var_name, dtype=f"U{max_len}" + ) trial_parameters.append(trial_param) return trial_parameters @@ -258,12 +264,12 @@ def _validate_vocs(self, vocs: VOCS) -> None: "Objectives given: {}.".format(n_objectives) ) # Check that there is a discrete variable called 'trial_type' - assert "trial_type" in vocs.variables, ( - "Multitask generator requires a discrete variable named 'trial_type'" - ) - assert isinstance(vocs.variables["trial_type"], DiscreteVariable), ( - "Variable 'trial_type' must be a discrete variable" - ) + assert ( + "trial_type" in vocs.variables + ), "Multitask generator requires a discrete variable named 'trial_type'" + assert isinstance( + vocs.variables["trial_type"], DiscreteVariable + ), "Variable 'trial_type' must be a discrete variable" def _check_inputs( self, @@ -295,7 +301,7 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: for trial_param in self._custom_trial_parameters: if trial_param.name == "trial_type": point[trial_param.name] = trial_type - + # Generate unique _id and store mapping current_id = self._next_id self._id_mapping[current_id] = { diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 8d2572ff..82175526 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -534,7 +534,11 @@ def test_ax_multitask(): """Test that an exploration with a multitask generator runs""" vocs = VOCS( - variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "trial_type": {"task_1", "task_2"}}, + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0], + "trial_type": {"task_1", "task_2"}, + }, objectives={"f": "MAXIMIZE"}, ) @@ -721,7 +725,11 @@ def test_ax_multitask_with_history(): """ vocs = VOCS( - variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "trial_type": {"task_1", "task_2"}}, + variables={ + "x0": [-50.0, 5.0], + "x1": [-5.0, 15.0], + "trial_type": {"task_1", "task_2"}, + }, objectives={"f": "MAXIMIZE"}, ) From 6f0933fc73b1e681dcc89f31fee6800baa283e0a Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 28 Jul 2025 19:36:33 -0500 Subject: [PATCH 35/58] Create vocs in ax_client --- optimas/generators/ax/service/ax_client.py | 46 +++++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/optimas/generators/ax/service/ax_client.py b/optimas/generators/ax/service/ax_client.py index ce6db0f0..c392a96b 100644 --- a/optimas/generators/ax/service/ax_client.py +++ b/optimas/generators/ax/service/ax_client.py @@ -6,6 +6,7 @@ from ax.core.objective import MultiObjective from optimas.core import Objective, VaryingParameter, Parameter +from generator_standard.vocs import VOCS from .base import AxServiceGenerator @@ -71,18 +72,19 @@ def __init__( model_save_period: Optional[int] = 5, model_history_dir: Optional[str] = "model_history", ): - varying_parameters = self._get_varying_parameters(ax_client) - objectives = self._get_objectives(ax_client) + # Create VOCS object from AxClient data + vocs = self._create_vocs_from_ax_client(ax_client) + + # Add constraints to analyzed parameters analyzed_parameters = self._add_constraints_to_analyzed_parameters( analyzed_parameters, ax_client ) + use_cuda = self._use_cuda(ax_client) self._ax_client = ax_client + super().__init__( - varying_parameters=varying_parameters, - objectives=objectives, - analyzed_parameters=analyzed_parameters, - enforce_n_init=True, + vocs=vocs, abandon_failed_trials=abandon_failed_trials, use_cuda=use_cuda, gpu_id=gpu_id, @@ -92,6 +94,38 @@ def __init__( model_history_dir=model_history_dir, ) + def _create_vocs_from_ax_client(self, ax_client: AxClient) -> VOCS: + """Create a VOCS object from the AxClient data.""" + # Extract variables from search space + variables = {} + for _, p in ax_client.experiment.search_space.parameters.items(): + variables[p.name] = [p.lower, p.upper] + + # Extract objectives from optimization config + objectives = {} + ax_objective = ax_client.experiment.optimization_config.objective + if isinstance(ax_objective, MultiObjective): + ax_objectives = ax_objective.objectives + else: + ax_objectives = [ax_objective] + + for ax_obj in ax_objectives: + obj_type = "MINIMIZE" if ax_obj.minimize else "MAXIMIZE" + objectives[ax_obj.metric_names[0]] = obj_type + + # Extract observables from outcome constraints (if any) + observables = set() + ax_config = ax_client.experiment.optimization_config + if ax_config.outcome_constraints: + for constraint in ax_config.outcome_constraints: + observables.add(constraint.metric.name) + + return VOCS( + variables=variables, + objectives=objectives, + observables=observables, + ) + def _get_varying_parameters(self, ax_client: AxClient): """Obtain the list of varying parameters from the AxClient.""" varying_parameters = [] From 3a6ef4403c7852294cc20a71db41092393103a3a Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 29 Jul 2025 16:43:10 -0500 Subject: [PATCH 36/58] Update model manager test --- tests/test_ax_model_manager.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/test_ax_model_manager.py b/tests/test_ax_model_manager.py index 20fd2e46..89e7b67c 100644 --- a/tests/test_ax_model_manager.py +++ b/tests/test_ax_model_manager.py @@ -4,11 +4,11 @@ from matplotlib.gridspec import GridSpec from optimas.explorations import Exploration -from optimas.core import VaryingParameter, Objective from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import FunctionEvaluator from optimas.diagnostics import ExplorationDiagnostics from optimas.utils.ax import AxModelManager +from generator_standard.vocs import VOCS def eval_func_sf_moo(input_params, output_params): @@ -26,15 +26,13 @@ def test_ax_model_manager(): runs and that the generator and Ax client are updated after running. """ - var1 = VaryingParameter("x0", -50.0, 5.0) - var2 = VaryingParameter("x1", -5.0, 15.0) - var3 = VaryingParameter("x2", -5.0, 15.0) - obj = Objective("f", minimize=True) - obj2 = Objective("f2", minimize=False) - - gen = AxSingleFidelityGenerator( - varying_parameters=[var1, var2, var3], objectives=[obj, obj2] + # Create VOCS object + vocs = VOCS( + variables={"x0": [-50.0, 5.0], "x1": [-5.0, 15.0], "x2": [-5.0, 15.0]}, + objectives={"f": "MINIMIZE", "f2": "MAXIMIZE"}, ) + + gen = AxSingleFidelityGenerator(vocs=vocs) ev = FunctionEvaluator(function=eval_func_sf_moo) exploration = Exploration( generator=gen, @@ -88,8 +86,12 @@ def test_ax_model_manager(): gs = GridSpec(2, 2, wspace=0.2, hspace=0.3) # center coordinates - x1_c = 0.5 * (var2.lower_bound + var2.upper_bound) - x2_c = 0.5 * (var3.lower_bound + var3.upper_bound) + x1_c = 0.5 * ( + vocs.variables["x1"].domain[0] + vocs.variables["x1"].domain[1] + ) + x2_c = 0.5 * ( + vocs.variables["x2"].domain[0] + vocs.variables["x2"].domain[1] + ) # plot model for `f` with custom slice value fig, ax1 = mm_axcl.plot_contour( From f883c7b2a5161f492d84c19aa69f50f30a41e7f1 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 30 Jul 2025 15:30:40 -0500 Subject: [PATCH 37/58] Revert multitask to preserve ax id/arm info. * This way they are stored and read in from previous history * They are not part of vocs * Using TrialParameter uses already setup format in Optimas * They get put in the libE history array * trial_type is a vocs discrete, but uses TrialParameter internally --- optimas/generators/ax/developer/multitask.py | 35 ++++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index a53bb4f8..c0b212d6 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -193,10 +193,20 @@ def __init__( model_save_period: Optional[int] = 5, model_history_dir: Optional[str] = "model_history", ) -> None: + + # As trial parameters these get written to history array + # Ax trial_index and arm toegther locate a point + # Multiple points (Optimas trials) can share the same Ax trial_index + # vocs interface note: These are not part of vocs. They are only stored + # to allow keeping track of them from previous runs. + custom_trial_parameters = [ + TrialParameter("arm_name", "ax_arm_name", dtype="U32"), + TrialParameter("ax_trial_id", "ax_trial_index", dtype=int), + ] self._check_inputs(vocs, lofi_task, hifi_task) # Convert discrete variables to trial parameters before calling super().__init__ - custom_trial_parameters = ( + custom_trial_parameters.extend( self._convert_discrete_variables_to_trial_parameters(vocs) ) @@ -224,10 +234,6 @@ def __init__( self.gr_lofi = None self._experiment = self._create_experiment() - # Internal mapping: _id -> (arm_name, ax_trial_id, trial_type) - self._id_mapping = {} - self._next_id = 0 - def _convert_discrete_variables_to_trial_parameters( self, vocs: VOCS ) -> List[TrialParameter]: @@ -302,15 +308,8 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: if trial_param.name == "trial_type": point[trial_param.name] = trial_type - # Generate unique _id and store mapping - current_id = self._next_id - self._id_mapping[current_id] = { - "arm_name": arm.name, - "ax_trial_id": trial_index, - "trial_type": trial_type, - } - point["_id"] = current_id - self._next_id += 1 + point["ax_trial_id"] = trial_index + point["arm_name"] = arm.name points.append(point) return points @@ -328,14 +327,6 @@ def ingest(self, results: List[dict]) -> None: ) trials.append(trial) - # Apply _id mapping to all trials before processing - for trial in trials: - if trial.gen_id is not None and trial.gen_id in self._id_mapping: - mapping = self._id_mapping[trial.gen_id] - trial.arm_name = mapping["arm_name"] - trial.ax_trial_id = mapping["ax_trial_id"] - # trial_type should already be in trial from custom_parameters - if self.gen_state == NOT_STARTED: self._incorporate_external_data(trials) else: From 346dbeefef9b529440667299d56fad9564ca8b60 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 30 Jul 2025 16:17:56 -0500 Subject: [PATCH 38/58] Convert Discrete integer range to VaryingParameter --- optimas/generators/base.py | 25 ++++++++++++++++++++++--- tests/test_ax_generators.py | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 51289608..39ab6923 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -20,7 +20,7 @@ TrialParameter, TrialStatus, ) -from generator_standard.vocs import VOCS, ContinuousVariable +from generator_standard.vocs import VOCS, ContinuousVariable, DiscreteVariable from generator_standard.generator import Generator as StandardGenerator logger = get_logger(__name__) @@ -114,7 +114,7 @@ def __init__( self._check_parameters(self._varying_parameters) def _validate_vocs(self, vocs: VOCS) -> None: - """This generator should have atleast one variable and one objective""" + """Ensure the generator has at least one variable and one objective.""" if not vocs.variables: raise ValueError("VOCS must define at least one variable.") if not vocs.objectives: @@ -126,7 +126,7 @@ def _convert_vocs_variables_to_varying_parameters( """Convert VOCS variables to optimas VaryingParameter objects.""" varying_parameters = [] for var_name, var_spec in self._vocs.variables.items(): - # Only handle ContinuousVariable for now + # Handle ContinuousVariable if isinstance(var_spec, ContinuousVariable): vp = VaryingParameter( name=var_name, @@ -135,6 +135,25 @@ def _convert_vocs_variables_to_varying_parameters( default_value=var_spec.default_value, ) varying_parameters.append(vp) + # Handle DiscreteVariable that is a range of integers + # TODO: Suggest supporting IntegerVariables in vocs + elif isinstance(var_spec, DiscreteVariable): + values = list(var_spec.values) + if len(values) > 1: + # Check if values form a continuous integer range + sorted_values = sorted(values) + if all( + isinstance(v, int) for v in values + ) and sorted_values == list( + range(sorted_values[0], sorted_values[-1] + 1) + ): + vp = VaryingParameter( + name=var_name, + lower_bound=sorted_values[0], + upper_bound=sorted_values[-1], + dtype=int, + ) + varying_parameters.append(vp) return varying_parameters def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 82175526..114e278f 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -275,6 +275,7 @@ def test_ax_single_fidelity_int(): trial_count = 0 trials_to_fail = [2, 6] + # TODO: Suggest supporting IntegerVariables in vocs vocs = VOCS( variables={"x0": set(range(-50, 6)), "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, From 844ff1a785a7bdd0811e1933cad455995bbb9874 Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Mon, 18 Aug 2025 10:56:13 -0700 Subject: [PATCH 39/58] No need to pass `is_fidelity` --- optimas/generators/ax/service/multi_fidelity.py | 5 +---- tests/test_ax_generators.py | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index a9094a69..df8c9190 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -134,7 +134,6 @@ def _create_generation_steps( def set_fidelity_param( self, var_name: str, - is_fidelity: bool = True, fidelity_target_value: float = None, ) -> None: """Set a parameter as the fidelity parameter for multi-fidelity optimization. @@ -143,8 +142,6 @@ def set_fidelity_param( ---------- var_name : str Name of the variable to set as fidelity parameter. - is_fidelity : bool, optional - Whether this parameter is the fidelity parameter. Default True. fidelity_target_value : float, optional The target fidelity value for optimization. """ @@ -159,7 +156,7 @@ def set_fidelity_param( f"Variable '{var_name}' not found in varying parameters" ) - var.is_fidelity = is_fidelity + var.is_fidelity = True if fidelity_target_value is not None: var.fidelity_target_value = fidelity_target_value diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 114e278f..5c5b9cb8 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -498,7 +498,7 @@ def test_ax_multi_fidelity(): ) gen = AxMultiFidelityGenerator(vocs=vocs) - gen.set_fidelity_param("res", is_fidelity=True, fidelity_target_value=8.0) + gen.set_fidelity_param("res", fidelity_target_value=8.0) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, @@ -697,7 +697,7 @@ def test_ax_multi_fidelity_with_history(): ) gen = AxMultiFidelityGenerator(vocs=vocs) - gen.set_fidelity_param("res", is_fidelity=True, fidelity_target_value=8.0) + gen.set_fidelity_param("res", fidelity_target_value=8.0) ev = FunctionEvaluator(function=eval_func_mf) exploration = Exploration( generator=gen, From d019af55c281bd187de78c50311c0b56bb248f51 Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 19 Aug 2025 12:12:04 -0500 Subject: [PATCH 40/58] Update dummy examples to use vocs --- examples/dummy/run_example.py | 26 ++++++++++--------- examples/dummy_grid_sampling/run_example.py | 26 ++++++++++--------- examples/dummy_line_sampling/run_example.py | 26 ++++++++++--------- examples/dummy_mf/run_example.py | 28 +++++++++++---------- examples/dummy_mt/run_example.py | 27 ++++++++++++-------- examples/dummy_random/run_example.py | 26 ++++++++++--------- 6 files changed, 87 insertions(+), 72 deletions(-) diff --git a/examples/dummy/run_example.py b/examples/dummy/run_example.py index a586967f..929adfee 100644 --- a/examples/dummy/run_example.py +++ b/examples/dummy/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel Bayesian optimization with Ax.""" -from optimas.core import VaryingParameter, Objective +from generator_standard.vocs import VOCS from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -10,16 +10,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,16 +37,18 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0) -var_2 = VaryingParameter("x1", 0.0, 15.0) -obj = Objective("f", minimize=True) +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": [0.0, 15.0], + "x1": [0.0, 15.0], + }, + objectives={"f": "MINIMIZE"}, +) # Create generator. -gen = AxSingleFidelityGenerator( - varying_parameters=[var_1, var_2], objectives=[obj], n_init=2 -) +gen = AxSingleFidelityGenerator(vocs=vocs, n_init=2) # Create evaluator. diff --git a/examples/dummy_grid_sampling/run_example.py b/examples/dummy_grid_sampling/run_example.py index 89365677..63c88ccf 100644 --- a/examples/dummy_grid_sampling/run_example.py +++ b/examples/dummy_grid_sampling/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel grid sampling with simulations.""" -from optimas.core import VaryingParameter, Objective +from generator_standard.vocs import VOCS from optimas.generators import GridSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -10,16 +10,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,16 +37,18 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0) -var_2 = VaryingParameter("x1", 0.0, 15.0) -obj = Objective("f") +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": [0.0, 15.0], + "x1": [0.0, 15.0], + }, + objectives={"f": "MAXIMIZE"}, +) # Create generator. -gen = GridSamplingGenerator( - varying_parameters=[var_1, var_2], objectives=[obj], n_steps=[5, 7] -) +gen = GridSamplingGenerator(vocs=vocs, n_steps=[5, 7]) # Create evaluator. diff --git a/examples/dummy_line_sampling/run_example.py b/examples/dummy_line_sampling/run_example.py index d0084c02..56953671 100644 --- a/examples/dummy_line_sampling/run_example.py +++ b/examples/dummy_line_sampling/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel line sampling with simulations.""" -from optimas.core import VaryingParameter, Objective +from generator_standard.vocs import VOCS, ContinuousVariable from optimas.generators import LineSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -10,16 +10,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,16 +37,18 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0, default_value=5.0) -var_2 = VaryingParameter("x1", 0.0, 15.0, default_value=6.0) -obj = Objective("f") +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": ContinuousVariable(domain=[0.0, 15.0], default_value=5.0), + "x1": ContinuousVariable(domain=[0.0, 15.0], default_value=6.0), + }, + objectives={"f": "MAXIMIZE"}, +) # Create generator. -gen = LineSamplingGenerator( - varying_parameters=[var_1, var_2], objectives=[obj], n_steps=[5, 7] -) +gen = LineSamplingGenerator(vocs=vocs, n_steps=[5, 7]) # Create evaluator. diff --git a/examples/dummy_mf/run_example.py b/examples/dummy_mf/run_example.py index 402cd471..b8cef63e 100644 --- a/examples/dummy_mf/run_example.py +++ b/examples/dummy_mf/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel multi-fidelity Bayesian optimization with Ax.""" -from optimas.core import VaryingParameter, Objective +from generator_standard.vocs import VOCS from optimas.generators import AxMultiFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -10,16 +10,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,22 +37,24 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters (including fidelity) and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0) -var_2 = VaryingParameter("x1", 0.0, 15.0) -res = VaryingParameter( - "resolution", 1.0, 8.0, is_fidelity=True, fidelity_target_value=8.0 +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": [0.0, 15.0], + "x1": [0.0, 15.0], + "resolution": [1.0, 8.0], + }, + objectives={"f": "MINIMIZE"}, ) -obj = Objective("f", minimize=True) # Create generator. gen = AxMultiFidelityGenerator( - varying_parameters=[var_1, var_2, res], - objectives=[obj], + vocs=vocs, n_init=4, fidel_cost_intercept=2.0, ) +gen.set_fidelity_param("resolution", fidelity_target_value=8.0) # Create evaluator. diff --git a/examples/dummy_mt/run_example.py b/examples/dummy_mt/run_example.py index bce75f3a..6a51c546 100644 --- a/examples/dummy_mt/run_example.py +++ b/examples/dummy_mt/run_example.py @@ -1,6 +1,7 @@ """Basic example of parallel multitask Bayesian optimization with Ax.""" -from optimas.core import VaryingParameter, Objective, Task +from generator_standard.vocs import VOCS +from optimas.core import Task from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator from optimas.explorations import Exploration @@ -10,16 +11,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,10 +38,15 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0) -var_2 = VaryingParameter("x1", 0.0, 15.0) -obj = Objective("f", minimize=True) +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": [0.0, 15.0], + "x1": [0.0, 15.0], + "trial_type": {"cheap_model", "expensive_model"}, + }, + objectives={"f": "MINIMIZE"}, +) # Create tasks. @@ -50,8 +56,7 @@ def analyze_simulation(simulation_directory, output_params): # Create generator. gen = AxMultitaskGenerator( - varying_parameters=[var_1, var_2], - objectives=[obj], + vocs=vocs, lofi_task=lofi_task, hifi_task=hifi_task, ) diff --git a/examples/dummy_random/run_example.py b/examples/dummy_random/run_example.py index 2978323a..260ed4ed 100644 --- a/examples/dummy_random/run_example.py +++ b/examples/dummy_random/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel random sampling with simulations.""" -from optimas.core import VaryingParameter, Objective +from generator_standard.vocs import VOCS from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration @@ -10,16 +10,16 @@ def analyze_simulation(simulation_directory, output_params): """Analyze the simulation output. This method analyzes the output generated by the simulation to - obtain the value of the optimization objective and other analyzed - parameters, if specified. The value of these parameters has to be - given to the `output_params` dictionary. + obtain the value of the optimization objective and other observables. + The value of these parameters has to be given to the + `output_params` dictionary. Parameters ---------- simulation_directory : str Path to the simulation folder where the output was generated. output_params : dict - Dictionary where the value of the objectives and analyzed parameters + Dictionary where the value of the objectives and observables will be stored. There is one entry per parameter, where the key is the name of the parameter given by the user. @@ -37,16 +37,18 @@ def analyze_simulation(simulation_directory, output_params): return output_params -# Create varying parameters and objectives. -var_1 = VaryingParameter("x0", 0.0, 15.0) -var_2 = VaryingParameter("x1", 0.0, 15.0) -obj = Objective("f") +# Create VOCS object defining variables, objectives. +vocs = VOCS( + variables={ + "x0": [0.0, 15.0], + "x1": [0.0, 15.0], + }, + objectives={"f": "MAXIMIZE"}, +) # Create generator. -gen = RandomSamplingGenerator( - varying_parameters=[var_1, var_2], objectives=[obj], distribution="normal" -) +gen = RandomSamplingGenerator(vocs=vocs, distribution="normal") # Create evaluator. From 21210b9e431803029a29d84462ffd1bc5545626f Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 19 Aug 2025 12:13:34 -0500 Subject: [PATCH 41/58] Update line --- optimas/generators/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 39ab6923..d4707f9c 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -78,7 +78,7 @@ def __init__( allow_fixed_parameters: Optional[bool] = False, allow_updating_parameters: Optional[bool] = False, ) -> None: - # Initialize the standard generator which called validate_vocs + # Initialize the standard generator which calls `_validate_vocs` super().__init__(vocs) # Store copies to prevent unexpected behavior if parameters are changed From b760144f6bd6478d6513e0454b24ffd83bbc0640 Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 19 Aug 2025 12:14:53 -0500 Subject: [PATCH 42/58] Remove duplicate line --- optimas/diagnostics/exploration_diagnostics.py | 1 - 1 file changed, 1 deletion(-) diff --git a/optimas/diagnostics/exploration_diagnostics.py b/optimas/diagnostics/exploration_diagnostics.py index bf631214..14b5ec4b 100644 --- a/optimas/diagnostics/exploration_diagnostics.py +++ b/optimas/diagnostics/exploration_diagnostics.py @@ -102,7 +102,6 @@ def _create_exploration( p = Parameter.model_validate_json(param["value"]) analyzed_parameters.append(p) - # Create exploration using dummy generator and evaluator. # Create exploration using dummy generator and evaluator. variables = {} for vp in varying_parameters: From ee2cb137312068859f4af60a5a0c485c588de93c Mon Sep 17 00:00:00 2001 From: shudson Date: Tue, 19 Aug 2025 16:01:51 -0500 Subject: [PATCH 43/58] Update ionization examples to use vocs --- examples/ionization_injection/run_example.py | 29 ++++++++-------- .../ionization_injection_mf/run_example.py | 33 +++++++++---------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/examples/ionization_injection/run_example.py b/examples/ionization_injection/run_example.py index 100f9d64..33c5db5d 100644 --- a/examples/ionization_injection/run_example.py +++ b/examples/ionization_injection/run_example.py @@ -13,33 +13,30 @@ the `analysis_script.py` file. """ -from optimas.core import Parameter, VaryingParameter, Objective from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("laser_scale", 0.7, 1.05) -var_2 = VaryingParameter("z_foc", 3.0, 7.5) -var_3 = VaryingParameter("mult", 0.1, 1.5) -var_4 = VaryingParameter("plasma_scale", 0.6, 0.8) -obj = Objective("f", minimize=False) - - -# Define additional parameters to analyze. -energy_med = Parameter("energy_med") -energy_mad = Parameter("energy_mad") -charge = Parameter("charge") +# Create VOCS object. +vocs = VOCS( + variables={ + "laser_scale": [0.7, 1.05], + "z_foc": [3.0, 7.5], + "mult": [0.1, 1.5], + "plasma_scale": [0.6, 0.8], + }, + objectives={"f": "MAXIMIZE"}, + observables=["energy_med", "energy_mad", "charge"], +) # Create generator. gen = AxSingleFidelityGenerator( - varying_parameters=[var_1, var_2, var_3, var_4], - objectives=[obj], - analyzed_parameters=[energy_med, energy_mad, charge], + vocs=vocs, n_init=4, ) diff --git a/examples/ionization_injection_mf/run_example.py b/examples/ionization_injection_mf/run_example.py index c153deab..e30a58ea 100644 --- a/examples/ionization_injection_mf/run_example.py +++ b/examples/ionization_injection_mf/run_example.py @@ -13,36 +13,33 @@ the `analysis_script.py` file. """ -from optimas.core import Parameter, VaryingParameter, Objective from optimas.generators import AxMultiFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("laser_scale", 0.7, 1.05) -var_2 = VaryingParameter("z_foc", 3.0, 7.5) -var_3 = VaryingParameter("mult", 0.1, 1.5) -var_4 = VaryingParameter("plasma_scale", 0.6, 0.8) -res = VaryingParameter( - "resolution", 2.0, 4.0, is_fidelity=True, fidelity_target_value=4.0 +# Create VOCS object. +vocs = VOCS( + variables={ + "laser_scale": [0.7, 1.05], + "z_foc": [3.0, 7.5], + "mult": [0.1, 1.5], + "plasma_scale": [0.6, 0.8], + "resolution": [2.0, 4.0], + }, + objectives={"f": "MINIMIZE"}, + observables=["energy_med", "energy_mad", "charge"], + fidelity_parameter="resolution", + fidelity_target_value=4.0, ) -obj = Objective("f", minimize=True) - - -# Define additional parameters to analyze. -energy_med = Parameter("energy_med") -energy_mad = Parameter("energy_mad") -charge = Parameter("charge") # Create generator. gen = AxMultiFidelityGenerator( - varying_parameters=[var_1, var_2, var_3, var_4, res], - objectives=[obj], - analyzed_parameters=[energy_med, energy_mad, charge], + vocs=vocs, n_init=4, ) From b91a7dbb24bdd071f3345594f88bbe5706304ebd Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 20 Aug 2025 11:01:26 -0500 Subject: [PATCH 44/58] Update remaining examples to use vocs --- .../astra/run_optimization_serial_ASTRA.py | 30 +++++++++--------- examples/hipace/run_example.py | 23 ++++++-------- examples/multi_stage/run_example.py | 26 +++++++--------- examples/multitask_lpa_fbpic_waket/run_opt.py | 31 +++++++++---------- examples/wake_t/run_example.py | 16 +++++----- examples/wake_t_fbpic_mt/run_example.py | 17 ++++++---- 6 files changed, 70 insertions(+), 73 deletions(-) diff --git a/examples/astra/run_optimization_serial_ASTRA.py b/examples/astra/run_optimization_serial_ASTRA.py index 434ca5a5..64f46635 100644 --- a/examples/astra/run_optimization_serial_ASTRA.py +++ b/examples/astra/run_optimization_serial_ASTRA.py @@ -9,34 +9,32 @@ https://optimas.readthedocs.io/en/latest/index.html """ -from optimas.core import VaryingParameter, Objective, Parameter from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. +# Create VOCS object. # name of parameter, lower bound of values to be explored, # upper bound of values to be explored -var_1 = VaryingParameter("RF_phase", -2.5, 2.5) -var_2 = VaryingParameter("B_sol", 0.12, 0.38) -# Objectives that will be minimized: -obj_1 = Objective("bunch_length", minimize=True) -obj_2 = Objective("emittance", minimize=True) -# Additional example parameters that will be analyzed but are not used for the -# optimization: -em_x = Parameter("emittance_x") -em_y = Parameter("emittance_y") +vocs = VOCS( + variables={ + "RF_phase": [-2.5, 2.5], + "B_sol": [0.12, 0.38], + }, + objectives={ + "bunch_length": "MINIMIZE", + "emittance": "MINIMIZE", + }, + observables=["emittance_x", "emittance_y"], +) # Create generator. # Pick the generator to be used, here Single-fidelity Bayesian optimization. -# The analyzed_parameters are parameters that are calculated for each -# simulation but not used for the optimization. gen = AxSingleFidelityGenerator( - varying_parameters=[var_1, var_2], - objectives=[obj_1, obj_2], + vocs=vocs, n_init=8, - analyzed_parameters=[em_x, em_y], ) diff --git a/examples/hipace/run_example.py b/examples/hipace/run_example.py index fa1789ad..b07e4af5 100644 --- a/examples/hipace/run_example.py +++ b/examples/hipace/run_example.py @@ -10,30 +10,27 @@ the `analysis_script.py` file. """ -from optimas.core import Parameter, VaryingParameter, Objective from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("witness_charge", 0.05, 1.0) -obj = Objective("f", minimize=False) - - -# Define additional parameters to analyze. -energy_med = Parameter("energy_med") -energy_mad = Parameter("energy_mad") -charge = Parameter("charge") +# Create VOCS object. +vocs = VOCS( + variables={ + "witness_charge": [0.05, 1.0], + }, + objectives={"f": "MAXIMIZE"}, + observables=["energy_med", "energy_mad", "charge"], +) # Create generator. gen = AxSingleFidelityGenerator( - varying_parameters=[var_1], - objectives=[obj], - analyzed_parameters=[energy_med, energy_mad, charge], + vocs=vocs, n_init=4, ) diff --git a/examples/multi_stage/run_example.py b/examples/multi_stage/run_example.py index cea0aeb6..612e5493 100644 --- a/examples/multi_stage/run_example.py +++ b/examples/multi_stage/run_example.py @@ -10,32 +10,28 @@ the `analysis_script.py` file. """ -from optimas.core import Parameter, VaryingParameter, Objective from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("adjust_factor", 0.7, 1.05) -var_2 = VaryingParameter("lens_start", 0.32, 0.347) -obj = Objective("f", minimize=True) - - -# Define additional parameters to analyze. -energy_std = Parameter("energy_std") -energy_avg = Parameter("energy_avg") -charge = Parameter("charge") -emittance = Parameter("emittance") +# Create VOCS object. +vocs = VOCS( + variables={ + "adjust_factor": [0.7, 1.05], + "lens_start": [0.32, 0.347], + }, + objectives={"f": "MINIMIZE"}, + observables=["energy_std", "energy_avg", "charge", "emittance"], +) # Create generator. gen = AxSingleFidelityGenerator( - varying_parameters=[var_1, var_2], - objectives=[obj], - analyzed_parameters=[energy_std, energy_avg, charge, emittance], + vocs=vocs, n_init=4, ) diff --git a/examples/multitask_lpa_fbpic_waket/run_opt.py b/examples/multitask_lpa_fbpic_waket/run_opt.py index f7b3ca73..c2aab41d 100644 --- a/examples/multitask_lpa_fbpic_waket/run_opt.py +++ b/examples/multitask_lpa_fbpic_waket/run_opt.py @@ -2,26 +2,27 @@ from multiprocessing import set_start_method -from optimas.core import VaryingParameter, Objective, Parameter, Task +from optimas.core import Task from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("beam_i_1", 1.0, 10.0) # kA -var_2 = VaryingParameter("beam_i_2", 1.0, 10.0) # kA -var_3 = VaryingParameter("beam_z_i_2", -10.0, 10.0) # µm -var_4 = VaryingParameter("beam_length", 1.0, 20.0) # µm -obj = Objective("f", minimize=True) - - -# Define other quantities to analyze (which are not the optimization objective) -par_1 = Parameter("energy_med") -par_2 = Parameter("energy_mad") -par_3 = Parameter("charge") +# Create VOCS object. +vocs = VOCS( + variables={ + "beam_i_1": [1.0, 10.0], # kA + "beam_i_2": [1.0, 10.0], # kA + "beam_z_i_2": [-10.0, 10.0], # µm + "beam_length": [1.0, 20.0], # µm + "trial_type": {"wake-t", "fbpic"}, + }, + objectives={"f": "MINIMIZE"}, + observables=["energy_med", "energy_mad", "charge"], +) # Create tasks. @@ -31,9 +32,7 @@ # Create generator. gen = AxMultitaskGenerator( - varying_parameters=[var_1, var_2, var_3, var_4], - objectives=[obj], - analyzed_parameters=[par_1, par_2, par_3], + vocs=vocs, use_cuda=True, dedicated_resources=True, hifi_task=hifi_task, diff --git a/examples/wake_t/run_example.py b/examples/wake_t/run_example.py index 64367684..36df8660 100644 --- a/examples/wake_t/run_example.py +++ b/examples/wake_t/run_example.py @@ -11,23 +11,25 @@ file. """ -from optimas.core import VaryingParameter, Objective from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("g_lens", 100.0, 1000.0) -obj = Objective("f", minimize=True) +# Create VOCS object. +vocs = VOCS( + variables={ + "g_lens": [100.0, 1000.0], + }, + objectives={"f": "MINIMIZE"}, +) # Create generator. -gen = AxSingleFidelityGenerator( - varying_parameters=[var_1], objectives=[obj], n_init=12 -) +gen = AxSingleFidelityGenerator(vocs=vocs, n_init=12) # Create evaluator. diff --git a/examples/wake_t_fbpic_mt/run_example.py b/examples/wake_t_fbpic_mt/run_example.py index 62954902..e75ce9ab 100644 --- a/examples/wake_t_fbpic_mt/run_example.py +++ b/examples/wake_t_fbpic_mt/run_example.py @@ -12,17 +12,23 @@ file. """ -from optimas.core import VaryingParameter, Objective, Task +from optimas.core import Task from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator from optimas.explorations import Exploration +from generator_standard.vocs import VOCS from analysis_script import analyze_simulation -# Create varying parameters and objectives. -var_1 = VaryingParameter("g_lens", 100.0, 1000.0) -obj = Objective("f", minimize=True) +# Create VOCS object. +vocs = VOCS( + variables={ + "g_lens": [100.0, 1000.0], + "trial_type": {"wake-t", "fbpic"}, + }, + objectives={"f": "MINIMIZE"}, +) # Create tasks. lofi_task = Task("wake-t", n_init=12, n_opt=12) @@ -31,8 +37,7 @@ # Create generator. gen = AxMultitaskGenerator( - varying_parameters=[var_1], - objectives=[obj], + vocs=vocs, lofi_task=lofi_task, hifi_task=hifi_task, ) From ff9272355bf4790ced9450de903c6e1fbf80b539 Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 20 Aug 2025 12:22:42 -0500 Subject: [PATCH 45/58] Handle discrete variables in gen base class --- optimas/generators/ax/developer/multitask.py | 20 ------------- optimas/generators/base.py | 31 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index c0b212d6..75a98efd 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -205,11 +205,6 @@ def __init__( ] self._check_inputs(vocs, lofi_task, hifi_task) - # Convert discrete variables to trial parameters before calling super().__init__ - custom_trial_parameters.extend( - self._convert_discrete_variables_to_trial_parameters(vocs) - ) - super().__init__( vocs=vocs, use_cuda=use_cuda, @@ -234,21 +229,6 @@ def __init__( self.gr_lofi = None self._experiment = self._create_experiment() - def _convert_discrete_variables_to_trial_parameters( - self, vocs: VOCS - ) -> List[TrialParameter]: - """Convert discrete variables from VOCS to TrialParameter objects.""" - trial_parameters = [] - for var_name, var_spec in vocs.variables.items(): - if isinstance(var_spec, DiscreteVariable): - # Convert discrete variable to trial parameter - max_len = max(len(str(val)) for val in var_spec.values) - trial_param = TrialParameter( - var_name, var_name, dtype=f"U{max_len}" - ) - trial_parameters.append(trial_param) - return trial_parameters - def get_gen_specs( self, sim_workers: int, run_params: Dict, sim_max: int ) -> Dict: diff --git a/optimas/generators/base.py b/optimas/generators/base.py index d4707f9c..c99037ac 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -105,6 +105,12 @@ def __init__( self._custom_trial_parameters = ( [] if custom_trial_parameters is None else custom_trial_parameters ) + + # Automatically add discrete variables as trial parameters + discrete_trial_params = ( + self._convert_vocs_discrete_variables_to_trial_parameters() + ) + self._custom_trial_parameters.extend(discrete_trial_params) self._allow_fixed_parameters = allow_fixed_parameters self._allow_updating_parameters = allow_updating_parameters self._gen_function = persistent_generator @@ -191,6 +197,31 @@ def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: parameters.append(param) return parameters + def _convert_vocs_discrete_variables_to_trial_parameters( + self, + ) -> List[TrialParameter]: + """Convert discrete variables from VOCS to TrialParameter objects. + + Only converts discrete variables that were NOT already converted to + VaryingParameters. + """ + trial_parameters = [] + # Get the names of variables that were already converted to + # VaryingParameters + varying_param_names = {vp.name for vp in self._varying_parameters} + + for var_name, var_spec in self._vocs.variables.items(): + if isinstance(var_spec, DiscreteVariable): + # Only convert if it wasn't already converted to a + # VaryingParameter + if var_name not in varying_param_names: + max_len = max(len(str(val)) for val in var_spec.values) + trial_param = TrialParameter( + var_name, var_name, dtype=f"U{max_len}" + ) + trial_parameters.append(trial_param) + return trial_parameters + @property def vocs(self) -> VOCS: """Get the VOCS object.""" From e7e359fa4bae5275f356f0fa0a5cba69b403c435 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 22 Sep 2025 13:11:35 -0500 Subject: [PATCH 46/58] Move CI to main generator standard branch --- .github/workflows/unix-noax.yml | 2 +- .github/workflows/unix-openmpi.yml | 2 +- .github/workflows/unix.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unix-noax.yml b/.github/workflows/unix-noax.yml index 04a643fe..57715288 100644 --- a/.github/workflows/unix-noax.yml +++ b/.github/workflows/unix-noax.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type + pip install git+https://github.com/campa-consortium/generator_standard.git pip uninstall --yes ax-platform # Run without Ax - shell: bash -l {0} name: Run unit tests without Ax diff --git a/.github/workflows/unix-openmpi.yml b/.github/workflows/unix-openmpi.yml index 86af3eb1..68370041 100644 --- a/.github/workflows/unix-openmpi.yml +++ b/.github/workflows/unix-openmpi.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py openmpi=5.* pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type + pip install git+https://github.com/campa-consortium/generator_standard.git - shell: bash -l {0} name: Run unit tests with openMPI run: | diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 34749727..44455d3a 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git@obs_type + pip install git+https://github.com/campa-consortium/generator_standard.git - shell: bash -l {0} name: Run unit tests with MPICH run: | From 296f3d55c3c4da6d1532ae278511a8f6de692855 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 22 Sep 2025 13:12:29 -0500 Subject: [PATCH 47/58] Update to handle vocs observables as objects --- optimas/generators/base.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index c99037ac..3f56ce0b 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -187,13 +187,9 @@ def _convert_vocs_constraints_to_constraints( def _convert_vocs_observables_to_parameters(self) -> List[Parameter]: """Convert VOCS observables to optimas Parameter objects.""" parameters = [] - # Handle both set of strings and dict - for obs_name in self._vocs.observables: - if isinstance(self._vocs.observables, dict): - obs_spec = self._vocs.observables[obs_name] - else: - obs_spec = None - param = Parameter(name=obs_name, dtype=obs_spec) + for obs_name, obs_spec in self._vocs.observables.items(): + dtype = obs_spec.dtype + param = Parameter(name=obs_name, dtype=dtype) parameters.append(param) return parameters From 1f207bf232858dbbbdecdf781e44faa87eca1a4e Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 1 Oct 2025 14:45:45 -0500 Subject: [PATCH 48/58] Update generator_standard to gest_api --- .github/workflows/unix-noax.yml | 2 +- .github/workflows/unix-openmpi.yml | 2 +- .github/workflows/unix.yml | 2 +- examples/astra/run_optimization_serial_ASTRA.py | 2 +- examples/dummy/run_example.py | 2 +- examples/dummy_grid_sampling/run_example.py | 2 +- examples/dummy_line_sampling/run_example.py | 2 +- examples/dummy_mf/run_example.py | 2 +- examples/dummy_mt/run_example.py | 2 +- examples/dummy_random/run_example.py | 2 +- examples/hipace/run_example.py | 2 +- examples/ionization_injection/run_example.py | 2 +- examples/ionization_injection_mf/run_example.py | 2 +- examples/multi_stage/run_example.py | 2 +- examples/multitask_lpa_fbpic_waket/run_opt.py | 2 +- examples/wake_t/run_example.py | 2 +- examples/wake_t_fbpic_mt/run_example.py | 2 +- optimas/diagnostics/exploration_diagnostics.py | 2 +- optimas/generators/ax/base.py | 4 ++-- optimas/generators/ax/developer/multitask.py | 5 +---- optimas/generators/ax/service/ax_client.py | 2 +- optimas/generators/ax/service/base.py | 4 +--- optimas/generators/ax/service/multi_fidelity.py | 3 +-- optimas/generators/ax/service/single_fidelity.py | 3 +-- optimas/generators/base.py | 4 ++-- optimas/generators/grid_sampling.py | 4 ++-- optimas/generators/line_sampling.py | 4 ++-- optimas/generators/random_sampling.py | 4 ++-- tests/test_analyzed_parameters.py | 3 +-- tests/test_ax_generators.py | 2 +- tests/test_ax_model_manager.py | 2 +- tests/test_chain_evaluator.py | 3 +-- tests/test_comms.py | 3 +-- tests/test_env_script.py | 3 +-- tests/test_exploration_diagnostics.py | 3 +-- tests/test_exploration_resume.py | 3 +-- tests/test_exploration_run_exception.py | 2 +- tests/test_function_evaluator.py | 3 +-- tests/test_gpu_resources.py | 2 +- tests/test_grid_sampling.py | 3 +-- tests/test_grid_sampling_mpi.py | 3 +-- tests/test_line_sampling.py | 3 +-- tests/test_manual_exploration.py | 3 +-- tests/test_random_sampling.py | 3 +-- tests/test_template_evaluator.py | 3 +-- 45 files changed, 50 insertions(+), 70 deletions(-) diff --git a/.github/workflows/unix-noax.yml b/.github/workflows/unix-noax.yml index 57715288..5c772541 100644 --- a/.github/workflows/unix-noax.yml +++ b/.github/workflows/unix-noax.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/gest-api.git pip uninstall --yes ax-platform # Run without Ax - shell: bash -l {0} name: Run unit tests without Ax diff --git a/.github/workflows/unix-openmpi.yml b/.github/workflows/unix-openmpi.yml index 68370041..c74be8b5 100644 --- a/.github/workflows/unix-openmpi.yml +++ b/.github/workflows/unix-openmpi.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py openmpi=5.* pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/gest-api.git - shell: bash -l {0} name: Run unit tests with openMPI run: | diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 44455d3a..92b0112f 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -31,7 +31,7 @@ jobs: conda install numpy pandas pytorch cpuonly -c pytorch conda install -c conda-forge mpi4py mpich pip install .[test] - pip install git+https://github.com/campa-consortium/generator_standard.git + pip install git+https://github.com/campa-consortium/gest-api.git - shell: bash -l {0} name: Run unit tests with MPICH run: | diff --git a/examples/astra/run_optimization_serial_ASTRA.py b/examples/astra/run_optimization_serial_ASTRA.py index 64f46635..bf83a6b6 100644 --- a/examples/astra/run_optimization_serial_ASTRA.py +++ b/examples/astra/run_optimization_serial_ASTRA.py @@ -12,7 +12,7 @@ from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation # Create VOCS object. diff --git a/examples/dummy/run_example.py b/examples/dummy/run_example.py index 929adfee..29287464 100644 --- a/examples/dummy/run_example.py +++ b/examples/dummy/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel Bayesian optimization with Ax.""" -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/examples/dummy_grid_sampling/run_example.py b/examples/dummy_grid_sampling/run_example.py index 63c88ccf..c3838c0e 100644 --- a/examples/dummy_grid_sampling/run_example.py +++ b/examples/dummy_grid_sampling/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel grid sampling with simulations.""" -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.generators import GridSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/examples/dummy_line_sampling/run_example.py b/examples/dummy_line_sampling/run_example.py index 56953671..2de74516 100644 --- a/examples/dummy_line_sampling/run_example.py +++ b/examples/dummy_line_sampling/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel line sampling with simulations.""" -from generator_standard.vocs import VOCS, ContinuousVariable +from gest_api.vocs import VOCS, ContinuousVariable from optimas.generators import LineSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/examples/dummy_mf/run_example.py b/examples/dummy_mf/run_example.py index b8cef63e..be62133f 100644 --- a/examples/dummy_mf/run_example.py +++ b/examples/dummy_mf/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel multi-fidelity Bayesian optimization with Ax.""" -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.generators import AxMultiFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/examples/dummy_mt/run_example.py b/examples/dummy_mt/run_example.py index 6a51c546..50a185aa 100644 --- a/examples/dummy_mt/run_example.py +++ b/examples/dummy_mt/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel multitask Bayesian optimization with Ax.""" -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.core import Task from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator diff --git a/examples/dummy_random/run_example.py b/examples/dummy_random/run_example.py index 260ed4ed..31a38923 100644 --- a/examples/dummy_random/run_example.py +++ b/examples/dummy_random/run_example.py @@ -1,6 +1,6 @@ """Basic example of parallel random sampling with simulations.""" -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/examples/hipace/run_example.py b/examples/hipace/run_example.py index b07e4af5..5c8a1644 100644 --- a/examples/hipace/run_example.py +++ b/examples/hipace/run_example.py @@ -13,7 +13,7 @@ from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/ionization_injection/run_example.py b/examples/ionization_injection/run_example.py index 33c5db5d..8e9445b6 100644 --- a/examples/ionization_injection/run_example.py +++ b/examples/ionization_injection/run_example.py @@ -16,7 +16,7 @@ from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/ionization_injection_mf/run_example.py b/examples/ionization_injection_mf/run_example.py index e30a58ea..789987bc 100644 --- a/examples/ionization_injection_mf/run_example.py +++ b/examples/ionization_injection_mf/run_example.py @@ -16,7 +16,7 @@ from optimas.generators import AxMultiFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/multi_stage/run_example.py b/examples/multi_stage/run_example.py index 612e5493..5bc86014 100644 --- a/examples/multi_stage/run_example.py +++ b/examples/multi_stage/run_example.py @@ -13,7 +13,7 @@ from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/multitask_lpa_fbpic_waket/run_opt.py b/examples/multitask_lpa_fbpic_waket/run_opt.py index c2aab41d..6fb2f743 100644 --- a/examples/multitask_lpa_fbpic_waket/run_opt.py +++ b/examples/multitask_lpa_fbpic_waket/run_opt.py @@ -6,7 +6,7 @@ from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/wake_t/run_example.py b/examples/wake_t/run_example.py index 36df8660..f3a4175e 100644 --- a/examples/wake_t/run_example.py +++ b/examples/wake_t/run_example.py @@ -14,7 +14,7 @@ from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/examples/wake_t_fbpic_mt/run_example.py b/examples/wake_t_fbpic_mt/run_example.py index e75ce9ab..36327a51 100644 --- a/examples/wake_t_fbpic_mt/run_example.py +++ b/examples/wake_t_fbpic_mt/run_example.py @@ -16,7 +16,7 @@ from optimas.generators import AxMultitaskGenerator from optimas.evaluators import TemplateEvaluator, MultitaskEvaluator from optimas.explorations import Exploration -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from analysis_script import analyze_simulation diff --git a/optimas/diagnostics/exploration_diagnostics.py b/optimas/diagnostics/exploration_diagnostics.py index 14b5ec4b..13f0eb79 100644 --- a/optimas/diagnostics/exploration_diagnostics.py +++ b/optimas/diagnostics/exploration_diagnostics.py @@ -19,7 +19,7 @@ from optimas.explorations import Exploration from optimas.utils.other import get_df_with_selection from optimas.utils.ax import AxModelManager -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS class ExplorationDiagnostics: diff --git a/optimas/generators/ax/base.py b/optimas/generators/ax/base.py index 0575986a..e721b147 100644 --- a/optimas/generators/ax/base.py +++ b/optimas/generators/ax/base.py @@ -5,9 +5,9 @@ import torch -from optimas.core import Objective, TrialParameter, VaryingParameter, Parameter +from optimas.core import TrialParameter from optimas.generators.base import Generator -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS # Disable Ax loggers to get cleaner output. In principle, setting diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index 75a98efd..c9246bc5 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -59,15 +59,12 @@ from optimas.generators.ax.base import AxGenerator from optimas.core import ( TrialParameter, - VaryingParameter, - Objective, - Parameter, Task, Trial, TrialStatus, ) from .ax_metric import AxMetric -from generator_standard.vocs import VOCS, DiscreteVariable +from gest_api.vocs import VOCS, DiscreteVariable # Define generator states. NOT_STARTED = "not_started" diff --git a/optimas/generators/ax/service/ax_client.py b/optimas/generators/ax/service/ax_client.py index c392a96b..11d2cb0a 100644 --- a/optimas/generators/ax/service/ax_client.py +++ b/optimas/generators/ax/service/ax_client.py @@ -6,7 +6,7 @@ from ax.core.objective import MultiObjective from optimas.core import Objective, VaryingParameter, Parameter -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from .base import AxServiceGenerator diff --git a/optimas/generators/ax/service/base.py b/optimas/generators/ax/service/base.py index 858943f7..3b452632 100644 --- a/optimas/generators/ax/service/base.py +++ b/optimas/generators/ax/service/base.py @@ -19,9 +19,7 @@ from ax import Arm from optimas.core import ( - Objective, Trial, - VaryingParameter, Parameter, ) from optimas.generators.ax.base import AxGenerator @@ -30,7 +28,7 @@ convert_optimas_to_ax_parameters, convert_optimas_to_ax_objectives, ) -from generator_standard.vocs import ( +from gest_api.vocs import ( VOCS, LessThanConstraint, GreaterThanConstraint, diff --git a/optimas/generators/ax/service/multi_fidelity.py b/optimas/generators/ax/service/multi_fidelity.py index df8c9190..dbbefb01 100644 --- a/optimas/generators/ax/service/multi_fidelity.py +++ b/optimas/generators/ax/service/multi_fidelity.py @@ -9,9 +9,8 @@ from ax.modelbridge.generation_strategy import GenerationStep from ax.modelbridge.registry import Models -from optimas.core import Objective, VaryingParameter, Parameter from .base import AxServiceGenerator -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS class AxMultiFidelityGenerator(AxServiceGenerator): diff --git a/optimas/generators/ax/service/single_fidelity.py b/optimas/generators/ax/service/single_fidelity.py index af94a4e0..2728cf89 100644 --- a/optimas/generators/ax/service/single_fidelity.py +++ b/optimas/generators/ax/service/single_fidelity.py @@ -5,9 +5,8 @@ from ax.modelbridge.generation_strategy import GenerationStep from ax.modelbridge.registry import Models -from optimas.core import Objective, VaryingParameter, Parameter from .base import AxServiceGenerator -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS class AxSingleFidelityGenerator(AxServiceGenerator): diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 3f56ce0b..5a232055 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -20,8 +20,8 @@ TrialParameter, TrialStatus, ) -from generator_standard.vocs import VOCS, ContinuousVariable, DiscreteVariable -from generator_standard.generator import Generator as StandardGenerator +from gest_api.vocs import VOCS, ContinuousVariable, DiscreteVariable +from gest_api.generator import Generator as StandardGenerator logger = get_logger(__name__) diff --git a/optimas/generators/grid_sampling.py b/optimas/generators/grid_sampling.py index 2a0a1a0a..ddda75e7 100644 --- a/optimas/generators/grid_sampling.py +++ b/optimas/generators/grid_sampling.py @@ -4,8 +4,8 @@ import numpy as np -from optimas.core import Objective, Trial, VaryingParameter, Parameter -from generator_standard.vocs import VOCS +from optimas.core import Trial +from gest_api.vocs import VOCS from .base import Generator diff --git a/optimas/generators/line_sampling.py b/optimas/generators/line_sampling.py index c1afc8fa..a73ea997 100644 --- a/optimas/generators/line_sampling.py +++ b/optimas/generators/line_sampling.py @@ -4,8 +4,8 @@ import numpy as np -from optimas.core import Objective, Trial, VaryingParameter, Parameter -from generator_standard.vocs import VOCS +from optimas.core import Trial +from gest_api.vocs import VOCS from .base import Generator diff --git a/optimas/generators/random_sampling.py b/optimas/generators/random_sampling.py index 00aeb398..c038200b 100644 --- a/optimas/generators/random_sampling.py +++ b/optimas/generators/random_sampling.py @@ -4,8 +4,8 @@ import numpy as np -from optimas.core import Objective, Trial, VaryingParameter, Parameter -from generator_standard.vocs import VOCS +from optimas.core import Trial +from gest_api.vocs import VOCS from .base import Generator diff --git a/tests/test_analyzed_parameters.py b/tests/test_analyzed_parameters.py index c39fa2da..222781b2 100644 --- a/tests/test_analyzed_parameters.py +++ b/tests/test_analyzed_parameters.py @@ -1,10 +1,9 @@ import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective, Parameter def eval_func(input_params, output_params): diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 5c5b9cb8..727ee172 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -15,7 +15,7 @@ ) from optimas.evaluators import FunctionEvaluator, MultitaskEvaluator from optimas.core import Task -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS # Some tests will use threading (instead of multiprocessing) to be able to diff --git a/tests/test_ax_model_manager.py b/tests/test_ax_model_manager.py index 89e7b67c..f18800f9 100644 --- a/tests/test_ax_model_manager.py +++ b/tests/test_ax_model_manager.py @@ -8,7 +8,7 @@ from optimas.evaluators import FunctionEvaluator from optimas.diagnostics import ExplorationDiagnostics from optimas.utils.ax import AxModelManager -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS def eval_func_sf_moo(input_params, output_params): diff --git a/tests/test_chain_evaluator.py b/tests/test_chain_evaluator.py index 9a3d067e..790adea1 100644 --- a/tests/test_chain_evaluator.py +++ b/tests/test_chain_evaluator.py @@ -1,12 +1,11 @@ import os import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator, ChainEvaluator -from optimas.core import VaryingParameter, Objective, Parameter def analysis_func_1(sim_dir, output_params): diff --git a/tests/test_comms.py b/tests/test_comms.py index dcae8ac8..f39ae418 100644 --- a/tests/test_comms.py +++ b/tests/test_comms.py @@ -1,10 +1,9 @@ import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): diff --git a/tests/test_env_script.py b/tests/test_env_script.py index 74471e1d..4f1e4dfe 100644 --- a/tests/test_env_script.py +++ b/tests/test_env_script.py @@ -1,12 +1,11 @@ import os import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator -from optimas.core import VaryingParameter, Objective, Parameter def analysis_func(sim_dir, output_params): diff --git a/tests/test_exploration_diagnostics.py b/tests/test_exploration_diagnostics.py index c3f11878..59220b24 100644 --- a/tests/test_exploration_diagnostics.py +++ b/tests/test_exploration_diagnostics.py @@ -4,12 +4,11 @@ from matplotlib.gridspec import GridSpec import matplotlib.pyplot as plt import pytest -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator -from optimas.core import VaryingParameter, Objective from optimas.diagnostics import ExplorationDiagnostics diff --git a/tests/test_exploration_resume.py b/tests/test_exploration_resume.py index 7d42eec5..dfc3d11e 100644 --- a/tests/test_exploration_resume.py +++ b/tests/test_exploration_resume.py @@ -1,10 +1,9 @@ import os -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator -from optimas.core import VaryingParameter, Objective def analysis_func(sim_dir, output_params): diff --git a/tests/test_exploration_run_exception.py b/tests/test_exploration_run_exception.py index d6c197f8..20a3a4f3 100644 --- a/tests/test_exploration_run_exception.py +++ b/tests/test_exploration_run_exception.py @@ -1,6 +1,6 @@ import os -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator diff --git a/tests/test_function_evaluator.py b/tests/test_function_evaluator.py index 219016c2..3d331255 100644 --- a/tests/test_function_evaluator.py +++ b/tests/test_function_evaluator.py @@ -4,12 +4,11 @@ import numpy as np import matplotlib.pyplot as plt import pytest -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective, Parameter from optimas.diagnostics import ExplorationDiagnostics diff --git a/tests/test_gpu_resources.py b/tests/test_gpu_resources.py index 4dff5609..a3052925 100644 --- a/tests/test_gpu_resources.py +++ b/tests/test_gpu_resources.py @@ -4,7 +4,7 @@ set_start_method("spawn", force=True) import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.generators import AxSingleFidelityGenerator from optimas.evaluators import TemplateEvaluator from optimas.explorations import Exploration diff --git a/tests/test_grid_sampling.py b/tests/test_grid_sampling.py index d804fe3e..e454f346 100644 --- a/tests/test_grid_sampling.py +++ b/tests/test_grid_sampling.py @@ -1,10 +1,9 @@ import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import GridSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): diff --git a/tests/test_grid_sampling_mpi.py b/tests/test_grid_sampling_mpi.py index 12e31200..20f93eed 100644 --- a/tests/test_grid_sampling_mpi.py +++ b/tests/test_grid_sampling_mpi.py @@ -1,11 +1,10 @@ import numpy as np import pytest -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import GridSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): diff --git a/tests/test_line_sampling.py b/tests/test_line_sampling.py index b2ee3978..b8b3c917 100644 --- a/tests/test_line_sampling.py +++ b/tests/test_line_sampling.py @@ -2,12 +2,11 @@ import numpy as np import pytest -from generator_standard.vocs import VOCS, ContinuousVariable +from gest_api.vocs import VOCS, ContinuousVariable from optimas.explorations import Exploration from optimas.generators import LineSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): diff --git a/tests/test_manual_exploration.py b/tests/test_manual_exploration.py index 997e5121..adc1f845 100644 --- a/tests/test_manual_exploration.py +++ b/tests/test_manual_exploration.py @@ -1,11 +1,10 @@ import numpy as np import pandas as pd -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective, Parameter def eval_func(input_params, output_params): diff --git a/tests/test_random_sampling.py b/tests/test_random_sampling.py index b8b0d58f..ebf3e8a5 100644 --- a/tests/test_random_sampling.py +++ b/tests/test_random_sampling.py @@ -1,10 +1,9 @@ import numpy as np -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import FunctionEvaluator -from optimas.core import VaryingParameter, Objective def eval_func(input_params, output_params): diff --git a/tests/test_template_evaluator.py b/tests/test_template_evaluator.py index ce05cf43..9fc5301e 100644 --- a/tests/test_template_evaluator.py +++ b/tests/test_template_evaluator.py @@ -2,12 +2,11 @@ import numpy as np import matplotlib.pyplot as plt -from generator_standard.vocs import VOCS +from gest_api.vocs import VOCS from optimas.explorations import Exploration from optimas.generators import RandomSamplingGenerator from optimas.evaluators import TemplateEvaluator -from optimas.core import VaryingParameter, Objective, Parameter def analysis_func(sim_dir, output_params): From 1feb2d73b420d5cb089784ec88c6033b468f4ceb Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 1 Oct 2025 15:40:50 -0500 Subject: [PATCH 49/58] Fix vocs Objective comparison --- optimas/generators/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 5a232055..0c7df54c 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -166,7 +166,7 @@ def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: """Convert VOCS objectives to optimas Objective objects.""" objectives = [] for obj_name, obj_type in self._vocs.objectives.items(): - minimize = obj_type == "MINIMIZE" + minimize = isinstance(obj_type, MinimizeObjective) obj = Objective(name=obj_name, minimize=minimize) objectives.append(obj) return objectives From b9f82aa93a188a011cbf4d6165dd3133a38d14fe Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 1 Oct 2025 15:51:06 -0500 Subject: [PATCH 50/58] Add missing import --- optimas/generators/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 0c7df54c..7f438164 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -20,7 +20,12 @@ TrialParameter, TrialStatus, ) -from gest_api.vocs import VOCS, ContinuousVariable, DiscreteVariable +from gest_api.vocs import ( + VOCS, + ContinuousVariable, + DiscreteVariable, + MinimizeObjective, +) from gest_api.generator import Generator as StandardGenerator logger = get_logger(__name__) From 4ab4b48e76546659a5bb26b35418e893ce21b4bb Mon Sep 17 00:00:00 2001 From: shudson Date: Wed, 1 Oct 2025 16:15:47 -0500 Subject: [PATCH 51/58] Raise exception on ExploreObjective --- optimas/generators/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index 7f438164..ab13da65 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -25,6 +25,7 @@ ContinuousVariable, DiscreteVariable, MinimizeObjective, + ExploreObjective, ) from gest_api.generator import Generator as StandardGenerator @@ -171,6 +172,8 @@ def _convert_vocs_objectives_to_objectives(self) -> List[Objective]: """Convert VOCS objectives to optimas Objective objects.""" objectives = [] for obj_name, obj_type in self._vocs.objectives.items(): + if isinstance(obj_type, ExploreObjective): + raise ValueError("EXPLORE is not supported in Optimas") minimize = isinstance(obj_type, MinimizeObjective) obj = Objective(name=obj_name, minimize=minimize) objectives.append(obj) From fa74f3f4e62c9d44be356640b23941ad6786f600 Mon Sep 17 00:00:00 2001 From: shudson Date: Thu, 2 Oct 2025 14:54:34 -0500 Subject: [PATCH 52/58] Fix resolution setting --- examples/ionization_injection_mf/run_example.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/ionization_injection_mf/run_example.py b/examples/ionization_injection_mf/run_example.py index 789987bc..d5262b8d 100644 --- a/examples/ionization_injection_mf/run_example.py +++ b/examples/ionization_injection_mf/run_example.py @@ -32,8 +32,6 @@ }, objectives={"f": "MINIMIZE"}, observables=["energy_med", "energy_mad", "charge"], - fidelity_parameter="resolution", - fidelity_target_value=4.0, ) @@ -42,6 +40,7 @@ vocs=vocs, n_init=4, ) +gen.set_fidelity_param("resolution", fidelity_target_value=4.0) # Create evaluator. From ab0cb67748fc39652afb72460124459e1f762d2a Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Wed, 8 Oct 2025 09:59:36 -0700 Subject: [PATCH 53/58] Update Readthedocs environment --- doc/environment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/environment.yaml b/doc/environment.yaml index dac040ba..0815571d 100644 --- a/doc/environment.yaml +++ b/doc/environment.yaml @@ -11,6 +11,7 @@ dependencies: - matplotlib - nbsphinx - numpydoc + - git+https://github.com/campa-consortium/gest-api.git - pydata-sphinx-theme - sphinx-copybutton - sphinx-design From 365f1308a4bdcb0f1132bd76e04ef4d5f589e0ff Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 17 Oct 2025 14:24:10 -0500 Subject: [PATCH 54/58] Update hipace example for deprecated input spec --- examples/hipace/template_simulation_script | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/hipace/template_simulation_script b/examples/hipace/template_simulation_script index ee6cf6e5..4a3c2efd 100644 --- a/examples/hipace/template_simulation_script +++ b/examples/hipace/template_simulation_script @@ -11,7 +11,9 @@ hipace.depos_order_xy = 2 hipace.dt = adaptive hipace.nt_per_betatron = 30 -geometry.is_periodic = true true false # Is periodic? +boundary.field = Periodic +boundary.particle = Periodic + geometry.prob_lo = -250.e-6 -250.e-6 -250.e-6 # physical domain geometry.prob_hi = 250.e-6 250.e-6 110.e-6 From b9e81612b4118520aef5cdab06c5a773456803a2 Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 17 Oct 2025 15:24:36 -0500 Subject: [PATCH 55/58] Update exploration_diagnostics notebook for vocs --- .../basic_usage/exploration_diagnostics.ipynb | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/source/user_guide/basic_usage/exploration_diagnostics.ipynb b/doc/source/user_guide/basic_usage/exploration_diagnostics.ipynb index de162a2f..2f380dd6 100644 --- a/doc/source/user_guide/basic_usage/exploration_diagnostics.ipynb +++ b/doc/source/user_guide/basic_usage/exploration_diagnostics.ipynb @@ -125,16 +125,17 @@ "import matplotlib.pyplot as plt\n", "\n", "fig, ax = plt.subplots()\n", - "vps = diags.varying_parameters\n", + "vocs = diags._exploration.generator.vocs\n", "df = diags.history\n", - "f1 = diags.objectives[0]\n", - "ax.axvline(vps[0].lower_bound)\n", - "ax.axvline(vps[0].upper_bound)\n", - "ax.set_xlabel(vps[0].name)\n", - "ax.axhline(vps[1].lower_bound)\n", - "ax.axhline(vps[1].upper_bound)\n", - "ax.set_ylabel(vps[1].name)\n", - "ax.scatter(df[vps[0].name], df[vps[1].name], c=df[f1.name])" + "vps = list(vocs.variables.keys())\n", + "f1 = list(vocs.objectives.keys())[0]\n", + "ax.axvline(vocs.variables[vps[0]].domain[0])\n", + "ax.axvline(vocs.variables[vps[0]].domain[1])\n", + "ax.set_xlabel(vps[0])\n", + "ax.axhline(vocs.variables[vps[1]].domain[0])\n", + "ax.axhline(vocs.variables[vps[1]].domain[1])\n", + "ax.set_ylabel(vps[1])\n", + "ax.scatter(df[vps[0]], df[vps[1]], c=df[f1])" ] } ], @@ -144,5 +145,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } From 35117e767805dd00773dd714035e4a3e2e3a6c85 Mon Sep 17 00:00:00 2001 From: shudson Date: Fri, 17 Oct 2025 15:38:07 -0500 Subject: [PATCH 56/58] Update build_gp_surrogates notebook for vocs --- .../advanced_usage/build_gp_surrogates.ipynb | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/doc/source/user_guide/advanced_usage/build_gp_surrogates.ipynb b/doc/source/user_guide/advanced_usage/build_gp_surrogates.ipynb index edaa8f4c..b82f9971 100644 --- a/doc/source/user_guide/advanced_usage/build_gp_surrogates.ipynb +++ b/doc/source/user_guide/advanced_usage/build_gp_surrogates.ipynb @@ -11,7 +11,7 @@ "\n", "The :class:`~optimas.diagnostics.ExplorationDiagnostics` class\n", "provides a simple way of fitting a Gaussian process (GP) model to any of the\n", - "objectives or analyzed parameters of an ``optimas``\n", + "objectives or observables of an ``optimas``\n", ":class:`~optimas.explorations.Exploration`, independently of which generator\n", "was used. This is useful to get a better understanding of the underlying\n", "function, make predictions, etc.\n", @@ -29,7 +29,7 @@ "\n", "The following cell sets up and runs an optimization with two input parameters\n", "``x1`` and ``x2``, two objectives ``f1`` and ``f2``, and one additional\n", - "analyzed parameter ``p1``.\n", + "observable ``p1``.\n", "At each evaluation, the ``eval_func_sf_moo`` function is run,\n", "which assigns a value to each outcome parameter according to the analytical\n", "formulas\n", @@ -55,9 +55,9 @@ "source": [ "import numpy as np\n", "from optimas.explorations import Exploration\n", - "from optimas.core import VaryingParameter, Objective, Parameter\n", "from optimas.generators import RandomSamplingGenerator\n", "from optimas.evaluators import FunctionEvaluator\n", + "from gest_api.vocs import VOCS\n", "\n", "\n", "def eval_func_sf_moo(input_params, output_params):\n", @@ -70,17 +70,20 @@ " output_params[\"p1\"] = np.sin(x1) + np.cos(x2)\n", "\n", "\n", - "var1 = VaryingParameter(\"x1\", 0.0, 5.0)\n", - "var2 = VaryingParameter(\"x2\", -5.0, 5.0)\n", - "par1 = Parameter(\"p1\")\n", - "obj1 = Objective(\"f1\", minimize=True)\n", - "obj2 = Objective(\"f2\", minimize=False)\n", - "\n", - "gen = RandomSamplingGenerator(\n", - " varying_parameters=[var1, var2],\n", - " objectives=[obj1, obj2],\n", - " analyzed_parameters=[par1],\n", + "# Create VOCS object defining variables, objectives, and observables.\n", + "vocs = VOCS(\n", + " variables={\n", + " \"x1\": [0.0, 5.0],\n", + " \"x2\": [-5.0, 5.0],\n", + " },\n", + " objectives={\n", + " \"f1\": \"MINIMIZE\",\n", + " \"f2\": \"MAXIMIZE\",\n", + " },\n", + " observables=[\"p1\"],\n", ")\n", + "\n", + "gen = RandomSamplingGenerator(vocs=vocs)\n", "ev = FunctionEvaluator(function=eval_func_sf_moo)\n", "exploration = Exploration(\n", " generator=gen,\n", @@ -125,18 +128,18 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "Building a GP model of each objective and analyzed parameter\n", - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", + "Building a GP model of each objective and observable\n", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", "\n", "To build a GP model, simply call\n", ":meth:`~optimas.diagnostics.Exploration.build_gp_model` on the diagnostics,\n", "indicating the name of the variable to which the model should be fitted.\n", - "This variable can be any ``objective`` or ``analyzed_parameter`` of the\n", + "This variable can be any ``objective`` or ``observable`` of the\n", "optimization.\n", "\n", - "Note that when building a surrogate model of an analyzed parameter, it is\n", + "Note that when building a surrogate model of an observable, it is\n", "required to provide a value to the ``minimize`` argument. This parameter\n", - "should therefore be ``True`` if lower values of the analyzed parameter are\n", + "should therefore be ``True`` if lower values of the observable are\n", "better than higher values. This information is necessary, e.g., for determining\n", "the best point in the model." ] @@ -147,7 +150,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Build one model for each objective and analyzed parameter.\n", + "# Build one model for each objective and observable.\n", "f1_model = diags.build_gp_model(\"f1\")\n", "f2_model = diags.build_gp_model(\"f2\")\n", "p1_model = diags.build_gp_model(\"p1\", minimize=False)" From 6a1532868a3f4f63dcaca1c4ed743fb839c6242f Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 20 Oct 2025 10:50:39 -0500 Subject: [PATCH 57/58] Merge CI fix --- .github/workflows/unix-openmpi.yml | 3 ++- .github/workflows/unix.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unix-openmpi.yml b/.github/workflows/unix-openmpi.yml index c74be8b5..e807c210 100644 --- a/.github/workflows/unix-openmpi.yml +++ b/.github/workflows/unix-openmpi.yml @@ -28,7 +28,8 @@ jobs: - shell: bash -l {0} name: Install dependencies run: | - conda install numpy pandas pytorch cpuonly -c pytorch + conda install -c conda-forge pytorch-cpu + conda install -c pytorch numpy pandas conda install -c conda-forge mpi4py openmpi=5.* pip install .[test] pip install git+https://github.com/campa-consortium/gest-api.git diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 92b0112f..c464ea9f 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -28,7 +28,8 @@ jobs: - shell: bash -l {0} name: Install dependencies run: | - conda install numpy pandas pytorch cpuonly -c pytorch + conda install -c conda-forge pytorch-cpu + conda install -c pytorch numpy pandas conda install -c conda-forge mpi4py mpich pip install .[test] pip install git+https://github.com/campa-consortium/gest-api.git From c54e53628efcbadf0d49fd6851cc1285d6500b39 Mon Sep 17 00:00:00 2001 From: shudson Date: Mon, 20 Oct 2025 14:56:11 -0500 Subject: [PATCH 58/58] Clean up comments --- optimas/generators/ax/developer/multitask.py | 5 ++--- optimas/generators/base.py | 1 - tests/test_ax_generators.py | 2 +- tests/test_exploration_diagnostics.py | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/optimas/generators/ax/developer/multitask.py b/optimas/generators/ax/developer/multitask.py index c9246bc5..32cb49d6 100644 --- a/optimas/generators/ax/developer/multitask.py +++ b/optimas/generators/ax/developer/multitask.py @@ -279,8 +279,8 @@ def suggest(self, num_points: Optional[int]) -> List[dict]: var.name: arm.parameters.get(var.name) for var in self._varying_parameters } - # SH We can use a discrete var here in vocs (converted for now to trial parameters) - # But unlike varying parameters the name refers to a fixed generator concept. + # trial_type is declared as a discrete variable in vocs + # and converted internally to a trial parameter. for trial_param in self._custom_trial_parameters: if trial_param.name == "trial_type": point[trial_param.name] = trial_type @@ -312,7 +312,6 @@ def ingest(self, results: List[dict]) -> None: def _incorporate_external_data(self, trials: List[Trial]) -> None: """Incorporate external data (e.g., from history) into experiment.""" # Get trial indices. - # SH should have handling if ax_trial_ids are None... trial_indices = [] for trial in trials: trial_indices.append(trial.ax_trial_id) diff --git a/optimas/generators/base.py b/optimas/generators/base.py index ab13da65..14fe1524 100644 --- a/optimas/generators/base.py +++ b/optimas/generators/base.py @@ -148,7 +148,6 @@ def _convert_vocs_variables_to_varying_parameters( ) varying_parameters.append(vp) # Handle DiscreteVariable that is a range of integers - # TODO: Suggest supporting IntegerVariables in vocs elif isinstance(var_spec, DiscreteVariable): values = list(var_spec.values) if len(values) > 1: diff --git a/tests/test_ax_generators.py b/tests/test_ax_generators.py index 727ee172..57d566b5 100644 --- a/tests/test_ax_generators.py +++ b/tests/test_ax_generators.py @@ -275,7 +275,7 @@ def test_ax_single_fidelity_int(): trial_count = 0 trials_to_fail = [2, 6] - # TODO: Suggest supporting IntegerVariables in vocs + # Integer variables are not supported in vocs (use discrete variable) vocs = VOCS( variables={"x0": set(range(-50, 6)), "x1": [-5.0, 15.0]}, objectives={"f": "MAXIMIZE"}, diff --git a/tests/test_exploration_diagnostics.py b/tests/test_exploration_diagnostics.py index 59220b24..c02b5339 100644 --- a/tests/test_exploration_diagnostics.py +++ b/tests/test_exploration_diagnostics.py @@ -138,9 +138,7 @@ def test_exploration_diagnostics(): # Check that all possible objective inputs give the same result. _, trace1 = diags.get_objective_trace() _, trace2 = diags.get_objective_trace("f1") - # _, trace3 = diags.get_objective_trace(obj) # Can be removed np.testing.assert_array_equal(trace1, trace2) - # np.testing.assert_array_equal(trace1, trace3) # Test making plot using the diagnostics API. fig, ax = plt.subplots()