Skip to content

Commit 76ffc04

Browse files
committed
wip
1 parent 465dfda commit 76ffc04

25 files changed

Lines changed: 1854 additions & 711 deletions

Cargo.lock

Lines changed: 401 additions & 95 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
luminal = "0.2"
7+
luminal = {path="/home/andrew/code/luminal"}
8+
luminal_training = {path="/home/andrew/code/luminal/crates/luminal_training"}
89
serde = { version = "1.0", features = ["derive"] }
910
thiserror = "2.0.12"
1011
rand = { version = "0.9.1", features = ["small_rng", "std"] }
@@ -28,6 +29,8 @@ rand_chacha = "0.9.0"
2829
rayon = "1.10.0"
2930
html-escape = "0.2.13"
3031
itertools = "0.13.0"
32+
dfdx = { version = "0.13", features = ["f16"] }
33+
luminal_nn = {path="/home/andrew/code/luminal/crates/luminal_nn"}
3134

3235
[features]
3336
default = ["plotting"]

src/benchmarks/evaluation.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33

44
use crate::benchmarks::functions::OptimizationProblem;
55
use crate::optimizers::optimizer::Optimizer;
6+
use dfdx::prelude::Shape;
67
use log::{debug, info, warn};
8+
use luminal::prelude::*;
79
use rand::prelude::StdRng;
810
use rand::{Rng, SeedableRng};
11+
use rand_distr::num_traits::ToPrimitive;
912
use serde::{Deserialize, Serialize};
1013
use statrs::statistics::Statistics;
1114
use std::cmp::max;
@@ -14,8 +17,6 @@ use std::sync::atomic::AtomicBool;
1417
use std::sync::atomic::{AtomicUsize, Ordering};
1518
use std::sync::Arc;
1619
use std::time::{Duration, Instant};
17-
use luminal::prelude::*;
18-
use rand_distr::num_traits::ToPrimitive;
1920
use tokio::time::timeout;
2021
/// Global flag to disable optimal value thresholds for all problems
2122
static NO_THRESHOLD_MODE: AtomicBool = AtomicBool::new(false);
@@ -41,7 +42,6 @@ pub fn create_1d_tensor(data: &[f32], _device: &Device) -> Result<Tensor, String
4142
Ok(Tensor::new(data.to_vec()))
4243
}
4344

