From 7eba3138e25053285d9eea10c188dffb22869a50 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Wed, 3 Dec 2025 11:54:45 +0100 Subject: [PATCH 01/25] adding merge script (untested) --- utils/merge_yaml.py | 100 +++++++++++++++++++++++++++++++++++++++++ utils/validate_yaml.py | 29 +++++++----- 2 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 utils/merge_yaml.py diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py new file mode 100644 index 0000000..6a7dcce --- /dev/null +++ b/utils/merge_yaml.py @@ -0,0 +1,100 @@ +import yaml + +import sys +from pathlib import Path + +# Add parent directory to sys.path +parent = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(parent)) + +from .validate_yaml import read_data, validate_data, PROBLEMS_FILE + +TEMPLATE_FILE = "template.yaml" + + +def validity_check(old_data, new_data): + # Check there are not duplicate keys + duplicates = len(set(old_data[0].keys()) & set(new_data[0].keys())) > 0 + if duplicates: + print( + "::error::Duplicate problem names found between existing and new problems." + ) + return False + + # Validate new data + is_valid = validate_data(new_data) + if not is_valid: + print("::error::New problems YAML validation failed") + return False + + return True + + +def write_data(filepath, data): + try: + with open(filepath, "w") as f: + yaml.safe_dump(data, f) + print(f"::notice::Wrote data to {filepath}.") + except FileNotFoundError: + print(f"::error::File not found: {filepath}") + return False + except yaml.YAMLError as e: + print(f"::error::YAML syntax error: {e}") + return False + return True + + +def update_existing_data(existing_data, new_data): + existing_data.update(new_data) + + write_success = write_data(PROBLEMS_FILE, existing_data) + return write_success + + +def reset_to_template(file_path): + # Reset the content of the file to a template + template_data_status, template_data = read_data(TEMPLATE_FILE) + if template_data_status != 0: + return template_data_status + write_success = write_data(file_path, template_data) + return write_success + + +def merge_new_problems(new_problems_yaml_path: str): + existing_data_status, existing_data = read_data(PROBLEMS_FILE) + if not existing_data_status: + return False + new_data_status, new_data = read_data(new_problems_yaml_path) + if not new_data_status: + return False + # Validate data + is_valid = validity_check(existing_data, new_data) + if not is_valid: + return False + + # All valid, we can now just merge the dicts + assert existing_data is not None + assert new_data is not None + updated = update_existing_data(existing_data, new_data) + if not updated: + return False + + # Reset the template content + reset_status = reset_to_template(new_problems_yaml_path) + if not reset_status: + return False + + print(f"::notice::Merged {len(new_data)} new problems into {PROBLEMS_FILE}.") + return True + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python merge_yaml.py ") + sys.exit(1) + new_problems_yaml_path = sys.argv[1] + status = merge_new_problems(new_problems_yaml_path) + if not status: + sys.exit(1) + else: + sys.exit(0) diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index ad2c158..b181d07 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -93,29 +93,36 @@ def check_novelty(data): return True -def validate_yaml(filepath): - status, data = read_data(filepath) - if status != 0: - sys.exit(1) +def validate_data(data) -> bool: if not check_format(data): - sys.exit(1) - assert data is not None + return False for i, new_data in enumerate(data): # Iterate through each top-level entry # Check required and unique fields if not check_fields(new_data) or not check_novelty(new_data): print(f"::error::Validation failed for entry {i+1}.") - sys.exit(1) + return False # YAML is valid if we reach this point - print("YAML syntax is valid.") - sys.exit(0) + print("::notice::YAML syntax is valid.") + return True + + +def validate_yaml(filepath: str) -> bool: + status, data = read_data(filepath) + if status != 0: + return False + return validate_data(data) if __name__ == "__main__": - if len(sys.argv) < 2: + if len(sys.argv) != 2: print("::error::Usage: python validate_yaml.py ") sys.exit(1) filepath = sys.argv[1] - validate_yaml(filepath) + valid = validate_yaml(filepath) + if valid: + sys.exit(0) + else: + sys.exit(1) From 3535ab500a7863081cb8e35bd7933bed6197939a Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Wed, 8 Apr 2026 11:12:18 +0200 Subject: [PATCH 02/25] making tests not fail --- .github/workflows/new_problem_check.yml | 3 +++ utils/README.md | 19 +++++++++++++++++++ utils/new_problem.yaml | 14 -------------- 3 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 utils/new_problem.yaml diff --git a/.github/workflows/new_problem_check.yml b/.github/workflows/new_problem_check.yml index 5a13ee6..5f2917c 100644 --- a/.github/workflows/new_problem_check.yml +++ b/.github/workflows/new_problem_check.yml @@ -2,6 +2,8 @@ name: New Problem Check on: push: + branches: + - main paths: - "utils/new_problem.yaml" pull_request: @@ -32,6 +34,7 @@ jobs: pip install -r utils/requirements.txt - name: Run New Problem Check + if: ${{ hashFiles('utils/new_problem.yaml') != '' }} run: | python utils/validate_yaml.py utils/new_problem.yaml diff --git a/utils/README.md b/utils/README.md index 3d3fbee..dd71de7 100644 --- a/utils/README.md +++ b/utils/README.md @@ -29,3 +29,22 @@ This script checks the new content for the following: pip install -r utils/requirements.txt python utils/validate_yaml.py utils/new_problem.yaml ``` + +## new problem example + +```json +- name: template + suite/generator/single: suite + objectives: '1' + dimensionality: scalable + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'no' + multimodal: 'yes' + multi-fidelity: 'no' + reference: '' + implementation: '' + source (real-world/artificial): '' + textual description: 'This is a dummy template' +`` diff --git a/utils/new_problem.yaml b/utils/new_problem.yaml deleted file mode 100644 index 2c300e1..0000000 --- a/utils/new_problem.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- name: template - suite/generator/single: suite - objectives: '2' - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: '' - implementation: '' - source (real-world/artificial): '' - textual description: 'This is a dummy template' From 4376e178bbea7430f35af8a21a7ec0b2298a2aa2 Mon Sep 17 00:00:00 2001 From: Vanessa <3634850+CIGbalance@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:38:56 +0200 Subject: [PATCH 03/25] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- utils/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/README.md b/utils/README.md index dd71de7..a7165a0 100644 --- a/utils/README.md +++ b/utils/README.md @@ -33,7 +33,7 @@ python utils/validate_yaml.py utils/new_problem.yaml ## new problem example ```json -- name: template +- name: example-problem-name suite/generator/single: suite objectives: '1' dimensionality: scalable @@ -47,4 +47,3 @@ python utils/validate_yaml.py utils/new_problem.yaml implementation: '' source (real-world/artificial): '' textual description: 'This is a dummy template' -`` From c39e790f5c5cd55696de1cdcae4457e06f658923 Mon Sep 17 00:00:00 2001 From: Vanessa <3634850+CIGbalance@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:39:47 +0200 Subject: [PATCH 04/25] Change example problem format to YAML Updated example problem format from JSON to YAML. --- utils/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/README.md b/utils/README.md index a7165a0..064f5ab 100644 --- a/utils/README.md +++ b/utils/README.md @@ -32,7 +32,7 @@ python utils/validate_yaml.py utils/new_problem.yaml ## new problem example -```json +```yaml - name: example-problem-name suite/generator/single: suite objectives: '1' From 014b6e2aa2362b9625f6db7c736217e9b0dda199 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Wed, 8 Apr 2026 11:45:07 +0200 Subject: [PATCH 05/25] requested changes --- utils/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/README.md b/utils/README.md index 064f5ab..2ad9e5e 100644 --- a/utils/README.md +++ b/utils/README.md @@ -4,7 +4,7 @@ This folder contains utility scripts for working with the YAML format to describ The intended way of adding a new problem to the repository is thus as follows: -* Change the [new_problem.yaml](new_problem.yaml) template file to fit the new problem. +* Create a file in 'utils/new_problem.yaml' based on the template (see below). * Create a PR with the changes (for example with a fork). What happens in the background then is: @@ -13,7 +13,7 @@ What happens in the background then is: * Then the PR should be reviewed manually. * When the PR is merged into the main branch, a second script runs (which doesn't exist yet), that adds the content of [new_problem.yaml](new_problem.yaml) to the [problems.yaml](../problems.yaml) file, and reverts the changes to the new_problem.yaml. -:alert: Note that the GitHubActions do not exist yet either, this is a WIP. +:warning: Note that the GitHubActions do not exist yet either, this is a WIP. ## validate_yaml.py @@ -23,7 +23,7 @@ This script checks the new content for the following: * The required fields are present. * Specific fields are unique across the new set of problems (e.g. name) -:alert: Execute from root of the repository. Tested with python 3.12 +:warning: Execute from root of the repository. Tested with python 3.12 ```bash pip install -r utils/requirements.txt From 35f90fcf9d72b211890f4e47796cae9f14b60a19 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 12:32:52 +0200 Subject: [PATCH 06/25] follow new workflow --- utils/merge_yaml.py | 54 ++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index 6a7dcce..11fecf3 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -1,5 +1,5 @@ import yaml - +import os import sys from pathlib import Path @@ -9,26 +9,6 @@ from .validate_yaml import read_data, validate_data, PROBLEMS_FILE -TEMPLATE_FILE = "template.yaml" - - -def validity_check(old_data, new_data): - # Check there are not duplicate keys - duplicates = len(set(old_data[0].keys()) & set(new_data[0].keys())) > 0 - if duplicates: - print( - "::error::Duplicate problem names found between existing and new problems." - ) - return False - - # Validate new data - is_valid = validate_data(new_data) - if not is_valid: - print("::error::New problems YAML validation failed") - return False - - return True - def write_data(filepath, data): try: @@ -51,25 +31,29 @@ def update_existing_data(existing_data, new_data): return write_success -def reset_to_template(file_path): - # Reset the content of the file to a template - template_data_status, template_data = read_data(TEMPLATE_FILE) - if template_data_status != 0: - return template_data_status - write_success = write_data(file_path, template_data) - return write_success +def delete_new_file(file_path): + # Delete the new file after merging + try: + os.remove(file_path) + print(f"::notice::Deleted new problem file {file_path}.") + except OSError as e: + print(f"::error::Error deleting file {file_path}: {e}") + return False + return True def merge_new_problems(new_problems_yaml_path: str): - existing_data_status, existing_data = read_data(PROBLEMS_FILE) - if not existing_data_status: - return False + # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) if not new_data_status: return False - # Validate data - is_valid = validity_check(existing_data, new_data) - if not is_valid: + valid = validate_data(new_data) + if not valid: + return False + + # Read existing data + existing_data_status, existing_data = read_data(PROBLEMS_FILE) + if not existing_data_status: return False # All valid, we can now just merge the dicts @@ -80,7 +64,7 @@ def merge_new_problems(new_problems_yaml_path: str): return False # Reset the template content - reset_status = reset_to_template(new_problems_yaml_path) + reset_status = delete_new_file(new_problems_yaml_path) if not reset_status: return False From 88092bc11dfc2c4bdd20f76219c846b46ce98cf8 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:29:29 +0200 Subject: [PATCH 07/25] tested script now --- docs/index.html | 8 ++++---- docs/problems.html | 8 ++++---- utils/merge_yaml.py | 20 ++++++++++++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/index.html b/docs/index.html index 5819558..77f00b8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -911,7 +911,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -926,7 +926,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -941,7 +941,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + PorkchopPlotInterplanetaryTrajectory @@ -1050,7 +1050,7 @@

