diff --git a/docs/index.html b/docs/index.html
index e4b4df0..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) |
|
@@ -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 92dc2f8..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) |
|
@@ -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 |
|
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: