Request multiple solutions#
Ask the experiment for k solutions when you want several good candidates from different regions of the space rather than one winner. Optimization runs exactly as before; only best changes. Background on single-objective best lives in Metrics and objectives. The numbers below are one machine’s; another dependency set prints others.
Closed beta (nothing installed): the same request over plain HTTP is experimental_design_rest.py: num_solutions: 3 in the JSON body of POST /v1/experiments, and GET /v1/experiments/{id}/best_assignments returns count entries in data.
Ask for k solutions#
Use a single objective:
metric=..., ormetrics=[...]with exactly one optimized metric.Pass
num_solutions=k(an integer of at least 1) tocreate_experiment. The value is fixed for the life of the experiment;update_experimentcannot change it.Run the ask-observe loop as usual.
Read
best(exp.id):data[0]is the best observation; each further entry is the candidate farthest (by its minimum distance in the unit cube of the search space) from the entries already chosen, drawn from a pool of the top2kscored observations by value, or the top 20 percent of them when that is more. The result holds fewer thankentries while fewer scored observations exist, and only feasible ones when an outcome constraint is declared.Read
experiment(exp.id).progress.best_observationwhen you want the single best alone.
from tauopt import ApiError, Client
client = Client(driver="local")
exp = client.create_experiment(
name="three-candidates",
parameters=[
{"name": "x1", "type": "double", "bounds": (0.0, 1.0)},
{"name": "x2", "type": "double", "bounds": (0.0, 1.0)},
],
metric={"name": "score", "objective": "maximize"},
num_solutions=3,
)
def score(a):
# two good basins: around (0.2, 0.8) and around (0.8, 0.2)
left = -((a["x1"] - 0.2) ** 2) - (a["x2"] - 0.8) ** 2
right = -((a["x1"] - 0.8) ** 2) - (a["x2"] - 0.2) ** 2
return max(left, right)
for i in range(14):
sug = client.suggest(
exp.id, config={"strategy": "auto", "seed": 4, "timebox_mode": "caps"}
)
client.observe(exp.id, suggestion_id=sug.id, value=score(sug.assignments))
best = client.best(exp.id)
print("count:", best.count)
for entry in best.data:
print(round(entry.value, 4), {k: round(v, 3) for k, v in entry.assignments.items()})
print("scalar best:", round(client.experiment(exp.id).progress.best_observation.value, 4))
# num_solutions is single-objective only
try:
client.create_experiment(
name="frontier-and-k",
parameters=[{"name": "x", "type": "double", "bounds": (0.0, 1.0)}],
metrics=[
{"name": "a", "objective": "minimize"},
{"name": "b", "objective": "minimize"},
],
num_solutions=3,
)
except ApiError as e:
print(e)
count: 3
-0.0 {'x1': 0.799, 'x2': 0.196}
-0.0199 {'x1': 0.06, 'x2': 0.815}
-0.0027 {'x1': 0.252, 'x2': 0.801}
scalar best: -0.0
[400] validation_error: num_solutions is single-objective only
After data[0], distance orders the entries: the second is the point farthest from the first (in the other basin), and the third, despite its better value, is the point farthest from both of them.
Combine with metrics mode#
num_solutions > 1 with two or more optimized metrics is refused at creation with num_solutions is single-objective only, as the end of the run shows. With two or more optimized metrics best already returns a set, the Pareto frontier; see Optimize multiple metrics. A metrics-mode experiment with one optimized metric plus constraint or stored metrics is single-objective and accepts num_solutions.