OPL – Optimisation problem library

TulipaEnergy - Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/docs/problems.html b/docs/problems.html index 4601b97..ad6bbce 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -885,7 +885,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -900,7 +900,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -915,7 +915,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + PorkchopPlotInterplanetaryTrajectory @@ -1024,7 +1024,7 @@ TulipaEnergy - Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index 11fecf3..e8223ae 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -7,13 +7,13 @@ parent = Path(__file__).resolve().parent.parent sys.path.insert(0, str(parent)) -from .validate_yaml import read_data, validate_data, PROBLEMS_FILE +from utils.validate_yaml import read_data, validate_data, PROBLEMS_FILE def write_data(filepath, data): try: with open(filepath, "w") as f: - yaml.safe_dump(data, f) + yaml.safe_dump(data, f, sort_keys=False) print(f"::notice::Wrote data to {filepath}.") except FileNotFoundError: print(f"::error::File not found: {filepath}") @@ -25,8 +25,7 @@ def write_data(filepath, data): def update_existing_data(existing_data, new_data): - existing_data.update(new_data) - + existing_data.extend(new_data) write_success = write_data(PROBLEMS_FILE, existing_data) return write_success @@ -45,15 +44,22 @@ def delete_new_file(file_path): def merge_new_problems(new_problems_yaml_path: str): # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) - if not new_data_status: + if new_data_status != 0: + print( + f"::error::New problems data could not be read from {new_problems_yaml_path}." + ) return False valid = validate_data(new_data) if not valid: + print(f"::error::New problems data in {new_problems_yaml_path} is not valid.") return False # Read existing data existing_data_status, existing_data = read_data(PROBLEMS_FILE) - if not existing_data_status: + if existing_data_status != 0: + print( + f"::error::Existing problems data could not be read from {PROBLEMS_FILE}." + ) return False # All valid, we can now just merge the dicts @@ -61,11 +67,13 @@ def merge_new_problems(new_problems_yaml_path: str): assert new_data is not None updated = update_existing_data(existing_data, new_data) if not updated: + print(f"::error::Failed to update existing problems data in {PROBLEMS_FILE}.") return False # Reset the template content reset_status = delete_new_file(new_problems_yaml_path) if not reset_status: + print(f"::error::Failed to delete new problem file {new_problems_yaml_path}.") return False print(f"::notice::Merged {len(new_data)} new problems into {PROBLEMS_FILE}.") From aab7baee0518128dba3b555c2b66928fc2e4b6d3 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:35:04 +0200 Subject: [PATCH 08/25] remove from PR --- docs/index.html | 8 ++++---- docs/problems.html | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/index.html b/docs/index.html index 77f00b8..5819558 100644 --- a/docs/index.html +++ b/docs/index.html @@ -911,7 +911,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -926,7 +926,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -941,7 +941,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None PorkchopPlotInterplanetaryTrajectory @@ -1050,7 +1050,7 @@

