Ask-and-tell interface
Optimize runs the loop for you. The ask-and-tell interface hands the loop back: you call
study.Ask() to get a trial, evaluate it however you like, and call study.Tell(...) to report the
result. This recipe shows the three situations where that control pays off.
1. Apply OptunaSharp with minimal changes to existing code
Suppose you already have a training script that ends with a validation score. Rather than wrapping it
in a Func<Trial, double>, keep your own loop and only ask for parameters and tell the result:
using OptunaSharp;
var study = Optuna.CreateStudy(direction: StudyDirection.Maximize);
const int nTrials = 10;
for (int i = 0; i < nTrials; i++)
{
Trial trial = study.Ask(); // a live Trial (not a FrozenTrial)
double c = trial.SuggestFloat("C", 1e-7, 10.0, log: true);
string solver = trial.SuggestCategorical("solver", new[] { "lbfgs", "saga" });
double valAccuracy = TrainAndScore(c, solver); // your existing code, unchanged
study.Tell(trial, valAccuracy); // finish the trial with its objective value
}
Ask creates a trial that can sample; Tell finishes it. Your training code never has to move into
an objective function.
To prune in an ask/tell loop, report intermediate values and pass an explicit state to Tell:
Trial trial = study.Ask();
double alpha = trial.SuggestFloat("alpha", 0.0, 1.0);
bool pruned = false;
for (int step = 0; step < nIter; step++)
{
double intermediate = TrainOneStep(alpha, step);
trial.Report(intermediate, step);
if (trial.ShouldPrune()) { pruned = true; break; }
}
if (pruned)
study.Tell(trial, state: TrialState.Pruned); // record the prune…
else
study.Tell(trial, FinalScore(alpha)); // …or the final value
Tellalso accepts a trial number instead of the trial object:study.Tell(trial.Number, y)is equivalent tostudy.Tell(trial, y).
2. Define-and-run
Ask/tell supports both define-by-run (above) and define-and-run: declare the distributions up
front and pass them to Ask, which pre-samples every one. The returned trial already holds the
values in Params.
using OptunaSharp;
using OptunaSharp.Collections;
using OptunaSharp.Distributions;
var distributions = new OrderedDict<string, BaseDistribution>
{
["C"] = new FloatDistribution(1e-7, 10.0, log: true),
["solver"] = new CategoricalDistribution(new CategoricalChoice[] { "lbfgs", "saga" }),
};
var study = Optuna.CreateStudy(direction: StudyDirection.Maximize);
for (int i = 0; i < 10; i++)
{
Trial trial = study.Ask(distributions); // both params already sampled
double c = (double)trial.Params["C"]!;
object? solver = trial.Params["solver"];
study.Tell(trial, TrainAndScore(c, solver));
}
3. Batch optimization
Because you decide when to evaluate, you can ask for a whole batch of trials, evaluate them together (vectorized, or across parallel workers), and tell them back. Store each trial's number so you can finish it after the batch runs:
using OptunaSharp;
using OptunaSharp.Samplers;
const int batchSize = 10;
var study = Optuna.CreateStudy(sampler: new CmaEsSampler());
for (int round = 0; round < 3; round++) // 3 batches x 10 = 30 trials
{
var numbers = new List<int>();
var xs = new List<double>();
var ys = new List<double>();
for (int i = 0; i < batchSize; i++)
{
Trial trial = study.Ask();
numbers.Add(trial.Number);
xs.Add(trial.SuggestFloat("x", -10, 10));
ys.Add(trial.SuggestFloat("y", -10, 10));
}
double[] objectives = BatchedObjective(xs, ys); // evaluate the whole batch at once
for (int i = 0; i < batchSize; i++)
study.Tell(numbers[i], objectives[i]);
}
Tips.
TPESamplertakes aconstantLiaroption (on by default) that discourages running trials from clustering on the same region — useful when many workers ask before any tells. ForCmaEsSampler, setting the population size to a multiple of the batch size lines up the algorithm with your batches. See the Samplers guide.
Relationship to Optuna
| Optuna (Python) | OptunaSharp |
|---|---|
trial = study.ask() |
Trial trial = study.Ask() |
study.tell(trial, value) |
study.Tell(trial, value) |
study.tell(trial.number, value) |
study.Tell(trial.Number, value) |
study.tell(trial, state=TrialState.PRUNED) |
study.Tell(trial, state: TrialState.Pruned) |
study.ask(distributions) (define-and-run) |
study.Ask(distributions) |
trial.params["C"] |
trial.Params["C"] |
See also
Study and Trials · Samplers · Pruners · Command-line interface (ask/tell from the shell) · the API reference.