Skip to main content

scirs2_vision/registration/
optimization.rs

1//! Optimization algorithms for registration
2
3use crate::error::Result;
4use scirs2_core::ndarray::Array1;
5
6/// Optimization result
7#[derive(Debug, Clone)]
8pub struct OptimizationResult {
9    /// Optimized parameter values
10    pub parameters: Array1<f64>,
11    /// Final cost function value
12    pub final_cost: f64,
13    /// Number of iterations performed
14    pub iterations: usize,
15    /// Whether optimization converged
16    pub converged: bool,
17}
18
19/// Gradient descent optimization
20#[allow(dead_code)]
21pub fn gradient_descent_optimize(
22    initialparams: &Array1<f64>,
23    cost_function: &dyn Fn(&Array1<f64>) -> Result<f64>,
24    gradient_function: &dyn Fn(&Array1<f64>) -> Result<Array1<f64>>,
25    learning_rate: f64,
26    maxiterations: usize,
27    tolerance: f64,
28) -> Result<OptimizationResult> {
29    use scirs2_core::ndarray::Zip;
30
31    let mut params = initialparams.clone();
32    let mut prev_cost = cost_function(&params)?;
33    let mut converged = false;
34    let mut iterations = 0;
35
36    for i in 0..maxiterations {
37        iterations = i + 1;
38
39        // Compute gradient
40        let gradient = gradient_function(&params)?;
41
42        // Update parameters: params = params - learning_rate * gradient
43        Zip::from(&mut params)
44            .and(&gradient)
45            .for_each(|p, &g| *p -= learning_rate * g);
46
47        // Compute new cost
48        let current_cost = cost_function(&params)?;
49
50        // Check convergence
51        if (prev_cost - current_cost).abs() < tolerance {
52            converged = true;
53            break;
54        }
55
56        // Check if cost is increasing (diverging)
57        if current_cost > prev_cost {
58            // Optionally implement adaptive learning _rate here
59            // For now, just continue with fixed learning _rate
60        }
61
62        prev_cost = current_cost;
63    }
64
65    Ok(OptimizationResult {
66        parameters: params,
67        final_cost: prev_cost,
68        iterations,
69        converged,
70    })
71}
72
73/// Powell's method optimization
74#[allow(dead_code)]
75pub fn powell_optimize(
76    initialparams: &Array1<f64>,
77    cost_function: &dyn Fn(&Array1<f64>) -> Result<f64>,
78    maxiterations: usize,
79    tolerance: f64,
80) -> Result<OptimizationResult> {
81    let n = initialparams.len();
82    let mut params = initialparams.clone();
83    let mut directions = scirs2_core::ndarray::Array2::eye(n);
84    let mut converged = false;
85    let mut iterations = 0;
86    let mut prev_cost = cost_function(&params)?;
87
88    for iter in 0..maxiterations {
89        iterations = iter + 1;
90        let startparams = params.clone();
91        let mut biggest_decrease = 0.0;
92        let mut biggest_decrease_idx = 0;
93
94        // Line search along each direction
95        for i in 0..n {
96            let old_cost = cost_function(&params)?;
97            let direction = directions.row(i).to_owned();
98
99            // Perform line search along this direction
100            let (newparams, new_cost) = line_search(&params, &direction, cost_function, 1e-6)?;
101            params = newparams;
102
103            let decrease = old_cost - new_cost;
104            if decrease > biggest_decrease {
105                biggest_decrease = decrease;
106                biggest_decrease_idx = i;
107            }
108        }
109
110        // Check convergence
111        let current_cost = cost_function(&params)?;
112        if (prev_cost - current_cost).abs() < tolerance {
113            converged = true;
114            break;
115        }
116
117        // Update search directions
118        if iter > 0 && iter % n == 0 {
119            // Calculate new direction
120            let new_direction = &params - &startparams;
121            let new_dir_norm = new_direction.dot(&new_direction).sqrt();
122
123            if new_dir_norm > 1e-10 {
124                // Replace the direction that gave the biggest decrease
125                let normalized_dir = &new_direction / new_dir_norm;
126                directions
127                    .row_mut(biggest_decrease_idx)
128                    .assign(&normalized_dir);
129
130                // Perform line search along the new direction
131                let (newparams_, _) = line_search(&params, &normalized_dir, cost_function, 1e-6)?;
132                params = newparams_;
133            }
134        }
135
136        prev_cost = current_cost;
137    }
138
139    Ok(OptimizationResult {
140        parameters: params,
141        final_cost: prev_cost,
142        iterations,
143        converged,
144    })
145}
146
147/// Perform line search along a direction
148#[allow(dead_code)]
149fn line_search(
150    start_point: &Array1<f64>,
151    direction: &Array1<f64>,
152    cost_function: &dyn Fn(&Array1<f64>) -> Result<f64>,
153    tolerance: f64,
154) -> Result<(Array1<f64>, f64)> {
155    // Golden section search
156    const GOLDEN_RATIO: f64 = 0.618033988749895;
157
158    // Bracket the minimum
159    let mut a = 0.0;
160    let mut b = 1.0;
161    let mut c = a + GOLDEN_RATIO * (b - a);
162    let mut d = b - GOLDEN_RATIO * (b - a);
163
164    // Evaluate at initial points
165    let mut fc = cost_function(&(start_point + c * direction))?;
166    let mut fd = cost_function(&(start_point + d * direction))?;
167
168    // Golden section search
169    while (b - a).abs() > tolerance {
170        if fc < fd {
171            b = d;
172            d = c;
173            fd = fc;
174            c = a + GOLDEN_RATIO * (b - a);
175            fc = cost_function(&(start_point + c * direction))?;
176        } else {
177            a = c;
178            c = d;
179            fc = fd;
180            d = b - GOLDEN_RATIO * (b - a);
181            fd = cost_function(&(start_point + d * direction))?;
182        }
183    }
184
185    // Return the optimal _point
186    let alpha = (a + b) / 2.0;
187    let optimal_point = start_point + alpha * direction;
188    let optimal_cost = cost_function(&optimal_point)?;
189
190    Ok((optimal_point, optimal_cost))
191}