OPL – Optimisation problem library

TulipaEnergy - Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/docs/problems.html b/docs/problems.html index ad6bbce..4601b97 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -885,7 +885,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -900,7 +900,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -915,7 +915,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None PorkchopPlotInterplanetaryTrajectory @@ -1024,7 +1024,7 @@ TulipaEnergy - Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable From 5e7af8a47a11552760cafcbf5ebe69e9194e7c67 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:40:00 +0200 Subject: [PATCH 09/25] remove unnecessary changes here --- utils/merge_yaml.py | 11 ++++++----- utils/validate_yaml.py | 29 +++++++++++------------------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index e8223ae..fb01b9d 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -2,6 +2,7 @@ import os import sys from pathlib import Path +from typing import List, Dict # Add parent directory to sys.path parent = Path(__file__).resolve().parent.parent @@ -10,7 +11,7 @@ from utils.validate_yaml import read_data, validate_data, PROBLEMS_FILE -def write_data(filepath, data): +def write_data(filepath: str, data: List[Dict]) -> bool: try: with open(filepath, "w") as f: yaml.safe_dump(data, f, sort_keys=False) @@ -24,13 +25,13 @@ def write_data(filepath, data): return True -def update_existing_data(existing_data, new_data): +def update_existing_data(existing_data: List[Dict], new_data: List[Dict]) -> bool: existing_data.extend(new_data) write_success = write_data(PROBLEMS_FILE, existing_data) return write_success -def delete_new_file(file_path): +def delete_new_file(file_path: str) -> bool: # Delete the new file after merging try: os.remove(file_path) @@ -41,7 +42,7 @@ def delete_new_file(file_path): return True -def merge_new_problems(new_problems_yaml_path: str): +def merge_new_problems(new_problems_yaml_path: str) -> bool: # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) if new_data_status != 0: @@ -70,7 +71,7 @@ def merge_new_problems(new_problems_yaml_path: str): print(f"::error::Failed to update existing problems data in {PROBLEMS_FILE}.") return False - # Reset the template content + # Remove the new file after merging reset_status = delete_new_file(new_problems_yaml_path) if not reset_status: print(f"::error::Failed to delete new problem file {new_problems_yaml_path}.") diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index 0470154..c71be85 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -108,36 +108,29 @@ def check_novelty(data): return True -def validate_data(data) -> bool: +def validate_yaml(filepath): + status, data = read_data(filepath) + if status != 0: + sys.exit(1) if not check_format(data): - return False + sys.exit(1) + assert data is not None for i, new_data in enumerate(data): # Iterate through each top-level entry # Check required and unique fields if not check_fields(new_data) or not check_novelty(new_data): print(f"::error::Validation failed for entry {i+1}.") - return False + sys.exit(1) # YAML is valid if we reach this point - print("::notice::YAML syntax is valid.") - return True - - -def validate_yaml(filepath: str) -> bool: - status, data = read_data(filepath) - if status != 0: - return False - return validate_data(data) + print("YAML syntax is valid.") + sys.exit(0) if __name__ == "__main__": - if len(sys.argv) != 2: + if len(sys.argv) < 2: print("::error::Usage: python validate_yaml.py ") sys.exit(1) filepath = sys.argv[1] - valid = validate_yaml(filepath) - if valid: - sys.exit(0) - else: - sys.exit(1) + validate_yaml(filepath) From c8ac3b28c282c251e7eea3f92d2d58d18870e8f0 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:41:12 +0200 Subject: [PATCH 10/25] undoing changes --- .github/workflows/new_problem_check.yml | 3 --- utils/new_problem.yaml | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 utils/new_problem.yaml diff --git a/.github/workflows/new_problem_check.yml b/.github/workflows/new_problem_check.yml index 5f2917c..5a13ee6 100644 --- a/.github/workflows/new_problem_check.yml +++ b/.github/workflows/new_problem_check.yml @@ -2,8 +2,6 @@ name: New Problem Check on: push: - branches: - - main paths: - "utils/new_problem.yaml" pull_request: @@ -34,7 +32,6 @@ jobs: pip install -r utils/requirements.txt - name: Run New Problem Check - if: ${{ hashFiles('utils/new_problem.yaml') != '' }} run: | python utils/validate_yaml.py utils/new_problem.yaml diff --git a/utils/new_problem.yaml b/utils/new_problem.yaml new file mode 100644 index 0000000..2c300e1 --- /dev/null +++ b/utils/new_problem.yaml @@ -0,0 +1,14 @@ +- name: template + suite/generator/single: suite + objectives: '2' + dimensionality: scalable + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'no' + multimodal: 'yes' + multi-fidelity: 'no' + reference: '' + implementation: '' + source (real-world/artificial): '' + textual description: 'This is a dummy template' From 7a3f931a78748e32dd86521d127dc4fc75d585bd Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:41:57 +0200 Subject: [PATCH 11/25] removing additional changes --- utils/README.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/utils/README.md b/utils/README.md index dd71de7..3d3fbee 100644 --- a/utils/README.md +++ b/utils/README.md @@ -29,22 +29,3 @@ This script checks the new content for the following: pip install -r utils/requirements.txt python utils/validate_yaml.py utils/new_problem.yaml ``` - -## new problem example - -```json -- name: template - suite/generator/single: suite - objectives: '1' - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: '' - implementation: '' - source (real-world/artificial): '' - textual description: 'This is a dummy template' -`` From 617971ff7a830725e7f0a95d8da9ab300ebc80ae Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:56:14 +0200 Subject: [PATCH 12/25] undoing changes --- .github/workflows/new_problem_check.yml | 3 + docs/index.html | 79 +------------------------ docs/problems.html | 79 +------------------------ problems.yaml | 76 +----------------------- utils/README.md | 1 - 5 files changed, 10 insertions(+), 228 deletions(-) diff --git a/.github/workflows/new_problem_check.yml b/.github/workflows/new_problem_check.yml index 5a13ee6..5f2917c 100644 --- a/.github/workflows/new_problem_check.yml +++ b/.github/workflows/new_problem_check.yml @@ -2,6 +2,8 @@ name: New Problem Check on: push: + branches: + - main paths: - "utils/new_problem.yaml" pull_request: @@ -32,6 +34,7 @@ jobs: pip install -r utils/requirements.txt - name: Run New Problem Check + if: ${{ hashFiles('utils/new_problem.yaml') != '' }} run: | python utils/validate_yaml.py utils/new_problem.yaml diff --git a/docs/index.html b/docs/index.html index 5819558..c3a5882 100644 --- a/docs/index.html +++ b/docs/index.html @@ -330,7 +330,7 @@

