Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use only dots to delimit controls #9942

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/everest/bin/config_branch_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def _updated_initial_guess(conf_controls, opt_controls):
conf_controls = copy(conf_controls)

for control in conf_controls:
control_name = f"{control['name']}_"
control_name = f"{control['name']}."
control.pop("initial_guess", None)
batch_controls = {
key.split(control_name)[-1]: val
Expand All @@ -78,7 +78,7 @@ def _updated_initial_guess(conf_controls, opt_controls):
var_index = variable.get("index", None)

if var_index is not None:
opt_control_name = f"{variable['name']}-{var_index}"
opt_control_name = f"{variable['name']}.{var_index}"
else:
opt_control_name = variable["name"]

Expand Down
50 changes: 33 additions & 17 deletions src/everest/config/everest_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from ..config_file_loader import yaml_file_to_substituted_config_dict
from ..strings import (
DEFAULT_OUTPUT_DIR,
EVEREST,
OPTIMIZATION_LOG_DIR,
OPTIMIZATION_OUTPUT_DIR,
STORAGE_DIR,
Expand Down Expand Up @@ -419,27 +420,26 @@ def validate_input_constraints_weight_definition(self) -> Self:
controls = self.controls
if controls is None:
return self
control_full_name = []
control_names = self.formatted_control_names
control_names_deprecated = self.formatted_control_names_dotdash
errors = []
for c in controls:
for v in c.variables:
if isinstance(v, ControlVariableGuessListConfig):
control_full_name.extend(
f"{c.name}.{v.name}-{index}"
for index, _ in enumerate(v.initial_guess, start=1)
)
elif v.index is not None:
control_full_name.append(f"{c.name}.{v.name}-{v.index}")
else:
control_full_name.append(f"{c.name}.{v.name}")

for input_const in input_constraints:
for key in input_const.weights:
if key not in control_full_name:
if key in control_names_deprecated and key not in control_names:
logging.getLogger(EVEREST).warning(
f"Deprecated reference to control: {key} in input constraint."
)
print(
f"Deprecated input control name: {key} "
f"reference in input constraint. This format is deprecated, "
f"please use only '.' as delimiters: {key.replace('-', '.')}"
)
elif key not in control_names and key not in control_names_deprecated:
errors.append(
f"Input control weight name {key} "
f"does not match any instance of "
f"control_name.variable_name-variable_index"
f"control_name.variable_name.variable_index"
)

if len(errors) > 0: # Note: python3.11 ExceptionGroup will solve this nicely
Expand Down Expand Up @@ -628,11 +628,27 @@ def formatted_control_names(self) -> list[str]:
for variable in control.variables:
if isinstance(variable, ControlVariableGuessListConfig):
for index in range(1, len(variable.initial_guess) + 1):
names.append(f"{control.name}_{variable.name}-{index}")
names.append(f"{control.name}.{variable.name}.{index}")
elif variable.index is not None:
names.append(f"{control.name}.{variable.name}.{variable.index}")
else:
names.append(f"{control.name}.{variable.name}")
return names

@property
def formatted_control_names_dotdash(self) -> list[str]:
# Note: Should be removed as the .- way of referencing controls
# from input constraints is removed
names = []
for control in self.controls:
for variable in control.variables:
if isinstance(variable, ControlVariableGuessListConfig):
for index in range(1, len(variable.initial_guess) + 1):
names.append(f"{control.name}.{variable.name}-{index}")
elif variable.index is not None:
names.append(f"{control.name}_{variable.name}-{variable.index}")
names.append(f"{control.name}.{variable.name}-{variable.index}")
else:
names.append(f"{control.name}_{variable.name}")
names.append(f"{control.name}.{variable.name}")
return names

@property
Expand Down
24 changes: 12 additions & 12 deletions src/everest/config/input_constraint_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"): # type: ignore
If we are trying to constrain only one control (i.e the z control) value:
| input_constraints:
| - weights:
| point_3D.x-0: 0
| point_3D.y-1: 0
| point_3D.z-2: 1
| point_3D.x.0: 0
| point_3D.y.1: 0
| point_3D.z.2: 1
| upper_bound: 0.2

Only control values (x, y, z) that satisfy the following equation will be allowed:
Expand All @@ -21,9 +21,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"): # type: ignore
description="""**Example**
| input_constraints:
| - weights:
| point_3D.x-0: 1
| point_3D.y-1: 2
| point_3D.z-2: 3
| point_3D.x.0: 1
| point_3D.y.1: 2
| point_3D.z.2: 3
| target: 4

Only control values (x, y, z) that satisfy the following equation will be allowed:
Expand All @@ -35,9 +35,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"): # type: ignore
description="""**Example**
| input_constraints:
| - weights:
| point_3D.x-0: 1
| point_3D.y-1: 2
| point_3D.z-2: 3
| point_3D.x.0: 1
| point_3D.y.1: 2
| point_3D.z.2: 3
| lower_bound: 4

Only control values (x, y, z) that satisfy the following
Expand All @@ -50,9 +50,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"): # type: ignore
description="""**Example**
| input_constraints:
| - weights:
| point_3D.x-0: 1
| point_3D.y-1: 2
| point_3D.z-2: 3
| point_3D.x.0: 1
| point_3D.y.1: 2
| point_3D.z.2: 3
| upper_bound: 4

Only control values (x, y, z) that satisfy the following equation will be allowed:
Expand Down
35 changes: 21 additions & 14 deletions src/everest/optimizer/everest2ropt.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,23 @@ def _parse_objectives(objective_functions: list[ObjectiveFunctionConfig], ropt_c
def _parse_input_constraints(
controls: FlattenedControls,
input_constraints: list[InputConstraintConfig] | None,
formatted_control_names: list[str],
formatted_control_names_dotdash: list[str],
ropt_config,
):
if not input_constraints:
return

# TODO: Issue #9816 is intended to address the need for a more general
# naming scheme. This code should be revisited once that issue is resolved.
# Ideally the formatted_control_names property of the config is used.
formatted_names = [
(
f"{control_name[0]}.{control_name[1]}-{control_name[2]}"
if len(control_name) > 2
else f"{control_name[0]}.{control_name[1]}"
)
for control_name in controls.names
]
def _get_control_index(name: str):
try:
matching_index = formatted_control_names.index(name.replace("-", "."))
return matching_index
except ValueError:
pass

# Dash is deprecated, should eventually be removed
# along with formatted_control_names_dotdash
return formatted_control_names_dotdash.index(name)

coefficients_matrix = []
rhs_values = []
Expand All @@ -135,9 +136,11 @@ def _add_input_constraint(rhs_value, coefficients, constraint_type):
types.append(constraint_type)

for constr in input_constraints:
coefficients = [0.0] * len(formatted_names)
coefficients = [0.0] * len(formatted_control_names)
for name, value in constr.weights.items():
coefficients[formatted_names.index(name)] = value
index = _get_control_index(name)
coefficients[index] = value

target = constr.target
upper_bound = constr.upper_bound
lower_bound = constr.lower_bound
Expand Down Expand Up @@ -358,7 +361,11 @@ def everest2ropt(
_parse_controls(flattened_controls, ropt_config)
_parse_objectives(ever_config.objective_functions, ropt_config)
_parse_input_constraints(
flattened_controls, ever_config.input_constraints, ropt_config
flattened_controls,
ever_config.input_constraints,
ever_config.formatted_control_names,
ever_config.formatted_control_names_dotdash,
ropt_config,
)
_parse_output_constraints(ever_config.output_constraints, ropt_config)
_parse_optimization(
Expand Down
6 changes: 3 additions & 3 deletions test-data/everest/math_func/config_advanced.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ objective_functions:

input_constraints:
- weights:
point.x-0: 0
point.x-1: 0
point.x-2: 1
point.x.0: 0
point.x.1: 0
point.x.2: 1
upper_bound: 0.4

output_constraints:
Expand Down
6 changes: 3 additions & 3 deletions tests/everest/functional/test_main_everest_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def test_everest_entry_run(cached_example):
storage.read_from_output_dir()
optimal = storage.get_optimal_result()

assert optimal.controls["point_x"] == pytest.approx(0.5, abs=0.05)
assert optimal.controls["point_y"] == pytest.approx(0.5, abs=0.05)
assert optimal.controls["point_z"] == pytest.approx(0.5, abs=0.05)
assert optimal.controls["point.x"] == pytest.approx(0.5, abs=0.05)
assert optimal.controls["point.y"] == pytest.approx(0.5, abs=0.05)
assert optimal.controls["point.z"] == pytest.approx(0.5, abs=0.05)

assert optimal.total_objective == pytest.approx(0.0, abs=0.0005)

Expand Down
Loading