pub struct QuantumSearch {
_private: (),
}
impl QuantumSearch {
pub fn new() -> Self {
Self { _private: () }
}
pub fn optimal_iterations(&self, search_space_size: u32) -> u32 {
if search_space_size <= 1 {
return 1;
}
let n = search_space_size as f64;
let iters = (std::f64::consts::FRAC_PI_4 * n.sqrt()).floor() as u32;
iters.max(1)
}
pub fn diversity_select(
&self,
scores: &[(String, f32)],
k: usize,
) -> Vec<(String, f32)> {
if scores.is_empty() || k == 0 {
return Vec::new();
}
let k = k.min(scores.len());
#[cfg(not(target_arch = "wasm32"))]
{
if k <= 8 {
if let Some(result) = self.qaoa_diversity_select(scores, k) {
return result;
}
}
}
self.greedy_diversity_select(scores, k)
}
pub fn amplitude_boost(
&self,
scores: &mut [(String, f32)],
target_threshold: f32,
) {
if scores.is_empty() {
return;
}
let above_count = scores.iter().filter(|(_, s)| *s >= target_threshold).count();
let below_count = scores.len() - above_count;
if above_count == 0 || below_count == 0 {
return;
}
let boost_factor = (scores.len() as f64 / above_count as f64)
.clamp(1.5, 4.0);
let sqrt_boost = (boost_factor).sqrt() as f32;
let inv_sqrt_boost = 1.0 / sqrt_boost;
for (_id, score) in scores.iter_mut() {
if *score >= target_threshold {
*score *= sqrt_boost;
} else {
*score *= inv_sqrt_boost;
}
}
let max_score = scores
.iter()
.map(|(_, s)| *s)
.fold(f32::NEG_INFINITY, f32::max);
let min_score = scores
.iter()
.map(|(_, s)| *s)
.fold(f32::INFINITY, f32::min);
let range = max_score - min_score;
if range > f32::EPSILON {
for (_id, score) in scores.iter_mut() {
*score = (*score - min_score) / range;
}
} else {
for (_id, score) in scores.iter_mut() {
*score = 1.0;
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn qaoa_diversity_select(
&self,
scores: &[(String, f32)],
k: usize,
) -> Option<Vec<(String, f32)>> {
use ruqu_algorithms::{Graph, QaoaConfig, run_qaoa};
let n = scores.len();
if n < 2 {
return Some(scores.to_vec());
}
let mut graph = Graph::new(n as u32);
for i in 0..n {
for j in (i + 1)..n {
let similarity = 1.0 - (scores[i].1 - scores[j].1).abs();
graph.add_edge(i as u32, j as u32, similarity as f64);
}
}
let config = QaoaConfig {
graph,
p: 2,
max_iterations: 50,
learning_rate: 0.1,
seed: Some(42),
};
let result = run_qaoa(&config).ok()?;
let partition_true: Vec<usize> = result
.best_bitstring
.iter()
.enumerate()
.filter(|(_, &b)| b)
.map(|(i, _)| i)
.collect();
let partition_false: Vec<usize> = result
.best_bitstring
.iter()
.enumerate()
.filter(|(_, &b)| !b)
.map(|(i, _)| i)
.collect();
let chosen = if (partition_true.len() as isize - k as isize).unsigned_abs()
<= (partition_false.len() as isize - k as isize).unsigned_abs()
{
partition_true
} else {
partition_false
};
if chosen.len() < k {
return None;
}
let mut selected: Vec<(String, f32)> = chosen
.iter()
.map(|&i| scores[i].clone())
.collect();
selected.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
selected.truncate(k);
Some(selected)
}
fn greedy_diversity_select(
&self,
scores: &[(String, f32)],
k: usize,
) -> Vec<(String, f32)> {
let mut remaining: Vec<(usize, &(String, f32))> =
scores.iter().enumerate().collect();
remaining.sort_by(|a, b| b.1 .1.partial_cmp(&a.1 .1).unwrap_or(std::cmp::Ordering::Equal));
let mut selected: Vec<(String, f32)> = Vec::with_capacity(k);
if let Some((_, first)) = remaining.first() {
selected.push((*first).clone());
}
let first_idx = remaining.first().map(|(i, _)| *i);
remaining.retain(|(i, _)| Some(*i) != first_idx);
while selected.len() < k && !remaining.is_empty() {
let mut best_idx_in_remaining = 0;
let mut best_value = f64::NEG_INFINITY;
for (ri, (_, candidate)) in remaining.iter().enumerate() {
let min_dist: f32 = selected
.iter()
.map(|(_, sel_score)| (candidate.1 - sel_score).abs())
.fold(f32::INFINITY, f32::min);
let value = candidate.1 as f64 + min_dist as f64;
if value > best_value {
best_value = value;
best_idx_in_remaining = ri;
}
}
let (_, picked) = remaining.remove(best_idx_in_remaining);
selected.push(picked.clone());
}
selected
}
}
impl Default for QuantumSearch {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_optimal_iterations_basic() {
let qs = QuantumSearch::new();
assert_eq!(qs.optimal_iterations(1), 1);
assert_eq!(qs.optimal_iterations(4), 1); }
#[test]
fn test_optimal_iterations_larger() {
let qs = QuantumSearch::new();
assert_eq!(qs.optimal_iterations(100), 7);
}
#[test]
fn test_diversity_select_empty() {
let qs = QuantumSearch::new();
let result = qs.diversity_select(&[], 3);
assert!(result.is_empty());
}
#[test]
fn test_diversity_select_k_zero() {
let qs = QuantumSearch::new();
let scores = vec![("a".to_string(), 0.5)];
let result = qs.diversity_select(&scores, 0);
assert!(result.is_empty());
}
#[test]
fn test_amplitude_boost_empty() {
let qs = QuantumSearch::new();
let mut scores: Vec<(String, f32)> = Vec::new();
qs.amplitude_boost(&mut scores, 0.5);
assert!(scores.is_empty());
}
#[test]
fn test_amplitude_boost_all_above() {
let qs = QuantumSearch::new();
let mut scores = vec![
("a".to_string(), 0.8),
("b".to_string(), 0.9),
];
let orig = scores.clone();
qs.amplitude_boost(&mut scores, 0.5);
assert_eq!(scores[0].0, orig[0].0);
assert_eq!(scores[1].0, orig[1].0);
}
}