OPL – Optimisation problem library

GBEA - expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA + expensive evaluations 5s-35s suite 1-2 scalable @@ -341,7 +341,7 @@

OPL – Optimisation problem library

no real world https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea + ? Car structure @@ -1138,81 +1138,6 @@

OPL – Optimisation problem library

https://dis.ijs.si/tea/Publications/Tusar23Multistep.pdf (paper in Slovene) Implementation not freely available - - RW-GAN-Mario - RW-GAN-Mario is a component of GBEA - suite - 1-2 - scalable - continuous - no - no - yes - no - real-world - https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea - - - TopTrumps - TopTrump is a component of GBEA - suite - 1-2 - scalable - continuous - no - no - yes - no - real-world - https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea - - - AutomotiveCrashworthiness - - suite - 1 - 8–22 (fixed per instance) - continuous - no - no - yes - no - real-world - https://dl.acm.org/doi/pdf/10.1145/3646554 - https://github.com/fx-long/CEOELA - - - PowertrainAnomalyTimeseries - - suite - 1 - 1–7 (problem-dependent; fixed per instance) - continuous - yes - no - no - no - real-world - https://www.sciencedirect.com/science/article/pii/S2214579625000681 - https://zenodo.org/records/14892756 - - - HPA - - suite - 1–9 - scalable - continuous - yes - no - no - no - real-world-inspired - https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 - https://github.com/Nobuo-Namura/hpa - name textual description suite/generator/single objectives dimensionality variable type constraints dynamic noise multi-fidelity source (real-world/artificial) reference implementation diff --git a/docs/problems.html b/docs/problems.html index 4601b97..ff51d79 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -304,7 +304,7 @@ GBEA - expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA + expensive evaluations 5s-35s suite 1-2 scalable @@ -315,7 +315,7 @@ no real world https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea + ? Car structure @@ -1112,80 +1112,5 @@ https://dis.ijs.si/tea/Publications/Tusar23Multistep.pdf (paper in Slovene) Implementation not freely available - - RW-GAN-Mario - RW-GAN-Mario is a component of GBEA - suite - 1-2 - scalable - continuous - no - no - yes - no - real-world - https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea - - - TopTrumps - TopTrump is a component of GBEA - suite - 1-2 - scalable - continuous - no - no - yes - no - real-world - https://doi.org/10.1145/3321707.3321805 - https://github.com/ttusar/coco-gbea - - - AutomotiveCrashworthiness - - suite - 1 - 8–22 (fixed per instance) - continuous - no - no - yes - no - real-world - https://dl.acm.org/doi/pdf/10.1145/3646554 - https://github.com/fx-long/CEOELA - - - PowertrainAnomalyTimeseries - - suite - 1 - 1–7 (problem-dependent; fixed per instance) - continuous - yes - no - no - no - real-world - https://www.sciencedirect.com/science/article/pii/S2214579625000681 - https://zenodo.org/records/14892756 - - - HPA - - suite - 1–9 - scalable - continuous - yes - no - no - no - real-world-inspired - https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 - https://github.com/Nobuo-Namura/hpa - name textual description suite/generator/single objectives dimensionality variable type constraints dynamic noise multi-fidelity source (real-world/artificial) reference implementation \ No newline at end of file diff --git a/problems.yaml b/problems.yaml index 9e3b2fa..3cfb557 100644 --- a/problems.yaml +++ b/problems.yaml @@ -272,12 +272,12 @@ constraints: 'no' dynamic: 'no' noise: 'yes' - multimodal: 'yes' + multimodal: '?' multi-fidelity: 'no' reference: https://doi.org/10.1145/3321707.3321805 - implementation: 'https://github.com/ttusar/coco-gbea' + implementation: '?' source (real-world/artificial): real world - textual description: 'expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA' + textual description: expensive evaluations 5s-35s - name: Car structure suite/generator/single: suite objectives: '2' @@ -1270,73 +1270,3 @@ links to usage examples: '' general: This is not an available problem, but could be interesting to show to researchers which difficulties appear in real-world problems -- name: RW-GAN-Mario - suite/generator/single: suite - objectives: 1-2 - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'yes' - multimodal: 'yes' - multi-fidelity: 'no' - reference: https://doi.org/10.1145/3321707.3321805 - implementation: https://github.com/ttusar/coco-gbea - source (real-world/artificial): 'real-world' - textual description: 'RW-GAN-Mario is a component of GBEA' -- name: TopTrumps - suite/generator/single: suite - objectives: 1-2 - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'yes' - multimodal: 'yes' - multi-fidelity: 'no' - reference: https://doi.org/10.1145/3321707.3321805 - implementation: https://github.com/ttusar/coco-gbea - source (real-world/artificial): 'real-world' - textual description: 'TopTrump is a component of GBEA' -- name: AutomotiveCrashworthiness - suite/generator/single: suite - objectives: '1' - dimensionality: 8–22 (fixed per instance) - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'yes' - multimodal: 'yes' - multi-fidelity: 'no' - reference: https://dl.acm.org/doi/pdf/10.1145/3646554 - implementation: https://github.com/fx-long/CEOELA - source (real-world/artificial): 'real-world' - textual description: '' -- name: PowertrainAnomalyTimeseries - suite/generator/single: suite - objectives: 1 - dimensionality: 1–7 (problem-dependent; fixed per instance) - variable type: continuous - constraints: 'yes' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: https://www.sciencedirect.com/science/article/pii/S2214579625000681 - implementation: https://zenodo.org/records/14892756 - source (real-world/artificial): 'real-world' - textual description: '' -- name: HPA - suite/generator/single: suite - objectives: 1–9 - dimensionality: scalable - variable type: continuous - constraints: 'yes' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 - implementation: https://github.com/Nobuo-Namura/hpa - source (real-world/artificial): 'real-world-inspired' - textual description: '' diff --git a/utils/README.md b/utils/README.md index 3d9556c..2ad9e5e 100644 --- a/utils/README.md +++ b/utils/README.md @@ -47,4 +47,3 @@ python utils/validate_yaml.py utils/new_problem.yaml implementation: '' source (real-world/artificial): '' textual description: 'This is a dummy template' -``` From b9cd070d849da2a8bc1921649417e328b6597eae Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 14:56:42 +0200 Subject: [PATCH 13/25] remove file again --- utils/new_problem.yaml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 utils/new_problem.yaml diff --git a/utils/new_problem.yaml b/utils/new_problem.yaml deleted file mode 100644 index 2c300e1..0000000 --- a/utils/new_problem.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- name: template - suite/generator/single: suite - objectives: '2' - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: '' - implementation: '' - source (real-world/artificial): '' - textual description: 'This is a dummy template' From 27700d5491ce25061d67d041dee573a464358795 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 16:16:56 +0200 Subject: [PATCH 14/25] finishing merge --- docs/index.html | 79 ++++++++++++++++++++++++++++++++++++++++++++-- docs/problems.html | 79 ++++++++++++++++++++++++++++++++++++++++++++-- problems.yaml | 76 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 227 insertions(+), 7 deletions(-) diff --git a/docs/index.html b/docs/index.html index c3a5882..5819558 100644 --- a/docs/index.html +++ b/docs/index.html @@ -330,7 +330,7 @@

