From 34906da20c5693b0fa9bccf4d2f159d1976ffbc0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:51:35 +0000 Subject: [PATCH 1/3] feat: Make YAML serialization deterministic pydantic-yaml serializes model fields via model_dump_json() but set iteration is order depends on hash randomization, which is randomized per interpreter process, so `to_yaml_str()` produced a different ordering of these fields on every run even when the underlying data was unchanged. Add a `_CanonicalMixin` used by new `CanonicalModel`/`CanonicalRootModel` base classes in schema.py. Each defines a single model_serializer(mode="wrap") that inspects a model's actual field values at dump time and sorts any that are a `set` into a list. --- docs/index.html | 16 +++--- docs/problems.html | 16 +++--- problems.yaml | 124 ++++++++++++++++++++++------------------- src/opltools/schema.py | 70 ++++++++++++++++++++--- 4 files changed, 146 insertions(+), 80 deletions(-) diff --git a/docs/index.html b/docs/index.html index e4b4df0..80ebe5d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -890,11 +890,11 @@

OPL – Optimisation problem library

>=1 - MA-BBOB (IOHexperimenter) | IOHexperimenter + IOHexperimenter | MA-BBOB (IOHexperimenter) C++/Python - https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb | https://github.com/IOHprofiler/IOHexperimenter - Example notebook for MA-BBOB in IOHexperimenter | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb + IOHprofiler experimenter framework | Example notebook for MA-BBOB in IOHexperimenter @@ -1710,11 +1710,11 @@

OPL – Optimisation problem library

>=1 - CEC2013 reference code | IOHexperimenter + IOHexperimenter | CEC2013 reference code C++/Python - https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter - Suganthan's reference implementation | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 + IOHprofiler experimenter framework | Suganthan's reference implementation @@ -1876,7 +1876,7 @@

OPL – Optimisation problem library

