Packages
nous
0.16.3
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/eval/optimizer/strategies/random.ex
defmodule Nous.Eval.Optimizer.Strategies.Random do
@moduledoc """
Random search optimization strategy.
Random search samples configurations randomly from the search space.
Often surprisingly effective and much faster than grid search for
high-dimensional spaces.
## Options
* `:n_trials` - Number of trials to run (default: 100)
* `:timeout` - Total timeout in ms (default: 3600000 = 1 hour)
* `:early_stop` - Stop if score reaches threshold
* `:verbose` - Print progress (default: true)
* `:latin_hypercube` - Use Latin Hypercube Sampling for better coverage (default: false)
## Example
Optimizer.optimize(suite, params,
strategy: :random,
n_trials: 50,
metric: :score
)
## When to Use
Random search is recommended when:
- Search space is large (many parameters or wide ranges)
- Some parameters are more important than others (random search explores all)
- You have limited time/budget for optimization
- Grid search would take too long
## Latin Hypercube Sampling
Enable `latin_hypercube: true` for better coverage of the search space.
LHS ensures samples are spread evenly across each parameter's range.
"""
@behaviour Nous.Eval.Optimizer.Strategy
alias Nous.Eval.{Suite, Optimizer}
alias Nous.Eval.Optimizer.SearchSpace
@impl true
def run(%Suite{} = suite, %SearchSpace{} = space, metric, _maximize, opts) do
n_trials = Keyword.get(opts, :n_trials, 100)
timeout = Keyword.get(opts, :timeout, 3_600_000)
verbose = Keyword.get(opts, :verbose, true)
early_stop = Keyword.get(opts, :early_stop)
latin_hypercube = Keyword.get(opts, :latin_hypercube, false)
start_time = System.monotonic_time(:millisecond)
# Generate configurations
configs =
if latin_hypercube do
SearchSpace.latin_hypercube_sample(space, n_trials)
else
SearchSpace.sample_n(space, n_trials)
end
if verbose do
sampling = if latin_hypercube, do: "Latin Hypercube", else: "Random"
IO.puts("#{sampling} Search: #{n_trials} trials")
end
# Run trials
{trials, _} =
Enum.reduce_while(configs, {[], 0}, fn config, {acc, idx} ->
# Check timeout
elapsed = System.monotonic_time(:millisecond) - start_time
if elapsed > timeout do
{:halt, {acc, idx}}
else
if verbose do
IO.write("\rTrial #{idx + 1}/#{n_trials}")
end
case Optimizer.run_trial(suite, config, metric, opts) do
{:ok, trial} ->
# Check early stop
if early_stop && trial.score >= early_stop do
if verbose, do: IO.puts("\nEarly stop: score #{trial.score} >= #{early_stop}")
{:halt, {[trial | acc], idx + 1}}
else
{:cont, {[trial | acc], idx + 1}}
end
{:error, reason} ->
if verbose do
IO.puts("\nTrial #{idx + 1} failed: #{inspect(reason)}")
end
failed_trial = %{
config: config,
score: 0.0,
metrics: %{error: reason},
duration_ms: 0
}
{:cont, {[failed_trial | acc], idx + 1}}
end
end
end)
if verbose, do: IO.puts("")
{:ok, Enum.reverse(trials)}
end
end