Skip to content
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fa96b4c
Implement VOCS for simple generators
shuds13 Jul 22, 2025
31ba116
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 22, 2025
7cd13c9
Convert further tests to vocs
shuds13 Jul 23, 2025
b03f0f1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2025
f973cfa
Make exploration_diagnostics work with vocs
shuds13 Jul 23, 2025
e44319c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2025
0f8049d
Base generator inherits from standard
shuds13 Jul 23, 2025
a73735c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2025
942a692
For line_sampling set default value on setup
shuds13 Jul 23, 2025
56757a8
Merge branch 'vocs' of github.com:optimas-org/optimas into vocs
shuds13 Jul 23, 2025
6a51d00
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 23, 2025
3293fde
Add validate_vocs to line sampler
shuds13 Jul 23, 2025
684bf31
Actually add validate_vocs to line sampler
shuds13 Jul 23, 2025
0b690ff
Use observable types and update env test
shuds13 Jul 24, 2025
89323d0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 24, 2025
f313089
Allow observable to be any type
shuds13 Jul 24, 2025
cca80cc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 24, 2025
4b8d698
Update exception test to vocs
shuds13 Jul 24, 2025
c7660e6
Update Ax base and service generators to vocs
shuds13 Jul 25, 2025
fa43bc0
Update gpu resources test
shuds13 Jul 25, 2025
8e21bb5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 25, 2025
deaeb6b
Handle constraints in service generators
shuds13 Jul 25, 2025
68e2e13
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 25, 2025
d4bb5dc
First pass at converting test_ax_generators to VOCS
shuds13 Jul 25, 2025
cebffd1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 25, 2025
36f6277
Make fix_value work
shuds13 Jul 28, 2025
df8066b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 28, 2025
9d9eaf7
Add update_range to work with vocs
shuds13 Jul 28, 2025
bf31174
Merge commit
shuds13 Jul 28, 2025
4948cb5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 28, 2025
cf1211d
Set fidelity parameter via generator
shuds13 Jul 28, 2025
9b60771
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 28, 2025
ce09d6b
Update multitask for vocs and apply id mapping
shuds13 Jul 28, 2025
07eee24
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 28, 2025
9df7f76
Specify multitask trial_type as discrete variable
shuds13 Jul 28, 2025
bbc23bc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 28, 2025
6f0933f
Create vocs in ax_client
shuds13 Jul 29, 2025
3a6ef44
Update model manager test
shuds13 Jul 29, 2025
f883c7b
Revert multitask to preserve ax id/arm info.
shuds13 Jul 30, 2025
346dbee
Convert Discrete integer range to VaryingParameter
shuds13 Jul 30, 2025
844ff1a
No need to pass `is_fidelity`
RemiLehe Aug 18, 2025
d019af5
Update dummy examples to use vocs
shuds13 Aug 19, 2025
21210b9
Update line
shuds13 Aug 19, 2025
b760144
Remove duplicate line
shuds13 Aug 19, 2025
ee2cb13
Update ionization examples to use vocs
shuds13 Aug 19, 2025
b91a7db
Update remaining examples to use vocs
shuds13 Aug 20, 2025
ff92723
Handle discrete variables in gen base class
shuds13 Aug 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/unix-noax.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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@obs_type
pip uninstall --yes ax-platform # Run without Ax
- shell: bash -l {0}
name: Run unit tests without Ax
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/unix-openmpi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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@obs_type
- shell: bash -l {0}
name: Run unit tests with openMPI
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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@obs_type
- shell: bash -l {0}
name: Run unit tests with MPICH
run: |
Expand Down
26 changes: 21 additions & 5 deletions optimas/diagnostics/exploration_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -102,12 +103,27 @@ 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,
Expand Down
20 changes: 6 additions & 14 deletions optimas/generators/ax/base.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Contains the definition of the base Ax generator."""

from typing import List, Optional
from typing import Optional
import logging

import torch

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
Expand All @@ -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``.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
78 changes: 55 additions & 23 deletions optimas/generators/ax/developer/multitask.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
TrialStatus,
)
from .ax_metric import AxMetric
from generator_standard.vocs import VOCS, DiscreteVariable

# Define generator states.
NOT_STARTED = "not_started"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -184,31 +183,35 @@ 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,
save_model: Optional[bool] = True,
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("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)

# Convert discrete variables to trial parameters before calling super().__init__
custom_trial_parameters.extend(
self._convert_discrete_variables_to_trial_parameters(vocs)
)

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,
Expand All @@ -231,6 +234,21 @@ 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:
Expand All @@ -242,20 +260,30 @@ 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,
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)
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, (
Expand All @@ -274,11 +302,14 @@ 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.
# 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

point["ax_trial_id"] = trial_index
point["arm_name"] = arm.name
point["trial_type"] = trial_type
points.append(point)
return points

Expand All @@ -295,6 +326,7 @@ def ingest(self, results: List[dict]) -> None:
custom_parameters=self._custom_trial_parameters,
)
trials.append(trial)

if self.gen_state == NOT_STARTED:
self._incorporate_external_data(trials)
else:
Expand Down
46 changes: 40 additions & 6 deletions optimas/generators/ax/service/ax_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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,
Expand All @@ -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 = []
Expand Down
Loading
Loading