OPL – Optimisation problem library

GBEA - expensive evaluations 5s-35s + expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA suite 1-2 scalable @@ -341,7 +341,7 @@

OPL – Optimisation problem library

no real world https://doi.org/10.1145/3321707.3321805 - ? + https://github.com/ttusar/coco-gbea Car structure @@ -1138,6 +1138,81 @@

OPL – Optimisation problem library

https://dis.ijs.si/tea/Publications/Tusar23Multistep.pdf (paper in Slovene) Implementation not freely available + + RW-GAN-Mario + RW-GAN-Mario is a component of GBEA + suite + 1-2 + scalable + continuous + no + no + yes + no + real-world + https://doi.org/10.1145/3321707.3321805 + https://github.com/ttusar/coco-gbea + + + TopTrumps + TopTrump is a component of GBEA + suite + 1-2 + scalable + continuous + no + no + yes + no + real-world + https://doi.org/10.1145/3321707.3321805 + https://github.com/ttusar/coco-gbea + + + AutomotiveCrashworthiness + + suite + 1 + 8–22 (fixed per instance) + continuous + no + no + yes + no + real-world + https://dl.acm.org/doi/pdf/10.1145/3646554 + https://github.com/fx-long/CEOELA + + + PowertrainAnomalyTimeseries + + suite + 1 + 1–7 (problem-dependent; fixed per instance) + continuous + yes + no + no + no + real-world + https://www.sciencedirect.com/science/article/pii/S2214579625000681 + https://zenodo.org/records/14892756 + + + HPA + + suite + 1–9 + scalable + continuous + yes + no + no + no + real-world-inspired + https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 + https://github.com/Nobuo-Namura/hpa + name textual description suite/generator/single objectives dimensionality variable type constraints dynamic noise multi-fidelity source (real-world/artificial) reference implementation diff --git a/docs/problems.html b/docs/problems.html index ff51d79..4601b97 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -304,7 +304,7 @@ GBEA - expensive evaluations 5s-35s + expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA suite 1-2 scalable @@ -315,7 +315,7 @@ no real world https://doi.org/10.1145/3321707.3321805 - ? + https://github.com/ttusar/coco-gbea Car structure @@ -1112,5 +1112,80 @@ https://dis.ijs.si/tea/Publications/Tusar23Multistep.pdf (paper in Slovene) Implementation not freely available + + RW-GAN-Mario + RW-GAN-Mario is a component of GBEA + suite + 1-2 + scalable + continuous + no + no + yes + no + real-world + https://doi.org/10.1145/3321707.3321805 + https://github.com/ttusar/coco-gbea + + + TopTrumps + TopTrump is a component of GBEA + suite + 1-2 + scalable + continuous + no + no + yes + no + real-world + https://doi.org/10.1145/3321707.3321805 + https://github.com/ttusar/coco-gbea + + + AutomotiveCrashworthiness + + suite + 1 + 8–22 (fixed per instance) + continuous + no + no + yes + no + real-world + https://dl.acm.org/doi/pdf/10.1145/3646554 + https://github.com/fx-long/CEOELA + + + PowertrainAnomalyTimeseries + + suite + 1 + 1–7 (problem-dependent; fixed per instance) + continuous + yes + no + no + no + real-world + https://www.sciencedirect.com/science/article/pii/S2214579625000681 + https://zenodo.org/records/14892756 + + + HPA + + suite + 1–9 + scalable + continuous + yes + no + no + no + real-world-inspired + https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 + https://github.com/Nobuo-Namura/hpa + name textual description suite/generator/single objectives dimensionality variable type constraints dynamic noise multi-fidelity source (real-world/artificial) reference implementation \ No newline at end of file diff --git a/problems.yaml b/problems.yaml index 3cfb557..9e3b2fa 100644 --- a/problems.yaml +++ b/problems.yaml @@ -272,12 +272,12 @@ constraints: 'no' dynamic: 'no' noise: 'yes' - multimodal: '?' + multimodal: 'yes' multi-fidelity: 'no' reference: https://doi.org/10.1145/3321707.3321805 - implementation: '?' + implementation: 'https://github.com/ttusar/coco-gbea' source (real-world/artificial): real world - textual description: expensive evaluations 5s-35s + textual description: 'expensive evaluations 5s-35s, RW-GAN-Mario and TopTrumps are part of GBEA' - name: Car structure suite/generator/single: suite objectives: '2' @@ -1270,3 +1270,73 @@ links to usage examples: '' general: This is not an available problem, but could be interesting to show to researchers which difficulties appear in real-world problems +- name: RW-GAN-Mario + suite/generator/single: suite + objectives: 1-2 + dimensionality: scalable + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'yes' + multimodal: 'yes' + multi-fidelity: 'no' + reference: https://doi.org/10.1145/3321707.3321805 + implementation: https://github.com/ttusar/coco-gbea + source (real-world/artificial): 'real-world' + textual description: 'RW-GAN-Mario is a component of GBEA' +- name: TopTrumps + suite/generator/single: suite + objectives: 1-2 + dimensionality: scalable + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'yes' + multimodal: 'yes' + multi-fidelity: 'no' + reference: https://doi.org/10.1145/3321707.3321805 + implementation: https://github.com/ttusar/coco-gbea + source (real-world/artificial): 'real-world' + textual description: 'TopTrump is a component of GBEA' +- name: AutomotiveCrashworthiness + suite/generator/single: suite + objectives: '1' + dimensionality: 8–22 (fixed per instance) + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'yes' + multimodal: 'yes' + multi-fidelity: 'no' + reference: https://dl.acm.org/doi/pdf/10.1145/3646554 + implementation: https://github.com/fx-long/CEOELA + source (real-world/artificial): 'real-world' + textual description: '' +- name: PowertrainAnomalyTimeseries + suite/generator/single: suite + objectives: 1 + dimensionality: 1–7 (problem-dependent; fixed per instance) + variable type: continuous + constraints: 'yes' + dynamic: 'no' + noise: 'no' + multimodal: 'yes' + multi-fidelity: 'no' + reference: https://www.sciencedirect.com/science/article/pii/S2214579625000681 + implementation: https://zenodo.org/records/14892756 + source (real-world/artificial): 'real-world' + textual description: '' +- name: HPA + suite/generator/single: suite + objectives: 1–9 + dimensionality: scalable + variable type: continuous + constraints: 'yes' + dynamic: 'no' + noise: 'no' + multimodal: 'yes' + multi-fidelity: 'no' + reference: https://link.springer.com/chapter/10.1007/978-981-96-3506-1_14 + implementation: https://github.com/Nobuo-Namura/hpa + source (real-world/artificial): 'real-world-inspired' + textual description: '' From f6dc0dfc906009c870806b5338416512c4db626e Mon Sep 17 00:00:00 2001 From: Vanessa <3634850+CIGbalance@users.noreply.github.com> Date: Mon, 20 Apr 2026 16:24:07 +0200 Subject: [PATCH 15/25] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- utils/merge_yaml.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index fb01b9d..d157550 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -19,6 +19,9 @@ def write_data(filepath: str, data: List[Dict]) -> bool: except FileNotFoundError: print(f"::error::File not found: {filepath}") return False + except OSError as e: + print(f"::error::Error writing file {filepath}: {e}") + return False except yaml.YAMLError as e: print(f"::error::YAML syntax error: {e}") return False @@ -74,8 +77,10 @@ def merge_new_problems(new_problems_yaml_path: str) -> bool: # Remove the new file after merging reset_status = delete_new_file(new_problems_yaml_path) if not reset_status: - print(f"::error::Failed to delete new problem file {new_problems_yaml_path}.") - return False + print( + f"::warning::Merged data into {PROBLEMS_FILE}, but failed to delete " + f"new problem file {new_problems_yaml_path}." + ) print(f"::notice::Merged {len(new_data)} new problems into {PROBLEMS_FILE}.") return True From 4f5fac95c10e19edec9a69a7a0a229abb9c7bf35 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 16:24:34 +0200 Subject: [PATCH 16/25] new_data None --- utils/merge_yaml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index fb01b9d..6d6c972 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -45,7 +45,7 @@ def delete_new_file(file_path: str) -> bool: def merge_new_problems(new_problems_yaml_path: str) -> bool: # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) - if new_data_status != 0: + if new_data_status != 0 or new_data is None: print( f"::error::New problems data could not be read from {new_problems_yaml_path}." ) @@ -57,7 +57,7 @@ def merge_new_problems(new_problems_yaml_path: str) -> bool: # Read existing data existing_data_status, existing_data = read_data(PROBLEMS_FILE) - if existing_data_status != 0: + if existing_data_status != 0 or existing_data is None: print( f"::error::Existing problems data could not be read from {PROBLEMS_FILE}." ) From c65cdc9451faf715770fb33b5e6d870e2775d5f3 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 16:29:03 +0200 Subject: [PATCH 17/25] imports --- docs/index.html | 8 ++++---- docs/problems.html | 8 ++++---- utils/merge_yaml.py | 4 ++-- utils/validate_yaml.py | 25 +++++++++++++++++-------- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/docs/index.html b/docs/index.html index 5819558..77f00b8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -911,7 +911,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -926,7 +926,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -941,7 +941,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - None + PorkchopPlotInterplanetaryTrajectory @@ -1050,7 +1050,7 @@

