Packages

A powerful and approachable neuroevolution library for Elixir

Current section

Files

Jump to
bardo lib mix tasks run_examples.ex
Raw

lib/mix/tasks/run_examples.ex

defmodule Mix.Tasks.RunExamples do
use Mix.Task
@shortdoc "Runs Bardo examples and benchmarks"
@moduledoc """
Runs Bardo examples and benchmarks with small parameters for testing.
## Usage
mix run_examples [--xor-only]
Options:
--xor-only: Run only the XOR example
"""
@impl Mix.Task
def run(args) do
# Parse arguments
{opts, _, _} = OptionParser.parse(args, strict: [xor_only: :boolean])
xor_only = Keyword.get(opts, :xor_only, false)
IO.puts("\n=========================================")
IO.puts("BARDO EXAMPLES AND BENCHMARKS RUNNER")
IO.puts("=========================================\n")
# Ensure application is started
Mix.Task.run("app.start")
if xor_only do
run_xor_example()
else
run_all_examples()
end
# Make sure process doesn't end too quickly
:timer.sleep(1000)
end
defp run_xor_example do
IO.puts("\n#{String.duplicate("=", 60)}")
IO.puts("RUNNING: XOR Example")
IO.puts("#{String.duplicate("=", 60)}")
start_time = System.monotonic_time(:millisecond)
result = try do
Bardo.Examples.Simple.Xor.run(
population_size: 20,
max_generations: 10,
show_progress: true
)
rescue
error ->
IO.puts("\n❌ ERROR in XOR Example:")
IO.puts(" #{inspect(error)}")
IO.puts("\nStacktrace:")
__STACKTRACE__ |> Enum.take(5) |> Enum.each(fn line ->
IO.puts(" #{inspect(line)}")
end)
{:error, error}
end
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
case result do
{:error, _} ->
IO.puts("\n❌ XOR Example failed after #{duration}ms")
_ ->
IO.puts("\n✅ XOR Example completed successfully in #{duration}ms")
IO.puts("Neural network structure:")
IO.inspect(result, limit: 5)
end
end
defp run_all_examples do
# Helper functions for the runner
examples = [
%{
name: "XOR Example",
module: Bardo.Examples.Simple.Xor,
function: :run,
args: [[population_size: 10, max_generations: 5, show_progress: true]]
},
%{
name: "Double Pole Balancing (with damping)",
module: Bardo.Examples.Benchmarks.Dpb,
function: :run_with_damping,
args: [:dpb_test, 5, 3, 1000]
},
%{
name: "Double Pole Balancing (test best solution)",
module: Bardo.Examples.Benchmarks.Dpb,
function: :test_best_solution,
args: [:dpb_test],
depends_on: "Double Pole Balancing (with damping)"
},
%{
name: "Double Pole Balancing (without damping)",
module: Bardo.Examples.Benchmarks.Dpb,
function: :run_without_damping,
args: [:dpb_wo_test, 5, 3, 1000]
},
%{
name: "Flatland Predator-Prey Simulation",
module: Bardo.Examples.Applications.Flatland,
function: :run,
args: [:flatland_test, 5, 5, 10, 100, 3]
},
%{
name: "Forex (FX) Trading",
module: Bardo.Examples.Applications.Fx,
function: :run,
args: [:fx_test, 5, 500, 3]
},
%{
name: "Forex (FX) Best Agent Test",
module: Bardo.Examples.Applications.Fx,
function: :test_best_agent,
args: [:fx_test],
depends_on: "Forex (FX) Trading"
}
]
# Check which examples are available
IO.puts("Checking for available examples...")
available_examples = Enum.filter(examples, fn example ->
module_exists?(example.module, example.function, length(example.args))
end)
if Enum.empty?(available_examples) do
IO.puts("\n⚠️ No examples are available to run.")
# Return early by using a custom value to indicate no examples
{:no_examples, []}
else
# Run examples with dependency handling
results = %{}
executed = []
{results, _} = Enum.reduce(available_examples, {results, executed}, fn example, {acc_results, acc_executed} ->
run_example(example, acc_results, acc_executed)
end)
# Print summary
print_summary(results, examples, available_examples)
end
end
defp run_example(example, results, executed) do
if Map.has_key?(example, :depends_on) && example.depends_on not in executed do
IO.puts("⚠️ Skipping #{example.name} because dependency #{example.depends_on} was not executed")
{Map.put(results, example.name, {:error, :dependency_not_executed}), executed}
else
if Map.has_key?(example, :depends_on) && results[example.depends_on] == {:error, :dependency_failed} do
IO.puts("⚠️ Skipping #{example.name} because dependency #{example.depends_on} failed")
{Map.put(results, example.name, {:error, :dependency_failed}), executed}
else
IO.puts("\n#{String.duplicate("=", 60)}")
IO.puts("RUNNING: #{example.name}")
IO.puts("#{String.duplicate("=", 60)}")
start_time = System.monotonic_time(:millisecond)
result = try do
apply(example.module, example.function, example.args)
rescue
error ->
IO.puts("\n❌ ERROR in #{example.name}:")
IO.puts(" #{inspect(error)}")
IO.puts("\nStacktrace:")
__STACKTRACE__ |> Enum.take(5) |> Enum.each(fn line ->
IO.puts(" #{inspect(line)}")
end)
{:error, error}
end
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
case result do
:ok ->
IO.puts("\n#{example.name} completed successfully in #{duration}ms")
{:ok, _} ->
IO.puts("\n#{example.name} completed successfully in #{duration}ms")
{:error, _} ->
IO.puts("\n#{example.name} failed after #{duration}ms")
_ ->
IO.puts("\n#{example.name} completed with result: #{inspect(result)} in #{duration}ms")
end
IO.puts("\nWaiting 2 seconds before next example...")
:timer.sleep(2000)
{Map.put(results, example.name, result), [example.name | executed]}
end
end
end
defp module_exists?(module, function, arity) do
try do
# Check if module is available
module.__info__(:functions)
|> Keyword.get(function) == arity
rescue
UndefinedFunctionError ->
IO.puts("❌ Module #{module} is not available or function #{function}/#{arity} doesn't exist")
false
end
end
defp print_summary(results, all_examples, available_examples) do
IO.puts("\n#{String.duplicate("=", 60)}")
IO.puts("EXAMPLES SUMMARY")
IO.puts("#{String.duplicate("=", 60)}")
successful = Enum.count(results, fn {_, result} ->
case result do
:ok -> true
{:ok, _} -> true
_ -> not match?({:error, _}, result)
end
end)
total_run = map_size(results)
IO.puts("Total examples: #{length(all_examples)}")
IO.puts("Available examples: #{length(available_examples)}")
IO.puts("Executed examples: #{total_run}")
IO.puts("Successful: #{successful}")
IO.puts("Failed: #{total_run - successful}")
# Print detailed results
if total_run > 0 do
# Print successful examples
successful_examples = Enum.filter(results, fn {_, result} ->
case result do
:ok -> true
{:ok, _} -> true
_ -> not match?({:error, _}, result)
end
end)
if length(successful_examples) > 0 do
IO.puts("\nSUCCESSFUL EXAMPLES:")
Enum.each(successful_examples, fn {name, _} ->
IO.puts(" ✅ #{name}")
end)
end
# Print failed examples
failed_examples = Enum.filter(results, fn {_, result} -> match?({:error, _}, result) end)
if length(failed_examples) > 0 do
IO.puts("\nFAILED EXAMPLES:")
Enum.each(failed_examples, fn {name, {:error, error}} ->
IO.puts(" ❌ #{name}: #{inspect(error)}")
end)
IO.puts("\nSee detailed error messages above for troubleshooting.")
end
else
IO.puts("\n⚠️ No examples were executed")
end
if successful == total_run and total_run > 0 do
IO.puts("\n🎉 All executed examples ran successfully!")
end
end
end