Skip to content

Commit fae5616

Browse files
authored
Make the BoFire Data Models OpenAI compatible (#495)
* tuples to lists * fix tests * fix linting issues
1 parent 9c5acca commit fae5616

File tree

14 files changed

+71
-74
lines changed

14 files changed

+71
-74
lines changed

bofire/benchmarks/detergent.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ def __init__(self):
5757

5858
self._domain = Domain.from_lists(
5959
inputs=[
60-
ContinuousInput(key="x1", bounds=(0.0, 0.2)),
61-
ContinuousInput(key="x2", bounds=(0.0, 0.3)),
62-
ContinuousInput(key="x3", bounds=(0.02, 0.2)),
63-
ContinuousInput(key="x4", bounds=(0.0, 0.06)),
64-
ContinuousInput(key="x5", bounds=(0.0, 0.04)),
60+
ContinuousInput(key="x1", bounds=[0.0, 0.2]),
61+
ContinuousInput(key="x2", bounds=[0.0, 0.3]),
62+
ContinuousInput(key="x3", bounds=[0.02, 0.2]),
63+
ContinuousInput(key="x4", bounds=[0.0, 0.06]),
64+
ContinuousInput(key="x5", bounds=[0.0, 0.04]),
6565
],
6666
outputs=[ContinuousOutput(key=f"y{i+1}") for i in range(5)],
6767
constraints=[

bofire/benchmarks/multi.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def __init__(self, dim: PositiveInt, num_objectives: PositiveInt = 2, **kwargs):
5454

5555
inputs = []
5656
for i in range(self.dim):
57-
inputs.append(ContinuousInput(key="x_%i" % (i), bounds=(0, 1)))
57+
inputs.append(ContinuousInput(key="x_%i" % (i), bounds=[0, 1]))
5858
outputs = []
5959
self.k = self.dim - self.num_objectives + 1
6060
for i in range(self.num_objectives):
@@ -140,8 +140,8 @@ def __init__(self, constraints: bool = True, **kwargs):
140140
self._domain = Domain(
141141
inputs=Inputs(
142142
features=[
143-
ContinuousInput(key="x1", bounds=(0, 5)),
144-
ContinuousInput(key="x2", bounds=(0, 3)),
143+
ContinuousInput(key="x1", bounds=[0, 5]),
144+
ContinuousInput(key="x2", bounds=[0, 3]),
145145
],
146146
),
147147
outputs=Outputs(
@@ -187,8 +187,8 @@ def __init__(self, **kwargs):
187187
self._domain = Domain(
188188
inputs=Inputs(
189189
features=[
190-
ContinuousInput(key="x1", bounds=(0, math.pi)),
191-
ContinuousInput(key="x2", bounds=(0, math.pi)),
190+
ContinuousInput(key="x1", bounds=[0, math.pi]),
191+
ContinuousInput(key="x2", bounds=[0, math.pi]),
192192
],
193193
),
194194
outputs=Outputs(
@@ -293,13 +293,13 @@ def __init__(self, C_i: Optional[np.ndarray] = None, **kwargs):
293293
# Decision variables
294294
# "residence time in minutes"
295295
inputs = [
296-
ContinuousInput(key="tau", bounds=(0.5, 2)),
296+
ContinuousInput(key="tau", bounds=[0.5, 2]),
297297
# "equivalents of pyrrolidine"
298-
ContinuousInput(key="equiv_pldn", bounds=(1, 5)),
298+
ContinuousInput(key="equiv_pldn", bounds=[1, 5]),
299299
# "concentration of 2,4 dinitrofluorobenenze at reactor inlet (after mixing) in M"
300-
ContinuousInput(key="conc_dfnb", bounds=(0.1, 0.5)),
300+
ContinuousInput(key="conc_dfnb", bounds=[0.1, 0.5]),
301301
# "Reactor temperature in degrees celsius"
302-
ContinuousInput(key="temperature", bounds=(30, 120)),
302+
ContinuousInput(key="temperature", bounds=[30, 120]),
303303
]
304304
# Objectives
305305
# "space time yield (kg/m^3/h)"
@@ -442,7 +442,7 @@ def __init__(self, n_inputs=30, **kwargs):
442442
super().__init__(**kwargs)
443443
self.n_inputs = n_inputs
444444
inputs = [
445-
ContinuousInput(key=f"x{i+1}", bounds=(0, 1)) for i in range(n_inputs)
445+
ContinuousInput(key=f"x{i+1}", bounds=[0, 1]) for i in range(n_inputs)
446446
]
447447
inputs = Inputs(features=inputs)
448448
outputs = [
@@ -550,11 +550,11 @@ def __init__(
550550
],
551551
),
552552
# "base equivalents"
553-
ContinuousInput(key="base_eq", bounds=(1, 2.5)),
553+
ContinuousInput(key="base_eq", bounds=[1, 2.5]),
554554
# "Reactor temperature in degrees celsius"
555-
ContinuousInput(key="temperature", bounds=(30, 100)),
555+
ContinuousInput(key="temperature", bounds=[30, 100]),
556556
# "residence time in seconds (s)"
557-
ContinuousInput(key="t_res", bounds=(60, 1800)),
557+
ContinuousInput(key="t_res", bounds=[60, 1800]),
558558
]
559559

560560
input_preprocessing_specs = {
@@ -566,11 +566,11 @@ def __init__(
566566
outputs = [
567567
ContinuousOutput(
568568
key="yield",
569-
objective=MaximizeObjective(w=1.0, bounds=(0.0, 1.0)),
569+
objective=MaximizeObjective(w=1.0, bounds=[0.0, 1.0]),
570570
),
571571
ContinuousOutput(
572572
key="cost",
573-
objective=MinimizeObjective(w=1.0, bounds=(0.0, 1.0)),
573+
objective=MinimizeObjective(w=1.0, bounds=[0.0, 1.0]),
574574
),
575575
]
576576
self.ref_point = {"yield": 0.0, "cost": 1.0}

bofire/benchmarks/single.py

+23-27
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def __init__(
110110
# continuous input features
111111
for d in range(self.dim):
112112
input_feature_list.append(
113-
ContinuousInput(key=f"x_{d+1}", bounds=(self.lower, self.upper)),
113+
ContinuousInput(key=f"x_{d+1}", bounds=[self.lower, self.upper]),
114114
)
115115

116116
# Objective
@@ -179,7 +179,7 @@ def __init__(self, dim: int = 6, allowed_k: Optional[int] = None, **kwargs) -> N
179179
self._domain = Domain(
180180
inputs=Inputs(
181181
features=[
182-
ContinuousInput(key=f"x_{i}", bounds=(0, 1)) for i in range(dim)
182+
ContinuousInput(key=f"x_{i}", bounds=[0, 1]) for i in range(dim)
183183
],
184184
),
185185
outputs=Outputs(
@@ -235,7 +235,7 @@ def __init__(self, dim: int = 6, allowed_k: Optional[int] = None, **kwargs) -> N
235235
self._domain = Domain(
236236
inputs=Inputs(
237237
features=[
238-
ContinuousInput(key=f"x_{i}", bounds=(0, 1)) for i in range(dim)
238+
ContinuousInput(key=f"x_{i}", bounds=[0, 1]) for i in range(dim)
239239
]
240240
),
241241
outputs=Outputs(
@@ -291,27 +291,23 @@ def __init__(self, locality_factor: Optional[float] = None, **kwargs) -> None:
291291
features=[
292292
ContinuousInput(
293293
key="x_1",
294-
bounds=(-5.0, 10),
295-
local_relative_bounds=(
296-
(
297-
0.5 * locality_factor,
298-
0.5 * locality_factor,
299-
)
300-
if locality_factor is not None
301-
else None
302-
),
294+
bounds=[-5.0, 10],
295+
local_relative_bounds=[
296+
0.5 * locality_factor,
297+
0.5 * locality_factor,
298+
]
299+
if locality_factor is not None
300+
else None,
303301
),
304302
ContinuousInput(
305303
key="x_2",
306-
bounds=(0.0, 15.0),
307-
local_relative_bounds=(
308-
(
309-
1.5 * locality_factor,
310-
1.5 * locality_factor,
311-
)
312-
if locality_factor is not None
313-
else None
314-
),
304+
bounds=[0.0, 15.0],
305+
local_relative_bounds=[
306+
1.5 * locality_factor,
307+
1.5 * locality_factor,
308+
]
309+
if locality_factor is not None
310+
else None,
315311
),
316312
],
317313
),
@@ -355,7 +351,7 @@ def __init__(self, **kwargs) -> None:
355351
self._domain = Domain(
356352
inputs=Inputs(
357353
features=[
358-
ContinuousInput(key=f"x_{i+1:02d}", bounds=(0, 1))
354+
ContinuousInput(key=f"x_{i+1:02d}", bounds=[0, 1])
359355
for i in range(30)
360356
],
361357
),
@@ -398,8 +394,8 @@ def __init__(self, use_constraints: bool = False, **kwargs):
398394
self.use_constraints = use_constraints
399395
inputs = []
400396

401-
inputs.append(ContinuousInput(key="x_1", bounds=(-6, 6)))
402-
inputs.append(ContinuousInput(key="x_2", bounds=(-6, 6)))
397+
inputs.append(ContinuousInput(key="x_1", bounds=[-6, 6]))
398+
inputs.append(ContinuousInput(key="x_2", bounds=[-6, 6]))
403399

404400
objective = MinimizeObjective(w=1.0)
405401
output_feature = ContinuousOutput(key="y", objective=objective)
@@ -472,8 +468,8 @@ def __init__(self, use_constraints: bool = False, **kwargs):
472468
inputs = []
473469

474470
inputs.append(TaskInput(key="task_id", categories=["task_1", "task_2"]))
475-
inputs.append(ContinuousInput(key="x_1", bounds=(-6, 6)))
476-
inputs.append(ContinuousInput(key="x_2", bounds=(-6, 6)))
471+
inputs.append(ContinuousInput(key="x_1", bounds=[-6, 6]))
472+
inputs.append(ContinuousInput(key="x_2", bounds=[-6, 6]))
477473

478474
objective = MinimizeObjective(w=1.0)
479475
output_feature = ContinuousOutput(key="y", objective=objective)
@@ -645,7 +641,7 @@ def __init__(
645641
self._domain = Domain(
646642
inputs=Inputs(
647643
features=[
648-
ContinuousInput(key=f"x_{i}", bounds=(0, 1))
644+
ContinuousInput(key=f"x_{i}", bounds=[0, 1])
649645
for i in range(self.dim)
650646
],
651647
),

bofire/data_models/features/continuous.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class ContinuousInput(NumericalInput):
2727

2828
bounds: Bounds
2929
local_relative_bounds: Optional[
30-
Tuple[Annotated[float, Field(gt=0)], Annotated[float, Field(gt=0)]]
30+
Annotated[List[Annotated[float, Field(gt=0)]], Field(min_items=2, max_items=2)] # type: ignore
3131
] = None
3232
stepsize: Optional[float] = None
3333

bofire/data_models/objectives/identity.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from typing import Literal, Optional, Tuple, Union
1+
from typing import Literal, Optional, Union
22

33
import numpy as np
44
import pandas as pd
55
from pydantic import field_validator
66

77
from bofire.data_models.objectives.objective import Objective, TWeight
8+
from bofire.data_models.types import Bounds
89

910

1011
class IdentityObjective(Objective):
@@ -17,9 +18,9 @@ class IdentityObjective(Objective):
1718
1819
"""
1920

20-
type: Literal["IdentityObjective"] = "IdentityObjective"
21+
type: Literal["IdentityObjective"] = "IdentityObjective" # type: ignore
2122
w: TWeight = 1
22-
bounds: Tuple[float, float] = (0, 1)
23+
bounds: Bounds = [0, 1]
2324

2425
@property
2526
def lower_bound(self) -> float:

bofire/data_models/types.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import Sequence
2-
from typing import Annotated, Dict, List, Tuple, Union
2+
from typing import Annotated, Dict, List, Union
33

44
from pydantic import AfterValidator, Field, PositiveInt
55

@@ -92,7 +92,8 @@ def validate_monotonically_increasing(sequence: Sequence[float]) -> Sequence[flo
9292
]
9393

9494
Bounds = Annotated[
95-
Tuple[float, float],
95+
List[float],
96+
Field(min_length=2, max_length=2),
9697
AfterValidator(validate_monotonically_increasing),
9798
]
9899

bofire/strategies/doe/objective.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def __init__(
4040
else:
4141
self.transform = MinMaxTransform(
4242
inputs=self.domain.inputs,
43-
feature_range=transform_range,
43+
feature_range=tuple(transform_range), # type: ignore
4444
)
4545

4646
self.n_experiments = n_experiments

bofire/strategies/doe/utils_categorical_discrete.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def NChooseKGroup_with_quantity(
255255
# if we use_legacy_class is true this constraint will be added by the discrete_to_relaxable_domain_mapper function
256256
pick_exactly_one_of_group_const = []
257257
else:
258-
category = [ContinuousInput(key=k, bounds=(0, 1)) for k in keys]
258+
category = [ContinuousInput(key=k, bounds=[0, 1]) for k in keys]
259259
pick_exactly_one_of_group_const = [
260260
LinearEqualityConstraint(
261261
features=list(keys),
@@ -293,7 +293,7 @@ def _generate_quantity_var_constr(
293293
quantity_var = [
294294
ContinuousInput(
295295
key=unique_group_identifier + "_" + k + "_quantity",
296-
bounds=(0, q[1]),
296+
bounds=[0, q[1]],
297297
)
298298
for k, q in zip(keys, quantity_if_picked)
299299
]
@@ -469,7 +469,7 @@ def NChooseKGroup(
469469
# adding the new possible combinations to the list of keys
470470
keys = combined_keys
471471

472-
category = [ContinuousInput(key=k, bounds=(0, 1)) for k in keys]
472+
category = [ContinuousInput(key=k, bounds=[0, 1]) for k in keys]
473473
pick_exactly_one_of_group_const = [
474474
LinearEqualityConstraint(
475475
features=list(keys),
@@ -489,7 +489,7 @@ def NChooseKGroup(
489489
def generate_mixture_constraints(
490490
keys: List[str],
491491
) -> Tuple[LinearEqualityConstraint, List[ContinuousInput]]:
492-
binary_vars = (ContinuousInput(key=x, bounds=(0, 1)) for x in keys)
492+
binary_vars = (ContinuousInput(key=x, bounds=[0, 1]) for x in keys)
493493

494494
mixture_constraint = LinearEqualityConstraint(
495495
features=keys,

bofire/strategies/random.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def _sample_with_nchooseks(
147147
for key in u:
148148
feat = domain.inputs.get_by_key(key=key)
149149
assert isinstance(feat, ContinuousInput)
150-
feat.bounds = (0, 0)
150+
feat.bounds = [0.0, 0.0]
151151
# setup then sampler for this situation
152152
samples.append(
153153
self._sample_from_polytope(

bofire/utils/reduce.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ def remove_eliminated_inputs(domain: Domain, transform: AffineTransform) -> Doma
347347
feat: ContinuousInput = ContinuousInput(
348348
**domain.inputs.get_by_key(_features[0]).model_dump(),
349349
)
350-
feat.bounds = (_coefficients[0], _coefficients[0])
350+
feat.bounds = [_coefficients[0], _coefficients[0]]
351351
totally_removed = True
352352
elif len(_features) > 1:
353353
_c = LinearInequalityConstraint(
@@ -430,6 +430,6 @@ def adjust_boundary(feature: ContinuousInput, coef: float, rhs: float):
430430
boundary = rhs / coef
431431
if coef > 0:
432432
if boundary > feature.lower_bound:
433-
feature.bounds = (boundary, feature.upper_bound)
433+
feature.bounds = [boundary, feature.upper_bound]
434434
elif boundary < feature.upper_bound:
435-
feature.bounds = (feature.lower_bound, boundary)
435+
feature.bounds = [feature.lower_bound, boundary]

tests/bofire/data_models/specs/features.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import math
21
import random
32
import uuid
43

@@ -40,16 +39,16 @@
4039
features.ContinuousInput,
4140
lambda: {
4241
"key": str(uuid.uuid4()),
43-
"bounds": (3, 5.3),
42+
"bounds": [3, 5.3],
4443
"unit": random.choice(["°C", "mg", "mmol/l", None]),
45-
"local_relative_bounds": (math.inf, math.inf),
44+
"local_relative_bounds": None,
4645
"stepsize": None,
4746
},
4847
)
4948

5049
specs.add_invalid(
5150
features.ContinuousInput,
52-
lambda: {"key": "a", "bounds": (5, 3)},
51+
lambda: {"key": "a", "bounds": [5, 3]},
5352
error=ValueError,
5453
message="Sequence is not monotonically increasing.",
5554
)
@@ -58,11 +57,11 @@
5857
features.ContinuousDescriptorInput,
5958
lambda: {
6059
"key": str(uuid.uuid4()),
61-
"bounds": (3, 5.3),
60+
"bounds": [3, 5.3],
6261
"descriptors": ["d1", "d2"],
6362
"values": [1.0, 2.0],
6463
"unit": random.choice(["°C", "mg", "mmol/l", None]),
65-
"local_relative_bounds": (math.inf, math.inf),
64+
"local_relative_bounds": None,
6665
"stepsize": None,
6766
},
6867
)

tests/bofire/data_models/specs/objectives.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515
specs.add_valid(
1616
objectives.MaximizeObjective,
17-
lambda: {"w": 1.0, "bounds": (0.1, 0.9)},
17+
lambda: {"w": 1.0, "bounds": [0.1, 0.9]},
1818
)
1919
specs.add_valid(
2020
objectives.MaximizeSigmoidObjective,
@@ -26,7 +26,7 @@
2626
)
2727
specs.add_valid(
2828
objectives.MinimizeObjective,
29-
lambda: {"w": 1.0, "bounds": (0.1, 0.9)},
29+
lambda: {"w": 1.0, "bounds": [0.1, 0.9]},
3030
)
3131

3232
specs.add_valid(

0 commit comments

Comments
 (0)