OPL – Optimisation problem library

TulipaEnergy - Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/docs/problems.html b/docs/problems.html index 4601b97..ad6bbce 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -885,7 +885,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -900,7 +900,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -915,7 +915,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - None + PorkchopPlotInterplanetaryTrajectory @@ -1024,7 +1024,7 @@ TulipaEnergy - Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index 7844258..e6772cc 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -75,8 +75,8 @@ def merge_new_problems(new_problems_yaml_path: str) -> bool: return False # Remove the new file after merging - reset_status = delete_new_file(new_problems_yaml_path) - if not reset_status: + rm_status = delete_new_file(new_problems_yaml_path) + if not rm_status: print( f"::warning::Merged data into {PROBLEMS_FILE}, but failed to delete " f"new problem file {new_problems_yaml_path}." diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index c71be85..3ea9655 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -108,23 +108,32 @@ def check_novelty(data): return True -def validate_yaml(filepath): - status, data = read_data(filepath) - if status != 0: - sys.exit(1) - if not check_format(data): - sys.exit(1) +def validate_data(data): assert data is not None + if not check_format(data): + return False for i, new_data in enumerate(data): # Iterate through each top-level entry # Check required and unique fields if not check_fields(new_data) or not check_novelty(new_data): print(f"::error::Validation failed for entry {i+1}.") - sys.exit(1) + return False # YAML is valid if we reach this point print("YAML syntax is valid.") - sys.exit(0) + + return True + + +def validate_yaml(filepath): + status, data = read_data(filepath) + if status != 0: + sys.exit(1) + valid = validate_data(data) + if not valid: + sys.exit(1) + else: + sys.exit(0) if __name__ == "__main__": From cadb19a9d1ba886e5e7b3f559cda8b62a54b13b7 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 16:30:16 +0200 Subject: [PATCH 18/25] removing no changes --- docs/index.html | 8 ++++---- docs/problems.html | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/index.html b/docs/index.html index 77f00b8..5819558 100644 --- a/docs/index.html +++ b/docs/index.html @@ -911,7 +911,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -926,7 +926,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -941,7 +941,7 @@

OPL – Optimisation problem library

no artificial https://doi.org/10.1145/3449726.3459521 - + None PorkchopPlotInterplanetaryTrajectory @@ -1050,7 +1050,7 @@

OPL – Optimisation problem library

