feat(async): add run_async to every algorithm in the catalog
Async coverage was incomplete in 0.7 (only RandomSearch and DifferentialEvolution had run_async). 0.8 closes the gap: every one of the 33 algorithms now exposes run_async(&problem, concurrency).await, gated on the async feature. - Population-based algorithms fan out per-generation evaluations through evaluate_batch_async with concurrency-bounded FuturesOrdered chunks. - Steady-state algorithms (HillClimber, SimulatedAnnealing, OnePlusOneEs, Paes, NelderMead) await each step sequentially; they accept the concurrency parameter for API uniformity. - TabuSearch fans out the K-neighbor batch each step. - Surrogate algorithms (BayesianOpt, Tpe) batch the initial design and await per-iteration acquisitions sequentially so the surrogate can update between picks. - Hyperband uses a new AsyncPartialProblem trait (mirroring PartialProblem for multi-fidelity workloads) and a parallel evaluate_batch_at_budget_async helper; each Successive-Halving rung fans out its budgeted evaluations. All paths preserve seeded determinism: RNG draws happen on the main task in the same order as the sync path, and only the evaluations are concurrent. Adds a dedicated cookbook recipe at docs/book/src/cookbook/async.md with a worked example (DifferentialEvolution under tokio) and guidance on picking concurrency. Cross-references in SUMMARY.md and cookbook.md are updated to surface the new recipe. The follow-up docs commit reconciles the rest of the user guide and README to describe the new feature; this commit is the bare async surface.
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
|
||||
use futures::stream::{FuturesOrdered, StreamExt};
|
||||
|
||||
use crate::core::async_problem::AsyncProblem;
|
||||
use crate::core::async_problem::{AsyncPartialProblem, AsyncProblem};
|
||||
use crate::core::candidate::Candidate;
|
||||
use crate::core::evaluation::Evaluation;
|
||||
|
||||
/// Evaluate every decision concurrently against `problem`, preserving
|
||||
/// input order in the returned vector. Concurrency is bounded by
|
||||
@@ -56,3 +57,35 @@ where
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Evaluate every decision at the given `budget` concurrently against a
|
||||
/// multi-fidelity `problem`, preserving input order. Hyperband's async
|
||||
/// path uses this for each Successive-Halving rung.
|
||||
pub async fn evaluate_batch_at_budget_async<P>(
|
||||
problem: &P,
|
||||
decisions: &[P::Decision],
|
||||
budget: f64,
|
||||
concurrency: usize,
|
||||
) -> Vec<Evaluation>
|
||||
where
|
||||
P: AsyncPartialProblem,
|
||||
{
|
||||
assert!(
|
||||
concurrency >= 1,
|
||||
"evaluate_batch_at_budget_async concurrency must be >= 1"
|
||||
);
|
||||
let mut out: Vec<Evaluation> = Vec::with_capacity(decisions.len());
|
||||
let mut idx = 0usize;
|
||||
while idx < decisions.len() {
|
||||
let mut futs = FuturesOrdered::new();
|
||||
let end = (idx + concurrency).min(decisions.len());
|
||||
for d in &decisions[idx..end] {
|
||||
futs.push_back(async move { problem.evaluate_at_budget_async(d, budget).await });
|
||||
}
|
||||
while let Some(e) = futs.next().await {
|
||||
out.push(e);
|
||||
}
|
||||
idx = end;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user