Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions examples/basic_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,16 @@

use anyhow::Result;
use candle_core::{Device, Tensor};
use qqn_optimizer::utils::math::SeparateFunctions;
use qqn_optimizer::{
OptimizationProblem, Optimizer, QQNConfig,
QQNOptimizer,
};
use std::sync::Arc;
use qqn_optimizer::benchmarks::analytic_functions::RosenbrockFunction;
use qqn_optimizer::line_search::{LineSearchConfig, LineSearchMethod};
use qqn_optimizer::utils::math::SeparateFunctions;
use qqn_optimizer::{OptimizationProblem, Optimizer, QQNConfig, QQNOptimizer};
use std::sync::Arc;

fn main() -> Result<()> {
// Configure the QQN optimizer
let config = QQNConfig {
lbfgs_history: 10, // L-BFGS history length
lbfgs_history: 10, // L-BFGS history length
min_lbfgs_iterations: 2,
line_search: LineSearchConfig {
method: LineSearchMethod::StrongWolfe,
Expand All @@ -30,11 +27,11 @@ fn main() -> Result<()> {
initial_step: 1.0,
min_step: 1e-16,
max_step: 1e16,
verbose: false, // Enable verbose output for line search
verbose: false, // Enable verbose output for line search
line_bracket_method: 1, // 1: gradient-based bracketing, 2: function-value-based bracketing
},
epsilon: 1e-8, // Numerical stability constant
verbose: false, // Enable verbose output
epsilon: 1e-8, // Numerical stability constant
verbose: false, // Enable verbose output
min_step_persist: 0.0,
min_step_size: 0.0,
gradient_scale_factor: 1.0,
Expand All @@ -49,7 +46,10 @@ fn main() -> Result<()> {

println!("Starting optimization of 2D Rosenbrock function");
println!("Initial point: {:?}", initial_point);
println!("Initial value: {:.6}", problem.evaluate_f64(&initial_point)?);
println!(
"Initial value: {:.6}",
problem.evaluate_f64(&initial_point)?
);

// Optimization loop
let mut iteration = 0;
Expand All @@ -63,7 +63,10 @@ fn main() -> Result<()> {
// Print progress
if iteration % 10 == 0 {
let f_val = problem.evaluate_f64(&initial_point)?;
println!("Iteration {}: f = {:.6}, ||∇f|| = {:.6}", iteration, f_val, grad_norm);
println!(
"Iteration {}: f = {:.6}, ||∇f|| = {:.6}",
iteration, f_val, grad_norm
);
}

// Check convergence
Expand All @@ -78,22 +81,31 @@ fn main() -> Result<()> {
let problem = problem.clone();
move |params: &[Tensor]| -> candle_core::Result<f64> {
let x_vec = params[0].to_vec1::<f64>()?;
problem.evaluate_f64(&x_vec).map_err(|e| candle_core::Error::Msg(e.to_string()))
problem
.evaluate_f64(&x_vec)
.map_err(|e| candle_core::Error::Msg(e.to_string()))
}
},
{
let problem = problem.clone();
let device = device.clone();
move |params: &[Tensor]| -> candle_core::Result<Vec<Tensor>> {
let x_vec = params[0].to_vec1::<f64>()?;
let grad = problem.gradient_f64(&x_vec).map_err(|e| candle_core::Error::Msg(e.to_string()))?;
Ok(vec![Tensor::from_slice(&grad, grad.len(), &device).map_err(|e| candle_core::Error::Msg(e.to_string()))?])
let grad = problem
.gradient_f64(&x_vec)
.map_err(|e| candle_core::Error::Msg(e.to_string()))?;
Ok(vec![Tensor::from_slice(&grad, grad.len(), &device)
.map_err(|e| candle_core::Error::Msg(e.to_string()))?])
}
},
));

// Convert Vec<f64> to Tensor for optimizer
let mut x_tensor = vec![Tensor::from_slice(&initial_point, initial_point.len(), &device)?];
let mut x_tensor = vec![Tensor::from_slice(
&initial_point,
initial_point.len(),
&device,
)?];

// Perform optimization step
let _step_result = optimizer.step(&mut x_tensor, function.clone())?;
Expand Down Expand Up @@ -122,7 +134,8 @@ fn main() -> Result<()> {

// Compare with known optimum
let optimum = vec![1.0, 1.0];
let distance_to_optimum = initial_point.iter()
let distance_to_optimum = initial_point
.iter()
.zip(&optimum)
.map(|(xi, opt)| (xi - opt).powi(2))
.sum::<f64>()
Expand All @@ -137,4 +150,4 @@ fn main() -> Result<()> {
}

Ok(())
}
}
67 changes: 38 additions & 29 deletions examples/custom_problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use anyhow::Result;
use candle_core::{Device, Tensor};
use qqn_optimizer::utils::math::DifferentiableFunction;
use qqn_optimizer::{
LBFGSConfig, LBFGSOptimizer, OptimizationProblem, Optimizer,
QQNConfig, QQNOptimizer,
LBFGSConfig, LBFGSOptimizer, OptimizationProblem, Optimizer, QQNConfig, QQNOptimizer,
};
use std::sync::Arc;

Expand All @@ -20,11 +19,11 @@ use std::sync::Arc;
pub struct QuadraticProblem {
name: String,
dimension: usize,
matrix_a: Vec<Vec<f64>>, // Positive definite matrix
vector_b: Vec<f64>, // Linear term
constant_c: f64, // Constant term
optimal_point: Vec<f64>, // Known optimal point: x* = -A^(-1) * b
optimal_value: f64, // Known optimal value
matrix_a: Vec<Vec<f64>>, // Positive definite matrix
vector_b: Vec<f64>, // Linear term
constant_c: f64, // Constant term
optimal_point: Vec<f64>, // Known optimal point: x* = -A^(-1) * b
optimal_value: f64, // Known optimal value
}

impl QuadraticProblem {
Expand All @@ -40,15 +39,14 @@ impl QuadraticProblem {
}

// Create a random linear term
let vector_b: Vec<f64> = (0..dimension)
.map(|i| (i as f64 + 1.0) * 0.1)
.collect();
let vector_b: Vec<f64> = (0..dimension).map(|i| (i as f64 + 1.0) * 0.1).collect();

let constant_c = 5.0;

// Compute optimal point: x* = -A^(-1) * b
// For diagonal A, this is simple: x*[i] = -b[i] / A[i][i]
let optimal_point: Vec<f64> = vector_b.iter()
let optimal_point: Vec<f64> = vector_b
.iter()
.enumerate()
.map(|(i, &bi)| -bi / matrix_a[i][i])
.collect();
Expand Down Expand Up @@ -137,23 +135,21 @@ impl OptimizationProblem for QuadraticProblem {
impl DifferentiableFunction for QuadraticProblem {
fn evaluate(&self, params: &[Tensor]) -> candle_core::Result<f64> {
// Convert tensors to f64 vector
let x: Result<Vec<f64>, _> = params.iter()
.map(|t| t.to_scalar::<f64>())
.collect();
let x: Result<Vec<f64>, _> = params.iter().map(|t| t.to_scalar::<f64>()).collect();
let x = x?;
// Evaluate using f64 implementation
let result = self.evaluate_f64(&x)
let result = self
.evaluate_f64(&x)
.map_err(|e| candle_core::Error::Msg(format!("Evaluation error: {}", e)))?;
Ok(result)
}
fn gradient(&self, params: &[Tensor]) -> candle_core::Result<Vec<Tensor>> {
// Convert tensors to f64 vector
let x: Result<Vec<f64>, _> = params.iter()
.map(|t| t.to_scalar::<f64>())
.collect();
let x: Result<Vec<f64>, _> = params.iter().map(|t| t.to_scalar::<f64>()).collect();
let x = x?;
// Compute gradient using f64 implementation
let grad = self.gradient_f64(&x)
let grad = self
.gradient_f64(&x)
.map_err(|e| candle_core::Error::Msg(format!("Gradient error: {}", e)))?;
// Convert back to tensors
grad.iter()
Expand All @@ -162,7 +158,6 @@ impl DifferentiableFunction for QuadraticProblem {
}
}


fn main() -> Result<()> {
println!("Custom Optimization Problem Example");
println!("===================================");
Expand Down Expand Up @@ -191,8 +186,14 @@ fn main() -> Result<()> {
)?;
// Compare results
println!("\n--- Comparison ---");
println!("QQN: {} iterations, final value: {:.6}", qqn_result.0, qqn_result.1);
println!("L-BFGS: {} iterations, final value: {:.6}", lbfgs_result.0, lbfgs_result.1);
println!(
"QQN: {} iterations, final value: {:.6}",
qqn_result.0, qqn_result.1
);
println!(
"L-BFGS: {} iterations, final value: {:.6}",
lbfgs_result.0, lbfgs_result.1
);
let qqn_error = (qqn_result.1 - problem.optimal_value().unwrap()).abs();
let lbfgs_error = (lbfgs_result.1 - problem.optimal_value().unwrap()).abs();
println!("QQN error: {:.2e}", qqn_error);
Expand All @@ -214,7 +215,8 @@ fn run_optimizer(
let initial_point = problem.initial_point();
let device = Device::Cpu;
// Convert initial point to tensors
let mut params: Vec<Tensor> = initial_point.iter()
let mut params: Vec<Tensor> = initial_point
.iter()
.map(|&val| Tensor::from_slice(&[val], (1,), &device))
.collect::<candle_core::Result<Vec<_>>>()
.map_err(|e| anyhow::anyhow!("Failed to create tensors: {}", e))?;
Expand All @@ -223,31 +225,38 @@ fn run_optimizer(
println!("Starting {} optimization...", name);
while iteration < max_iterations {
// Convert tensors back to f64 for convergence checking
let x: Vec<f64> = params.iter()
let x: Vec<f64> = params
.iter()
.map(|t| t.to_scalar::<f64>())
.collect::<candle_core::Result<Vec<_>>>()
.map_err(|e| anyhow::anyhow!("Failed to extract values: {}", e))?;
let gradient = problem.gradient_f64(&x)?;
let grad_norm = gradient.iter().map(|g| g * g).sum::<f64>().sqrt();
// Perform optimization step
let _step_result = optimizer.step(&mut params, problem.clone())
let _step_result = optimizer
.step(&mut params, problem.clone())
.map_err(|e| anyhow::anyhow!("Optimizer step failed: {}", e))?;
iteration += 1;
// Print progress occasionally
if iteration % 50 == 0 {
let x: Vec<f64> = params.iter()
let x: Vec<f64> = params
.iter()
.map(|t| t.to_scalar::<f64>())
.collect::<candle_core::Result<Vec<_>>>()
.map_err(|e| anyhow::anyhow!("Failed to extract values: {}", e))?;
let f_val = problem.evaluate_f64(&x)?;
println!(" Iteration {}: f = {:.6}, ||∇f|| = {:.2e}", iteration, f_val, grad_norm);
println!(
" Iteration {}: f = {:.6}, ||∇f|| = {:.2e}",
iteration, f_val, grad_norm
);
}
}
// Convert final parameters back to f64 for evaluation
let final_x: Vec<f64> = params.iter()
let final_x: Vec<f64> = params
.iter()
.map(|t| t.to_scalar::<f64>())
.collect::<candle_core::Result<Vec<_>>>()
.map_err(|e| anyhow::anyhow!("Failed to extract final values: {}", e))?;
let final_value = problem.evaluate_f64(&final_x)?;
Ok((iteration, final_value))
}
}
Loading
Loading