Skip to content

Commit 014d004

Browse files
committed
doc: Add documentation for new schema
Rudimentary design document and reference for the new schema that attempts to summarize the discussion at the OPL hackathon in April. The core structure was written by the author, then fleshed out using Claude and finally reviewed by again the author. Co-Authored-By: Claude Opus 4.7
1 parent 0d4225c commit 014d004

1 file changed

Lines changed: 301 additions & 0 deletions

File tree

SCHEMA.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# OPL Schema
2+
3+
The OPL schema catalogs optimization **problems**, **suites**, **generators**, and their **implementations** in a single, machine-readable format.
4+
5+
Three design choices shape everything below:
6+
7+
1. **One flat library, keyed by ID.**
8+
Every entity lives in a `Library` dict.
9+
Suites reference problems, problems reference implementations using their respective ID.
10+
There is no embedding of problems or implementations within suites to facilitate reuse.
11+
F.e. an implementation might be referenced by multiple problems or suites.
12+
2. **Numeric fields accept a scalar, a set, or a range.**
13+
A problem may have exactly `2` objectives, one of `{2, 3, 5}`, or any value in `{min: 2, max: 50}`.
14+
The same union type is used for variable dimensions and constraint counts.
15+
3. **Three-valued logic for yes/no fields.**
16+
Many boolean fields (f.e. `hard`, `allows_partial_evaluation`, ...) [`YesNoSome`](#yesnosome) as their value.
17+
We lose some expressive power but simplify the data entry.
18+
If we force authors to decide on yes or no, then we would need more complex structures for variables, constraints etc. and that would make the usual case unnecessarily complex.
19+
20+
## Contents
21+
22+
- [Library](#library)
23+
- [Thing types](#thing-types)
24+
- [Implementation](#implementation)
25+
- [ProblemLike](#problemlike) (shared fields)
26+
- [Problem](#problem)
27+
- [Suite](#suite)
28+
- [Generator](#generator)
29+
- [Shared building blocks](#shared-building-blocks)
30+
- [Variable](#variable) / [VariableType](#variabletype)
31+
- [Constraint](#constraint) / [ConstraintType](#constrainttype)
32+
- [Reference](#reference) / [Link](#link)
33+
- [ValueRange](#valuerange)
34+
- [YesNoSome](#yesnosome)
35+
36+
---
37+
38+
## Library
39+
40+
A `Library` is a dict from ID to a [Thing](#thing-types).
41+
IDs are free-form but must be unique and the convention is to add a prefix marking the type to avoid collisions:
42+
43+
| Prefix | Type |
44+
|---------|------------------|
45+
| `impl_` | Implementation |
46+
| `fn_` | Problem |
47+
| `suite_`| Suite |
48+
| `gen_` | Generator |
49+
50+
On load the library validates that every ID referenced by a suite (`problems`) or problem (`implementations`) exists and has the correct type. Suites also have their `fidelity_levels` auto-populated from their problems.
51+
52+
```yaml
53+
impl_coco:
54+
type: implementation
55+
name: COCO
56+
description: Comparing Continuous Optimisers
57+
fn_sphere:
58+
type: problem
59+
name: Sphere
60+
objectives: [1]
61+
implementations: [impl_coco]
62+
suite_bbob:
63+
type: suite
64+
name: BBOB
65+
problems: [fn_sphere]
66+
```
67+
68+
---
69+
70+
## Thing types
71+
72+
All entities inherit from `Thing`, which only carries a discriminator:
73+
74+
```yaml
75+
type: problem # or: suite | generator | implementation
76+
```
77+
78+
We want to have as flat a structure as possible to make exploring and searching OPL as easy as possible.
79+
That's one of the reasonst the top level object is a dictionary of dissimilar things.
80+
But we need to be able to tell them apart so we have a `type` field to discriminate between them.
81+
82+
### Implementation
83+
84+
A pointer to code that implements one or more problems.
85+
Intentionally minimal so that the schema describes *what* a problem is, not how to run it.
86+
There are separate files which contain curated usage examples for problems or suites keyed by their respective IDs.
87+
88+
| Field | Type | Notes |
89+
|-------------------|-----------------------------------|----------------------------------------------|
90+
| `name` | str | required |
91+
| `description` | str | required |
92+
| `language` | str? (e.g. `python`, `c`) | |
93+
| `links` | list of [Link](#link)? | repo, release, docs… |
94+
| `evaluation_time` | str? | free-form ("8 minutes", "fast") |
95+
| `requirements` | str or list of str? | URL to requirements file or list of packages |
96+
97+
```yaml
98+
impl_coco:
99+
type: implementation
100+
name: COCO
101+
description: Comparing Continuous Optimisers benchmarking platform
102+
language: c
103+
links:
104+
- {type: repository, url: https://github.com/numbbo/coco-experiment}
105+
impl_py_cocoex:
106+
type: implementation
107+
name: Python bindings for COCO
108+
description: The Python bindings for the experimental part of the COCO framework
109+
language: Python
110+
links:
111+
- {type: source, url: https://github.com/numbbo/coco-experiment/tree/main/build/python}
112+
- {type: package, url: https://pypi.org/project/coco-experiment/}
113+
```
114+
115+
### ProblemLike
116+
117+
Fields shared by [Problem](#problem), [Suite](#suite), and [Generator](#generator).
118+
The schema deliberately puts most descriptive fields here so suites can be characterised without explicitly having to add all problems in the suite.
119+
120+
| Field | Type | Notes |
121+
|------------------------------------------|------------------------------------------------|----------------------------------------------------|
122+
| `name` | str | required |
123+
| `long_name` | str? | |
124+
| `description` | str? (markdown) | longer prose |
125+
| `tags` | set of str? | free-form keywords |
126+
| `references` | set of [Reference](#reference)? | |
127+
| `implementations` | set of IDs? | must resolve to [Implementation](#implementation)s |
128+
| `objectives` | set of int? | e.g. `{1}`, `{2, 3}` — **not** a ValueRange |
129+
| `variables` | set of [Variable](#variable)? | |
130+
| `constraints` | set of [Constraint](#constraint)? | omit entirely for unconstrained |
131+
| `dynamic_type` | set of str? | `{"no"}`, `{"time-varying"}`… |
132+
| `noise_type` | set of str? | `{"none"}`, `{"gaussian"}`… |
133+
| `allows_partial_evaluation` | [YesNoSome](#yesnosome)? | |
134+
| `can_evaluate_objectives_independently` | [YesNoSome](#yesnosome)? | |
135+
| `modality` | set of str? | `{"unimodal"}`, `{"multimodal"}` |
136+
| `fidelity_levels` | set of int? | `{1}` = single-fidelity, `{1,2}` = multi-fidelity |
137+
| `code_examples` | set of str? | paths to example scripts |
138+
| `source` | set of str? | `{"artificial"}`, `{"real-world"}` |
139+
140+
> `objectives` is a set of integers because we don't assume extreme scalability in this property so explicit enumeration is fine.
141+
> Dimensions of variables on the other hand are ranges because here problems often are scalable over wide ranges.
142+
143+
### Problem
144+
145+
One optimization problem (possibly parameterised by instances).
146+
147+
Adds:
148+
149+
| Field | Type | Notes |
150+
|-------------|--------------------------------------------|--------------------------------------------|
151+
| `instances` | [ValueRange](#valuerange) or list of str? | e.g. `{min: 1, max: 15}` or named variants |
152+
153+
```yaml
154+
fn_sphere:
155+
type: problem
156+
name: Sphere
157+
objectives: [1]
158+
variables: [{type: continuous, dim: {min: 2, max: 40}}]
159+
modality: [unimodal]
160+
source: [artificial]
161+
instances: {min: 1, max: 15}
162+
implementations: [impl_coco]
163+
```
164+
165+
### Suite
166+
167+
A curated, fixed collection of problems.
168+
169+
Adds:
170+
171+
| Field | Type | Notes |
172+
|------------|--------------|-----------------------------------------------|
173+
| `problems` | set of IDs? | must resolve to [Problem](#problem)s |
174+
175+
`fidelity_levels` is auto-unioned from member problems at validation time.
176+
177+
```yaml
178+
suite_bbob:
179+
type: suite
180+
name: BBOB
181+
problems: [fn_sphere, fn_rosenbrock, fn_rastrigin]
182+
objectives: [1]
183+
source: [artificial]
184+
implementations: [impl_coco]
185+
```
186+
187+
### Generator
188+
189+
A parametric family of problems — unlike a [Suite](#suite), the member problems are not enumerated. Uses the same fields as [ProblemLike](#problemlike) with no additions; the distinction from [Problem](#problem) is that a generator produces instances on demand.
190+
191+
```yaml
192+
gen_mpm2:
193+
type: generator
194+
name: MPM2
195+
description: Multiple peaks model, second instantiation
196+
objectives: [1]
197+
variables: [{type: continuous, dim: {min: 1}}]
198+
modality: [multimodal]
199+
```
200+
201+
---
202+
203+
## Shared building blocks
204+
205+
### Variable
206+
207+
A group of decision variables of the same type.
208+
Multi-type problems list multiple entries.
209+
While you can have multiple entries of the same type, this should be justified in some way like when you can evaluate the problem on only one subset of variables.
210+
211+
| Field | Type | Default |
212+
|--------|-----------------------------------------------|----------------------|
213+
| `type` | [VariableType](#variabletype) | `unknown` |
214+
| `dim` | int, set of int, [ValueRange](#valuerange), or null | `0` |
215+
216+
```yaml
217+
variables:
218+
- {type: continuous, dim: 10}
219+
- {type: integer, dim: {min: 1, max: 5}}
220+
```
221+
222+
### VariableType
223+
224+
`continuous | integer | binary | categorical | unknown`.
225+
Use `unknown` for permutation/combinatorial problems the schema doesn't yet distinguish **and** add an appropriate tag.
226+
We are actively watching for unknown variable types and are open to extending the above list if there is a critical mass of problems to justify it.
227+
228+
### Constraint
229+
230+
A group of constraints.
231+
To indicate that the problem is unconstrained, you need an _empty_ `constraints` field.
232+
A missing `constraints` field or if it is set to `null` means it is not known if unconstrained.
233+
234+
| Field | Type | Notes |
235+
|------------|-----------------------------------------------|------------------------------------|
236+
| `type` | [ConstraintType](#constrainttype) | default `unknown` |
237+
| `hard` | [YesNoSome](#yesnosome)? | hard vs. soft |
238+
| `equality` | [YesNoSome](#yesnosome)? | equality vs. inequality |
239+
| `number` | int, set of int, [ValueRange](#valuerange), null | |
240+
241+
```yaml
242+
constraints:
243+
- {type: box, hard: yes, number: 10}
244+
- {type: linear, hard: some, equality: no, number: {min: 1}}
245+
```
246+
247+
### ConstraintType
248+
249+
`box | linear | function | unknown`. `function` covers non-linear/black-box constraints.
250+
251+
### Reference
252+
253+
Bibliographic pointer.
254+
Requires a `title` and `authors` and optionally includes a `link` to the material.
255+
256+
```yaml
257+
references:
258+
- title: "Honey Badger Algorithm: New metaheuristic algorithm for solving optimization problems."
259+
authors:
260+
- Fatma A. Hashim
261+
- Essam H. Houssein
262+
- Kashif Hussain
263+
- Mai S. Mabrouk
264+
- Walid Al-Atabany
265+
link: {type: doi, url: "https://doi.org/10.1016/j.matcom.2021.08.013"]
266+
```
267+
268+
### Link
269+
270+
`{type?: str, url: str}`.
271+
`type` is free-form (`repository`, `arxiv`, `paper`, `doi`, ...).
272+
`url` is a URL to some resource.
273+
274+
If `type` is `doi`, please use the full URL (starting with `https://doi.org/...`) instead of the raw DOI.
275+
276+
### ValueRange
277+
278+
An inclusive numeric range type.
279+
At least one of `min`/`max` must be given.
280+
If `min` is given and `max` is missing, it does not imply that there is no upper bound.
281+
There might be one, it is just not known.
282+
The same applies for the case where `max` is given and `min` is missing.
283+
284+
```yaml
285+
dim: {min: 2} # 2 or more
286+
dim: {min: 2, max: 40} # between 2 and 40
287+
dim: {max: 100} # up to 100
288+
```
289+
290+
Used by `Variable.dim`, `Constraint.number`, `Problem.instances`.
291+
292+
### YesNoSome
293+
294+
Three-valued flag: `yes | no | some | ?` (the last serialises as the literal `'?'` string, meaning unknown).
295+
`some` captures the common case where *part* of something has some property.
296+
For example only some constraints might hard but we don't know the exact number of hard and soft constraints, only the total number.
297+
298+
```yaml
299+
constraints: [{type: box, hard: some}]
300+
allows_partial_evaluation: "?"
301+
```

0 commit comments

Comments
 (0)