CFD test problem suite - {'30s', '15m'} + {'15m', '30s'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2696,7 +2696,7 @@

OPL – Optimisation problem library

MECHBench Python - {'7 minutes', '1 minute'} + {'1 minute', '7 minutes'} https://github.com/BayesOptApp/MECHBench Structural mechanics design optimization benchmark diff --git a/docs/problems.html b/docs/problems.html index 92dc2f8..2271e18 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -864,11 +864,11 @@ >=1 - MA-BBOB (IOHexperimenter) | IOHexperimenter + IOHexperimenter | MA-BBOB (IOHexperimenter) C++/Python - https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb | https://github.com/IOHprofiler/IOHexperimenter - Example notebook for MA-BBOB in IOHexperimenter | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb + IOHprofiler experimenter framework | Example notebook for MA-BBOB in IOHexperimenter @@ -1684,11 +1684,11 @@ >=1 - CEC2013 reference code | IOHexperimenter + IOHexperimenter | CEC2013 reference code C++/Python - https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter - Suganthan's reference implementation | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 + IOHprofiler experimenter framework | Suganthan's reference implementation @@ -1850,7 +1850,7 @@ CFD test problem suite - {'30s', '15m'} + {'15m', '30s'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2670,7 +2670,7 @@ MECHBench Python - {'7 minutes', '1 minute'} + {'1 minute', '7 minutes'} https://github.com/BayesOptApp/MECHBench Structural mechanics design optimization benchmark diff --git a/problems.yaml b/problems.yaml index c32939e..bf6ab28 100644 --- a/problems.yaml +++ b/problems.yaml @@ -91,11 +91,11 @@ fn_building_spatial: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous fn_convex_dtlz2: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -142,7 +142,7 @@ fn_emdo: code_examples: null constraints: - equality: null - hard: 'yes' + hard: yes number: null type: box - equality: null @@ -153,28 +153,16 @@ fn_emdo: hard: 'yes' number: 12 type: unknown - description: '# Goal - - Find a design of a synchronous electric motor for power steering systems that - minimizes costs and satisfies all constraints. - - - # Motivation - - Challenging to find good solutions in a limited time. - - - # Key Challenges - - * Time-consuming solution evaluation - - * Highly-constrained problem - - * Constraints are multimodal - - - This is not an available problem, but could be interesting to show to researchers - which difficulties appear in real-world problems.' + - equality: null + hard: yes + number: null + type: box + description: "# Goal\nFind a design of a synchronous electric motor for power steering + systems that minimizes costs and satisfies all constraints.\n\n# Motivation\n\ + Challenging to find good solutions in a limited time.\n\n# Key Challenges\n* Time-consuming + solution evaluation\n* Highly-constrained problem\n* Constraints are multimodal\n\ + \nThis is not an available problem, but could be interesting to show to researchers + which difficulties appear in real-world problems." dynamic_type: null evaluation_time: - 8 minutes @@ -245,8 +233,8 @@ fn_fleetopt: type: problem variables: - dim: - - 13208 - 54 + - 13208 type: integer fn_gasoline: allows_partial_evaluation: null @@ -324,11 +312,11 @@ fn_invdeceptive_deceptive_rotell: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous fn_inverted_dtlz1: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -442,11 +430,11 @@ fn_onemax_sphere_deceptive_rotell: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous fn_onemax_sphere_zeromax_sphere: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -479,11 +467,11 @@ fn_onemax_sphere_zeromax_sphere: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous fn_radar_waveform: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -645,6 +633,10 @@ gen_ealain: tags: null type: generator variables: + - dim: + max: null + min: 1 + type: binary - dim: max: null min: 1 @@ -993,6 +985,10 @@ gen_randoptgen: tags: null type: generator variables: + - dim: + max: null + min: 1 + type: binary - dim: max: null min: 1 @@ -1595,15 +1591,15 @@ suite_amvop: - dim: max: null min: 1 - type: continuous + type: categorical - dim: max: null min: 1 - type: integer + type: continuous - dim: max: null min: 1 - type: categorical + type: integer suite_bbob: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -2286,6 +2282,10 @@ suite_cuter: tags: null type: suite variables: + - dim: + max: null + min: 1 + type: binary - dim: max: null min: 1 @@ -2313,6 +2313,10 @@ suite_cutest: max: null min: 1 type: unknown + - equality: null + hard: yes + number: null + type: box description: CUTEst for optimization software dynamic_type: null evaluation_time: null @@ -2339,6 +2343,10 @@ suite_cutest: tags: null type: suite variables: + - dim: + max: null + min: 1 + type: binary - dim: max: null min: 1 @@ -2560,8 +2568,12 @@ suite_expobench: hard: some number: null type: unknown - description: Wind farm layout optimization, gas filter design, pipe shape optimization, - hyperparameter tuning, and hospital simulation + - equality: null + hard: yes + number: null + type: box + description: Wind farm layout optimization, gas filter design, pipe shape + optimization, hyperparameter tuning, and hospital simulation dynamic_type: null evaluation_time: null fidelity_levels: null @@ -2571,8 +2583,8 @@ suite_expobench: modality: null name: EXPObench noise_type: - - real-life - observational + - real-life objectives: - 1 problems: null @@ -2591,15 +2603,15 @@ suite_expobench: - dim: max: 135 min: 10 - type: integer + type: categorical - dim: max: 135 min: 10 - type: categorical + type: continuous - dim: max: 135 min: 10 - type: continuous + type: integer suite_gbea: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -2808,11 +2820,11 @@ suite_l1_zdt: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous suite_l2_dtlz: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -2884,11 +2896,11 @@ suite_l2_zdt: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous suite_l3_dtlz: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -2960,11 +2972,11 @@ suite_l3_zdt: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous suite_maop: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -3244,10 +3256,10 @@ suite_modact: tags: null type: suite variables: - - dim: 20 - type: integer - dim: 20 type: continuous + - dim: 20 + type: integer suite_morepo: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -3388,11 +3400,11 @@ suite_re: - dim: max: 7 min: 2 - type: integer + type: continuous - dim: max: 7 min: 2 - type: continuous + type: integer suite_rwmvop: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -3429,15 +3441,15 @@ suite_rwmvop: - dim: max: null min: 1 - type: continuous + type: categorical - dim: max: null min: 1 - type: integer + type: continuous - dim: max: null min: 1 - type: categorical + type: integer suite_sbox_cost: allows_partial_evaluation: null can_evaluate_objectives_independently: null @@ -3712,8 +3724,8 @@ suite_zdt: - dim: max: null min: 1 - type: continuous + type: binary - dim: max: null min: 1 - type: binary + type: continuous diff --git a/src/opltools/schema.py b/src/opltools/schema.py index c2e1339..d63cd04 100644 --- a/src/opltools/schema.py +++ b/src/opltools/schema.py @@ -1,12 +1,66 @@ +import json from enum import Enum from typing import Any from typing_extensions import Self -from pydantic import BaseModel, RootModel, ConfigDict, model_validator +from pydantic import ( + BaseModel, + RootModel, + ConfigDict, + SerializerFunctionWrapHandler, + model_serializer, + model_validator, +) from .yesnosome import YesNoSome from .utils import ValueRange, union_range +def _sort_key(item: Any) -> Any: + """Stable, total-order sort key for an already-serialized set member. + + Scalars (str, int, float, bool) sort by their natural ordering, so e.g. + `{2, 10}` sorts numerically rather than as the strings "10" < "2". Dicts + and lists (serialized nested models) have no natural ordering, so those + fall back to a canonical JSON string. + """ + if isinstance(item, (dict, list)): + return json.dumps(item, sort_keys=True, default=str) + return item + + +class _CanonicalMixin: + """Serialize any `set`-valued field as a sorted list. + + `set` iteration order depends on Python's hash randomization, so two + otherwise-identical models can serialize to different YAML on every run + unless we impose a fixed order. Rather than hand-listing every `set` + field on every model, this inspects each model's actual field values at + serialization time and sorts whichever ones happen to be a `set` - + including nested models' own sets (each canonicalizes itself the same + way) and any `extra="allow"` fields. + """ + + @model_serializer(mode="wrap") + def _canonicalize(self, handler: SerializerFunctionWrapHandler) -> Any: + data = handler(self) + values = dict(self.__dict__) + extra = getattr(self, "__pydantic_extra__", None) + if extra: + values.update(extra) + for name, value in values.items(): + if isinstance(value, set) and name in data: + data[name] = sorted(data[name], key=_sort_key) + return data + + +class CanonicalModel(_CanonicalMixin, BaseModel): + pass + + +class CanonicalRootModel(_CanonicalMixin, RootModel): + pass + + class OPLType(Enum): problem = "problem" suite = "suite" @@ -14,7 +68,7 @@ class OPLType(Enum): implementation = "implementation" -class Link(BaseModel): +class Link(CanonicalModel): type: str | None = None url: str @@ -22,12 +76,12 @@ def __hash__(self): return hash(self.type) + hash(self.url) -class Thing(BaseModel): +class Thing(CanonicalModel): type: OPLType model_config = ConfigDict(extra="allow") -class Objectives(RootModel): +class Objectives(CanonicalRootModel): root: int | set[int] | ValueRange = 0 def union(self, other: Self) -> Self: @@ -43,7 +97,7 @@ class VariableType(Enum): unknown = "unknown" -class Variable(BaseModel): +class Variable(CanonicalModel): type: VariableType = VariableType.unknown dim: int | set[int] | ValueRange | None = 0 @@ -62,7 +116,7 @@ class ConstraintType(Enum): unknown = "unknown" -class Constraint(BaseModel): +class Constraint(CanonicalModel): type: ConstraintType = ConstraintType.unknown hard: YesNoSome | None = None equality: YesNoSome | None = None @@ -79,7 +133,7 @@ class ReferenceType(Enum): critique = "critique" -class Reference(BaseModel): +class Reference(CanonicalModel): title: str | None = None authors: list[str] | None = None link: Link | None = None @@ -143,7 +197,7 @@ class Generator(ProblemLike): type: OPLType = OPLType.generator -class Library(RootModel): +class Library(CanonicalRootModel): root: dict[str, Problem | Generator | Suite | Implementation] = {} def _check_id_references(self, ids, type: OPLType) -> None: From 53ad486eb4a22eff676523022d9cfa8c593f7b76 Mon Sep 17 00:00:00 2001 From: olafmersmann <2335934+olafmersmann@users.noreply.github.com> Date: Mon, 2 Nov 2026 12:11:35 +0000 Subject: [PATCH 2/3] chore: Auto-update HTML files --- docs/index.html | 24 ++++++++++++------------ docs/problems.html | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/index.html b/docs/index.html index 80ebe5d..2e5c578 100644 --- a/docs/index.html +++ b/docs/index.html @@ -890,11 +890,11 @@

OPL – Optimisation problem library

>=1 - IOHexperimenter | MA-BBOB (IOHexperimenter) + MA-BBOB (IOHexperimenter) | IOHexperimenter C++/Python - https://github.com/IOHprofiler/IOHexperimenter | https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb - IOHprofiler experimenter framework | Example notebook for MA-BBOB in IOHexperimenter + https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb | https://github.com/IOHprofiler/IOHexperimenter + Example notebook for MA-BBOB in IOHexperimenter | IOHprofiler experimenter framework @@ -1710,11 +1710,11 @@

OPL – Optimisation problem library

>=1 - IOHexperimenter | CEC2013 reference code + CEC2013 reference code | IOHexperimenter C++/Python - https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 - IOHprofiler experimenter framework | Suganthan's reference implementation + https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter + Suganthan's reference implementation | IOHprofiler experimenter framework @@ -1876,7 +1876,7 @@

OPL – Optimisation problem library

CFD test problem suite - {'15m', '30s'} + {'30s', '15m'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2696,7 +2696,7 @@

OPL – Optimisation problem library

MECHBench Python - {'1 minute', '7 minutes'} + {'7 minutes', '1 minute'} https://github.com/BayesOptApp/MECHBench Structural mechanics design optimization benchmark @@ -2899,11 +2899,11 @@

OPL – Optimisation problem library

20 20 - pymoo | modact + modact | pymoo Python {'20ms'} - https://github.com/anyoptimization/pymoo | https://github.com/epfl-lamd/modact - Multi-objective optimization in Python | EPFL-LAMD modact package + https://github.com/epfl-lamd/modact | https://github.com/anyoptimization/pymoo + EPFL-LAMD modact package | Multi-objective optimization in Python @@ -3270,7 +3270,7 @@

OPL – Optimisation problem library

TulipaEnergyModel.jl Julia / JuMP - {'hours', 'minutes'} + {'minutes', 'hours'} https://tulipaenergy.github.io/TulipaEnergyModel.jl/stable/ | https://github.com/TulipaEnergy/Tulipa-OBZ-CaseStudy Large linear program for optimal investment and operation of energy systems diff --git a/docs/problems.html b/docs/problems.html index 2271e18..a5d7865 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -864,11 +864,11 @@ >=1 - IOHexperimenter | MA-BBOB (IOHexperimenter) + MA-BBOB (IOHexperimenter) | IOHexperimenter C++/Python - https://github.com/IOHprofiler/IOHexperimenter | https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb - IOHprofiler experimenter framework | Example notebook for MA-BBOB in IOHexperimenter + https://github.com/IOHprofiler/IOHexperimenter/blob/master/example/Competitions/MA-BBOB/Example_MABBOB.ipynb | https://github.com/IOHprofiler/IOHexperimenter + Example notebook for MA-BBOB in IOHexperimenter | IOHprofiler experimenter framework @@ -1684,11 +1684,11 @@ >=1 - IOHexperimenter | CEC2013 reference code + CEC2013 reference code | IOHexperimenter C++/Python - https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 - IOHprofiler experimenter framework | Suganthan's reference implementation + https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter + Suganthan's reference implementation | IOHprofiler experimenter framework @@ -1850,7 +1850,7 @@ CFD test problem suite - {'15m', '30s'} + {'30s', '15m'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2670,7 +2670,7 @@ MECHBench Python - {'1 minute', '7 minutes'} + {'7 minutes', '1 minute'} https://github.com/BayesOptApp/MECHBench Structural mechanics design optimization benchmark @@ -2873,11 +2873,11 @@ 20 20 - pymoo | modact + modact | pymoo Python {'20ms'} - https://github.com/anyoptimization/pymoo | https://github.com/epfl-lamd/modact - Multi-objective optimization in Python | EPFL-LAMD modact package + https://github.com/epfl-lamd/modact | https://github.com/anyoptimization/pymoo + EPFL-LAMD modact package | Multi-objective optimization in Python @@ -3244,7 +3244,7 @@ TulipaEnergyModel.jl Julia / JuMP - {'hours', 'minutes'} + {'minutes', 'hours'} https://tulipaenergy.github.io/TulipaEnergyModel.jl/stable/ | https://github.com/TulipaEnergy/Tulipa-OBZ-CaseStudy Large linear program for optimal investment and operation of energy systems From 803f4be265a813eea2c33d4d67690d1bd5b3acb2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 12:12:49 +0000 Subject: [PATCH 3/3] chore: Auto-update HTML files --- docs/index.html | 12 ++++++------ docs/problems.html | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/index.html b/docs/index.html index 2e5c578..b30c225 100644 --- a/docs/index.html +++ b/docs/index.html @@ -154,7 +154,7 @@

OPL – Optimisation problem library

BSO-toolbox C++ - {'40 seconds', '1 second'} + {'1 second', '40 seconds'} https://github.com/TUe-excellent-buildings/BSO-toolbox Building Spatial Design toolbox (TU/e) @@ -1710,11 +1710,11 @@

OPL – Optimisation problem library

>=1 - CEC2013 reference code | IOHexperimenter + IOHexperimenter | CEC2013 reference code C++/Python - https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter - Suganthan's reference implementation | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 + IOHprofiler experimenter framework | Suganthan's reference implementation @@ -1876,7 +1876,7 @@

OPL – Optimisation problem library

CFD test problem suite - {'30s', '15m'} + {'15m', '30s'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2245,7 +2245,7 @@

OPL – Optimisation problem library

coco-gbea - {'5 seconds', '34 seconds'} + {'34 seconds', '5 seconds'} https://github.com/ttusar/coco-gbea Game-Benchmark for Evolutionary Algorithms (COCO fork) diff --git a/docs/problems.html b/docs/problems.html index a5d7865..6d9020b 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -128,7 +128,7 @@ BSO-toolbox C++ - {'40 seconds', '1 second'} + {'1 second', '40 seconds'} https://github.com/TUe-excellent-buildings/BSO-toolbox Building Spatial Design toolbox (TU/e) @@ -1684,11 +1684,11 @@ >=1 - CEC2013 reference code | IOHexperimenter + IOHexperimenter | CEC2013 reference code C++/Python - https://github.com/P-N-Suganthan/CEC2013 | https://github.com/IOHprofiler/IOHexperimenter - Suganthan's reference implementation | IOHprofiler experimenter framework + https://github.com/IOHprofiler/IOHexperimenter | https://github.com/P-N-Suganthan/CEC2013 + IOHprofiler experimenter framework | Suganthan's reference implementation @@ -1850,7 +1850,7 @@ CFD test problem suite - {'30s', '15m'} + {'15m', '30s'} https://bitbucket.org/arahat/cfd-test-problem-suite Expensive real-world CFD-based test problems @@ -2219,7 +2219,7 @@ coco-gbea - {'5 seconds', '34 seconds'} + {'34 seconds', '5 seconds'} https://github.com/ttusar/coco-gbea Game-Benchmark for Evolutionary Algorithms (COCO fork)