TulipaEnergy - Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable diff --git a/docs/problems.html b/docs/problems.html index ad6bbce..4601b97 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -885,7 +885,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None Onemax+Sphere / DeceptiveTrap+RotatedEllipsoid @@ -900,7 +900,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None InverseDeceptiveTrap+RotatedEllipsoid / DeceptiveTrap+RotatedEllipsoid @@ -915,7 +915,7 @@ no artificial https://doi.org/10.1145/3449726.3459521 - + None PorkchopPlotInterplanetaryTrajectory @@ -1024,7 +1024,7 @@ TulipaEnergy - Determine the optimal investment and operation decisions  for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. + Determine the optimal investment and operation decisions for different types of assets in the energy system (production, consumption, conversion, storage, and transport), while minimizing loss of load. Problem Suite 1 scalable From 894c9c1a3c60e00388b092014ab4ada65906243e Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 16:33:43 +0200 Subject: [PATCH 19/25] add typing --- utils/validate_yaml.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index 3ea9655..8744e41 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -2,6 +2,7 @@ import sys from pathlib import Path +from typing import List, Dict, Tuple # Add parent directory to sys.path parent = Path(__file__).resolve().parent.parent @@ -17,7 +18,7 @@ PROBLEMS_FILE = "problems.yaml" -def read_data(filepath): +def read_data(filepath: str) -> Tuple[int, List[Dict] | None]: try: with open(filepath, "r") as f: data = yaml.safe_load(f) @@ -30,7 +31,7 @@ def read_data(filepath): return 1, None -def check_format(data): +def check_format(data: List[Dict]) -> bool: num_problems = len(data) if len(data) < 1: print("::error::YAML file should contain at least one top level entry.") @@ -50,7 +51,7 @@ def check_format(data): return True -def check_fields(data): +def check_fields(data: Dict) -> bool: missing = [field for field in REQUIRED_FIELDS if field not in data] if missing: print(f"::error::Missing required fields: {', '.join(missing)}") @@ -80,7 +81,7 @@ def check_fields(data): return True -def check_novelty(data): +def check_novelty(data: Dict) -> bool: # Load existing problems read_status, existing_data = read_data(PROBLEMS_FILE) if read_status != 0: @@ -108,7 +109,7 @@ def check_novelty(data): return True -def validate_data(data): +def validate_data(data: List[Dict]) -> bool: assert data is not None if not check_format(data): return False @@ -125,9 +126,9 @@ def validate_data(data): return True -def validate_yaml(filepath): +def validate_yaml(filepath: str) -> None: status, data = read_data(filepath) - if status != 0: + if status != 0 or data is None: sys.exit(1) valid = validate_data(data) if not valid: From c209260e1c7504a7c65fc493d2755889a4a0079c Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 18:01:16 +0200 Subject: [PATCH 20/25] change to manual mode --- utils/merge_yaml.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index e6772cc..12c412f 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -8,7 +8,7 @@ parent = Path(__file__).resolve().parent.parent sys.path.insert(0, str(parent)) -from utils.validate_yaml import read_data, validate_data, PROBLEMS_FILE +from utils.validate_yaml import read_data, validate_data def write_data(filepath: str, data: List[Dict]) -> bool: @@ -28,9 +28,11 @@ def write_data(filepath: str, data: List[Dict]) -> bool: return True -def update_existing_data(existing_data: List[Dict], new_data: List[Dict]) -> bool: +def update_existing_data( + existing_data: List[Dict], new_data: List[Dict], out_file: str +) -> bool: existing_data.extend(new_data) - write_success = write_data(PROBLEMS_FILE, existing_data) + write_success = write_data(out_file, existing_data) return write_success @@ -45,7 +47,7 @@ def delete_new_file(file_path: str) -> bool: return True -def merge_new_problems(new_problems_yaml_path: str) -> bool: +def merge_new_problems(new_problems_yaml_path: str, big_yaml_path: str) -> bool: # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) if new_data_status != 0 or new_data is None: @@ -59,39 +61,40 @@ def merge_new_problems(new_problems_yaml_path: str) -> bool: return False # Read existing data - existing_data_status, existing_data = read_data(PROBLEMS_FILE) + existing_data_status, existing_data = read_data(big_yaml_path) if existing_data_status != 0 or existing_data is None: print( - f"::error::Existing problems data could not be read from {PROBLEMS_FILE}." + f"::error::Existing problems data could not be read from {big_yaml_path}." ) return False # All valid, we can now just merge the dicts assert existing_data is not None assert new_data is not None - updated = update_existing_data(existing_data, new_data) + updated = update_existing_data(existing_data, new_data, big_yaml_path) if not updated: - print(f"::error::Failed to update existing problems data in {PROBLEMS_FILE}.") + print(f"::error::Failed to update existing problems data in {big_yaml_path}.") return False # Remove the new file after merging rm_status = delete_new_file(new_problems_yaml_path) if not rm_status: print( - f"::warning::Merged data into {PROBLEMS_FILE}, but failed to delete " + f"::warning::Merged data into {big_yaml_path}, but failed to delete " f"new problem file {new_problems_yaml_path}." ) - print(f"::notice::Merged {len(new_data)} new problems into {PROBLEMS_FILE}.") + print(f"::notice::Merged {len(new_data)} new problems into {big_yaml_path}.") return True if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: python merge_yaml.py ") + if len(sys.argv) != 3: + print("Usage: python merge_yaml.py ") sys.exit(1) new_problems_yaml_path = sys.argv[1] - status = merge_new_problems(new_problems_yaml_path) + big_yaml_path = sys.argv[2] + status = merge_new_problems(new_problems_yaml_path, big_yaml_path) if not status: sys.exit(1) else: From 3182de51e2a839485300736fc8153e22d1669b2f Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Mon, 20 Apr 2026 18:13:54 +0200 Subject: [PATCH 21/25] updated for new workflow --- utils/README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/utils/README.md b/utils/README.md index 2ad9e5e..189dd82 100644 --- a/utils/README.md +++ b/utils/README.md @@ -1,19 +1,18 @@ # OPL YAML utils -This folder contains utility scripts for working with the YAML format to describe problems in context of OPL. They are mainly intended to be run automatically via GitHub Actions to make collaboration easier. +This folder contains utility scripts for working with the YAML format to describe problems in context of OPL. Some of them are mainly intended to be run automatically via GitHub Actions to make collaboration easier, others are utility functions for maintainers. The intended way of adding a new problem to the repository is thus as follows: -* Create a file in 'utils/new_problem.yaml' based on the template (see below). +* Create a new yaml file based on the template (see below). +* Run the [merge script](merge_yaml.py) locally to update the [problems.yaml](../problems.yaml) file and check that the formatting is correct. * Create a PR with the changes (for example with a fork). What happens in the background then is: -* On PR creation and commits to the PR, the [validate_yaml.py](validate_yaml.py) script is run to check that the YAML file is valid and consistent. It is expecting the changes to be in the [new_problem.yaml](new_problem.yaml) file. +* On PR creation and commits to the PR, the [validate_yaml.py](validate_yaml.py) script is run to check that the [problems.yaml](../problems.yaml) file is still valid and consistent. * Then the PR should be reviewed manually. -* When the PR is merged into the main branch, a second script runs (which doesn't exist yet), that adds the content of [new_problem.yaml](new_problem.yaml) to the [problems.yaml](../problems.yaml) file, and reverts the changes to the new_problem.yaml. - -:warning: Note that the GitHubActions do not exist yet either, this is a WIP. +* When the PR is merged into the main branch with changes to problems.yaml, the check are run again. ## validate_yaml.py @@ -21,13 +20,24 @@ This script checks the new content for the following: * The YAML syntax is valid and is in expected format * The required fields are present. -* Specific fields are unique across the new set of problems (e.g. name) +* Specific fields are unique across the set of problems (e.g. name) + +:warning: Execute from root of the repository. Tested with python 3.12 + +```bash +pip install -r utils/requirements.txt +python utils/validate_yaml.py problems.yaml +``` + +## merge_yaml.py + +This script merges a new problem description in a separate yaml file into the main [problems.yaml](../problems.yaml) file. It runs the validation checks from the above script before merging and deletes the separate yaml file after merging. :warning: Execute from root of the repository. Tested with python 3.12 ```bash pip install -r utils/requirements.txt -python utils/validate_yaml.py utils/new_problem.yaml +python utils/merge_yaml.py new_problem.yaml problems.yaml ``` ## new problem example From 3d8bc5868524ee256a5bd47e1b409c99791a1c60 Mon Sep 17 00:00:00 2001 From: Vanessa <3634850+CIGbalance@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:02:16 +0200 Subject: [PATCH 22/25] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- utils/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/README.md b/utils/README.md index 189dd82..263edda 100644 --- a/utils/README.md +++ b/utils/README.md @@ -12,7 +12,7 @@ What happens in the background then is: * On PR creation and commits to the PR, the [validate_yaml.py](validate_yaml.py) script is run to check that the [problems.yaml](../problems.yaml) file is still valid and consistent. * Then the PR should be reviewed manually. -* When the PR is merged into the main branch with changes to problems.yaml, the check are run again. +* When the PR is merged into the main branch with changes to problems.yaml, the checks are run again. ## validate_yaml.py @@ -20,7 +20,7 @@ This script checks the new content for the following: * The YAML syntax is valid and is in expected format * The required fields are present. -* Specific fields are unique across the set of problems (e.g. name) +* Specific fields are unique across the set of problems (e.g. name) :warning: Execute from root of the repository. Tested with python 3.12 From d332b7e5b14a982ab02ff76115aad63d62ea46c3 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Tue, 21 Apr 2026 12:25:50 +0200 Subject: [PATCH 23/25] some additional review updates --- utils/merge_yaml.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index 12c412f..26f0e2e 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -1,5 +1,4 @@ import yaml -import os import sys from pathlib import Path from typing import List, Dict @@ -8,7 +7,7 @@ parent = Path(__file__).resolve().parent.parent sys.path.insert(0, str(parent)) -from utils.validate_yaml import read_data, validate_data +from utils.validate_yaml import read_data, validate_data, validate_yaml def write_data(filepath: str, data: List[Dict]) -> bool: @@ -36,17 +35,6 @@ def update_existing_data( return write_success -def delete_new_file(file_path: str) -> bool: - # Delete the new file after merging - try: - os.remove(file_path) - print(f"::notice::Deleted new problem file {file_path}.") - except OSError as e: - print(f"::error::Error deleting file {file_path}: {e}") - return False - return True - - def merge_new_problems(new_problems_yaml_path: str, big_yaml_path: str) -> bool: # Read and validate new data new_data_status, new_data = read_data(new_problems_yaml_path) @@ -76,15 +64,17 @@ def merge_new_problems(new_problems_yaml_path: str, big_yaml_path: str) -> bool: print(f"::error::Failed to update existing problems data in {big_yaml_path}.") return False - # Remove the new file after merging - rm_status = delete_new_file(new_problems_yaml_path) - if not rm_status: + # Validate resulting data + final_status, final_data = validate_yaml(big_yaml_path) + if final_status != 0 or final_data is None: print( - f"::warning::Merged data into {big_yaml_path}, but failed to delete " - f"new problem file {new_problems_yaml_path}." + f"::error::Merged data in {big_yaml_path} is not valid after merging new problems." ) + return False - print(f"::notice::Merged {len(new_data)} new problems into {big_yaml_path}.") + print( + f"::notice::Merged {len(new_data)} new problems into {big_yaml_path}. {new_problems_yaml_path} can now be deleted." + ) return True From f0352ec5aae591d8ff491046c7a0112243d7e3ae Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Tue, 21 Apr 2026 16:28:21 +0200 Subject: [PATCH 24/25] check format fixes --- utils/validate_yaml.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index ed838b2..79f4238 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -32,6 +32,9 @@ def read_data(filepath: str) -> Tuple[int, List[Dict] | None]: def check_format(data: List[Dict]) -> bool: num_problems = len(data) + if not isinstance(data, list): + print("::error::YAML file should contain a list of entries.") + return False if len(data) < 1: print("::error::YAML file should contain at least one top level entry.") return False @@ -43,7 +46,9 @@ def check_format(data: List[Dict]) -> bool: return False unique_fields.append({k: v for k, v in entry.items() if k in UNIQUE_FIELDS}) for k in UNIQUE_FIELDS: - values = [entry[k] for entry in unique_fields] + values = [ + entry[k] for entry in unique_fields if k in entry and entry[k] is not None + ] if len(values) != len(set(values)): print(f"::error::Field '{k}' must be unique across all entries.") return False @@ -80,7 +85,7 @@ def check_fields(data: Dict) -> bool: return True -def check_novelty(data:Dict, checked_data:List[Dict]) -> bool: +def check_novelty(data: Dict, checked_data: List[Dict]) -> bool: for field in UNIQUE_FIELDS + UNIQUE_WARNING_FIELDS: # skip empty fields if not data.get(field): From 70361d48954217fba1abf2b4f1d7264ebefdcd08 Mon Sep 17 00:00:00 2001 From: TheHedgeify Date: Tue, 21 Apr 2026 17:05:44 +0200 Subject: [PATCH 25/25] do not write if failing --- utils/merge_yaml.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py index 26f0e2e..6705197 100644 --- a/utils/merge_yaml.py +++ b/utils/merge_yaml.py @@ -31,6 +31,11 @@ def update_existing_data( existing_data: List[Dict], new_data: List[Dict], out_file: str ) -> bool: existing_data.extend(new_data) + # validate merged data before writing + valid = validate_data(existing_data) + if not valid: + print(f"::error::Merged data is not valid, cannot write to {out_file}.") + return False write_success = write_data(out_file, existing_data) return write_success