Skip to content

Commit

Permalink
Use only dots to delimit controls
Browse files Browse the repository at this point in the history
  • Loading branch information
yngve-sk committed Feb 4, 2025
1 parent 0dc2a57 commit b084afc
Show file tree
Hide file tree
Showing 27 changed files with 288 additions and 184 deletions.
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
33 changes: 19 additions & 14 deletions src/everest/optimizer/everest2ropt.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,21 @@ 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

return formatted_control_names_dotdash.index(name)

coefficients_matrix = []
rhs_values = []
Expand All @@ -135,9 +134,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 +359,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

0 comments on commit b084afc

Please sign in to comment.