44-
4545
/// Wrapper for Duration that implements bincode traits
4646
#[derive(Debug, Clone, Serialize, Deserialize)]
4747
pub struct DurationWrapper {
@@ -135,9 +135,14 @@ impl OptimizationTrace {
135135
if self.iterations.is_empty() {
136136
None
137137
} else {
138-
Some(Statistics::min(
139-
self.iterations.iter().map(|data| data.function_value as f64),
140-
).to_f32()?)
138+
Some(
139+
Statistics::min(
140+
self.iterations
141+
.iter()
142+
.map(|data| data.function_value as f64),
143+
)
144+
.to_f32()?,
145+
)
141146
}
142147
}
143148

@@ -310,10 +315,10 @@ impl BenchmarkRunner {
310315
}
311316

312317
/// Run benchmarks for all combinations of problems and optimizers
313-
pub async fn run_benchmarks<S: Shape>(
318+
pub async fn run_benchmarks(
314319
&self,
315320
problems: Vec<Box<ProblemSpec>>,
316-
mut optimizers: Vec<Box<dyn Optimizer<S>>>,
321+
mut optimizers: Vec<Box<dyn Optimizer>>,
317322
) -> Result<BenchmarkResults, BenchmarkError> {
318323
let mut results = BenchmarkResults::new(self.config.clone());
319324
info!(
@@ -353,10 +358,10 @@ impl BenchmarkRunner {
353358
}
354359

355360
/// Run a single benchmark with one problem and one optimizer
356-
pub async fn run_single_benchmark<S: Shape>(
361+
pub async fn run_single_benchmark(
357362
&self,
358363
problem: &ProblemSpec,
359-
optimizer: &mut Box<dyn Optimizer<S>>,
364+
optimizer: &mut Box<dyn Optimizer>,
360365
run_id: usize,
361366
opt_name: &str,
362367
initial_point: Result<Vec<f32>, Result<SingleResult, BenchmarkError>>,
@@ -504,10 +509,10 @@ impl BenchmarkRunner {
504509
}
505510
}
506511

507-
async fn optimization_loop<S: Shape>(
512+
async fn optimization_loop(
508513
&self,
509514
problem: &ProblemSpec,
510-
optimizer: &mut dyn Optimizer<S>,
515+
optimizer: &mut dyn Optimizer,
511516
input_floats: &mut [f32],
512517
iteration: &mut usize,
513518
function_evaluations: &mut usize,
@@ -683,7 +688,7 @@ impl BenchmarkRunner {
683688
// This benchmark runner needs to be rewritten to support graph-based optimizers.
684689
// For now, we return an error to allow compilation.
685690
return Err(BenchmarkError::OptimizerError("Optimizer::step signature mismatch: BenchmarkRunner requires update for graph-based optimizers".to_string()));
686-
691+
687692
#[allow(unreachable_code)]
688693
let step_result = crate::optimizers::optimizer::StepResult {
689694
step_size: 0.0,
@@ -1096,4 +1101,4 @@ pub fn new_initial_point(
10961101
*xi += (random_delta * 2.0 - 1.0) * noise; // Random perturbation
10971102
}
10981103
Ok(x)
1099-
}
1104+
}

src/benchmarks/unified_tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ impl ProblemTestResults {
132132
&& self.finite_values_maintained
133133
&& self.clone_behavior_correct
134134
&& self
135-
.derivative_validation_results
136-
.numerical_gradient_accuracy
137-
> 0.7
135+
.derivative_validation_results
136+
.numerical_gradient_accuracy
137+
> 0.7
138138
&& (self.derivative_validation_results.robustness_score > 0.5 ||
139139
// For ML problems, allow lower robustness scores if other metrics are good
140140
((self.problem_name.contains("Regression") || self.problem_name.contains("SVM") || self.problem_name.contains("NeuralNetwork"))
@@ -445,8 +445,8 @@ impl UnifiedProblemTester {
445445
// Only flag as error if the values are extremely large
446446
if f_val.abs() > self.config.finite_check_tolerance
447447
|| grad
448-
.iter()
449-
.any(|&g| g.abs() > self.config.finite_check_tolerance)
448+
.iter()
449+
.any(|&g| g.abs() > self.config.finite_check_tolerance)
450450
{
451451
all_finite = false;
452452
break;
@@ -1685,7 +1685,7 @@ mod tests {
16851685
.collect(),
16861686
0.01,
16871687
)
1688-
.unwrap(),
1688+
.unwrap(),
16891689
),
16901690
Box::new(SupportVectorMachine::new(svm_x, svm_y, 1.0).unwrap()),
16911691
Box::new(NeuralNetworkTraining::mlp_classification(vec![3, 5, 2], &mut rng).unwrap()),
@@ -1733,7 +1733,7 @@ mod tests {
17331733
&mut rng,
17341734
None,
17351735
)
1736-
.unwrap(),
1736+
.unwrap(),
17371737
),
17381738
#[cfg(feature = "onednn")]
17391739
Box::new(
@@ -1789,7 +1789,7 @@ mod tests {
17891789
.collect(),
17901790
0.01,
17911791
)
1792-
.unwrap(),
1792+
.unwrap(),
17931793
),
17941794
];
17951795
let results = test_multiple_problems(problems, None);
@@ -1956,4 +1956,4 @@ mod tests {
19561956
// Should still pass with stricter config
19571957
assert!(results.is_valid() || !results.errors.is_empty());
19581958
}
1959-
}
1959+
}

src/experiment_runner/adaptive_runner.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::benchmarks::evaluation::{
44
DurationWrapper, ProblemSpec,
55
};
66
use crate::Optimizer;
7+
use dfdx::prelude::Shape;
78
use itertools::Itertools;
89
use log::{debug, info, trace, warn};
910
use rand::prelude::*;
@@ -17,7 +18,6 @@ use std::iter::Take;
1718
use std::slice::Iter;
1819
use std::sync::Arc;
1920
use std::time::Duration;
20-
use luminal::prelude::Shape;
2121
use tokio::sync::Semaphore;
2222

2323
/// Detailed tracking of evolutionary events
@@ -158,11 +158,11 @@ impl AdaptiveExperimentRunner {
158158
}
159159

160160
/// Run adaptive parameter evolution to find best optimizer configurations for each problem
161-
pub async fn run_adaptive_evolution<S: Shape>(
161+
pub async fn run_adaptive_evolution(
162162
&mut self,
163163
problems: Vec<ProblemSpec>,
164164
optimizer_types: Vec<super::parameter_evolution::OptimizerType>,
165-
) -> anyhow::Result<HashMap<String, Vec<(String, Arc<dyn Optimizer<S>>)>>> {
165+
) -> anyhow::Result<HashMap<String, Vec<(String, Arc<dyn Optimizer>)>>> {
166166
info!("Starting adaptive parameter evolution");
167167
info!(
168168
"Population size: {}, Generations: {}, Evaluation runs: {}",
@@ -221,7 +221,7 @@ impl AdaptiveExperimentRunner {
221221
);
222222

223223
let family_best = self
224-
.evolve_optimizer_for_problem_family::<S>(
224+
.evolve_optimizer_for_problem_family(
225225
family_problems.clone(),
226226
optimizer_type.clone(),
227227
&evolution_dir,
@@ -259,7 +259,7 @@ impl AdaptiveExperimentRunner {
259259
family_name, all_best_genomes.len());
260260

261261
// Convert best genomes to optimizers
262-
let mut optimizers: Vec<(String, Arc<dyn Optimizer<S>>)> = Vec::new();
262+
let mut optimizers: Vec<(String, Arc<dyn Optimizer>)> = Vec::new();
263263
let best: Vec<OptimizerGenome> = all_best_genomes
264264
.iter()
265265
.into_group_map_by(|x| x.optimizer_type.to_string())
@@ -289,7 +289,7 @@ impl AdaptiveExperimentRunner {
289289
"Created evolved optimizer '{}' with fitness: {:?}",
290290
name, genome.fitness
291291
);
292-
optimizers.push((name, genome.to_optimizer::<S>()));
292+
optimizers.push((name, genome.to_optimizer()));
293293
}
294294

295295
family_best_optimizers.insert(family_name.clone(), optimizers);
@@ -336,7 +336,7 @@ impl AdaptiveExperimentRunner {
336336
}
337337
}
338338

339-
async fn evolve_optimizer_for_problem_family<S: Shape>(
339+
async fn evolve_optimizer_for_problem_family(
340340
&self,
341341
family_problems: Vec<ProblemSpec>,
342342
optimizer_type: super::parameter_evolution::OptimizerType,
@@ -394,7 +394,7 @@ impl AdaptiveExperimentRunner {
394394

395395
// Evaluate fitness of population
396396
debug!("Starting fitness evaluation for generation {}", generation);
397-
self.evaluate_population_on_family::<S>(
397+
self.evaluate_population_on_family(
398398
&mut population,
399399
&family_problems,
400400
&mut tracker,
@@ -492,7 +492,7 @@ impl AdaptiveExperimentRunner {
492492
if i < sorted_population.len() {
493493
let genome = &mut sorted_population[i];
494494
match Self::evaluate_genome_with_metrics(
495-
genome.to_optimizer::<S>(),
495+
genome.to_optimizer(),
496496
(&family_problems[0]).clone().clone(),
497497
self.config.clone(),
498498
1, // Just one run for emergency evaluation
@@ -573,7 +573,7 @@ impl AdaptiveExperimentRunner {
573573
OptimizerGenome::new_random(optimizer_type.clone(), &mut rng);
574574
// Try to evaluate the fallback genome
575575
match Self::evaluate_genome(
576-
fallback_genome.to_optimizer::<S>(),
576+
fallback_genome.to_optimizer(),
577577
(&family_problems[0]).clone(),
578578
self.config.clone(),
579579
1,
@@ -659,7 +659,7 @@ impl AdaptiveExperimentRunner {
659659
Ok(best_genomes)
660660
}
661661

662-
async fn evaluate_population_on_family<S: Shape>(
662+
async fn evaluate_population_on_family(
663663
&self,
664664
population: &mut [OptimizerGenome],
665665
family_problems: &[ProblemSpec],
@@ -705,7 +705,7 @@ impl AdaptiveExperimentRunner {
705705
});
706706

707707
let semaphore = semaphore.clone();
708-
let optimizer = genome.to_optimizer::<S>();
708+
let optimizer = genome.to_optimizer();
709709
let problems = family_problems.to_vec();
710710
let config = self.config.clone();
711711
let evaluation_runs = self.evaluation_runs;
@@ -854,7 +854,7 @@ impl AdaptiveExperimentRunner {
854854
Ok(())
855855
}
856856

857-
async fn evaluate_population<S: Shape>(
857+
async fn evaluate_population(
858858
&self,
859859
population: &mut [OptimizerGenome],
860860
problem: &ProblemSpec,
@@ -895,7 +895,7 @@ impl AdaptiveExperimentRunner {
895895
});
896896

897897
let semaphore = semaphore.clone();
898-
let optimizer = genome.to_optimizer::<S>();
898+
let optimizer = genome.to_optimizer();
899899
let problem = problem.clone();
900900
let config = self.config.clone();
901901
let evaluation_runs = self.evaluation_runs;
@@ -1297,8 +1297,8 @@ impl AdaptiveExperimentRunner {
12971297
new_population
12981298
}
12991299

1300-
async fn evaluate_genome_with_metrics<S: Shape>(
1301-
optimizer: Arc<dyn Optimizer<S>>,
1300+
async fn evaluate_genome_with_metrics(
1301+
optimizer: Arc<dyn Optimizer>,
13021302
problem: ProblemSpec,
13031303
config: BenchmarkConfig,
13041304
num_runs: usize,
@@ -1376,8 +1376,8 @@ impl AdaptiveExperimentRunner {
13761376
))
13771377
}
13781378

1379-
async fn evaluate_genome<S: Shape>(
1380-
optimizer: Arc<dyn Optimizer<S>>,
1379+
async fn evaluate_genome(
1380+
optimizer: Arc<dyn Optimizer>,
13811381
problem: ProblemSpec,
13821382
config: BenchmarkConfig,
13831383
num_runs: usize,
@@ -1543,10 +1543,10 @@ impl AdaptiveExperimentRunner {
15431543
}
15441544

15451545
/// Run final championship with evolved optimizers
1546-
pub async fn run_evolved_championship<S: Shape>(
1546+
pub async fn run_evolved_championship(
15471547
&self,
15481548
problems: Vec<ProblemSpec>,
1549-
evolved_optimizers: HashMap<String, Vec<(String, Arc<dyn Optimizer<S>>)>>,
1549+
evolved_optimizers: HashMap<String, Vec<(String, Arc<dyn Optimizer>)>>,
15501550
) -> anyhow::Result<()> {
15511551
info!("Running championship with evolved optimizers");
15521552
info!("Championship includes {} problems", problems.len());
@@ -1927,7 +1927,7 @@ impl FamilyRepresentation {
19271927
}
19281928

19291929
/// Convenience function to run adaptive evolution experiments
1930-
pub async fn run_adaptive_benchmark<S: Shape>(
1930+
pub async fn run_adaptive_benchmark(
19311931
report_path_prefix: &str,
19321932
max_evals: usize,
19331933
num_runs: usize,
@@ -1984,7 +1984,7 @@ pub async fn run_adaptive_benchmark<S: Shape>(
19841984
// First, evolve optimizer parameters for each problem
19851985
info!("Starting parameter evolution phase");
19861986
let evolved_optimizers = runner
1987-
.run_adaptive_evolution::<S>(problems.clone(), optimizer_types)
1987+
.run_adaptive_evolution(problems.clone(), optimizer_types)
19881988
.await?;
19891989
info!("Parameter evolution phase completed");
19901990

@@ -1999,4 +1999,4 @@ pub async fn run_adaptive_benchmark<S: Shape>(
19991999
info!("Results saved to: {}", output_dir.display());
20002000

20012001
Ok(())
2002-
}
2002+
}

0 commit comments

Comments
 (0)