use std::collections::HashMap;
use std::time::Instant;
use super::error::{RLEmbeddingError, RLEmbeddingResult};
use super::state_action::StateActionProcessor;
use super::types::{EmbeddingAction, EmbeddingState, ObjectiveWeights};
use crate::embedding::{Embedding, HardwareTopology};
use crate::ising::IsingModel;
pub struct EmbeddingOptimizer;
impl EmbeddingOptimizer {
pub fn generate_initial_embedding(
problem: &IsingModel,
hardware: &HardwareTopology,
) -> RLEmbeddingResult<Embedding> {
let mut embedding = Embedding {
chains: HashMap::new(),
qubit_to_variable: HashMap::new(),
};
for logical in 0..problem.num_qubits {
let physical = logical % StateActionProcessor::get_num_qubits(hardware);
embedding.chains.insert(logical, vec![physical]);
}
Ok(embedding)
}
pub fn calculate_reward(
old_embedding: &Embedding,
new_embedding: &Embedding,
hardware: &HardwareTopology,
objective_weights: &ObjectiveWeights,
) -> RLEmbeddingResult<f64> {
let old_quality = Self::evaluate_embedding_quality(old_embedding, hardware)?;
let new_quality = Self::evaluate_embedding_quality(new_embedding, hardware)?;
let improvement = new_quality - old_quality;
let mut reward = 0.0;
reward += improvement * objective_weights.efficiency_weight;
let old_avg_chain_length = Self::calculate_average_chain_length(old_embedding);
let new_avg_chain_length = Self::calculate_average_chain_length(new_embedding);
let chain_penalty =
(new_avg_chain_length - old_avg_chain_length) * objective_weights.chain_length_weight;
reward -= chain_penalty;
let old_utilization = Self::calculate_hardware_utilization(old_embedding, hardware);
let new_utilization = Self::calculate_hardware_utilization(new_embedding, hardware);
let utilization_reward =
(new_utilization - old_utilization) * objective_weights.utilization_weight;
reward += utilization_reward;
Ok(reward)
}
pub fn evaluate_embedding_quality(
embedding: &Embedding,
hardware: &HardwareTopology,
) -> RLEmbeddingResult<f64> {
let mut quality = 0.0;
let avg_chain_length = Self::calculate_average_chain_length(embedding);
quality -= avg_chain_length * 0.1;
let utilization = Self::calculate_hardware_utilization(embedding, hardware);
quality += utilization * 0.5;
let connectivity = Self::calculate_connectivity_preservation(embedding, hardware);
quality += connectivity * 0.3;
let compactness = Self::calculate_embedding_compactness(embedding, hardware);
quality += compactness * 0.1;
Ok(quality)
}
pub fn calculate_average_chain_length(embedding: &Embedding) -> f64 {
if embedding.chains.is_empty() {
return 0.0;
}
let total_length: usize = embedding.chains.values().map(std::vec::Vec::len).sum();
total_length as f64 / embedding.chains.len() as f64
}
#[must_use]
pub fn calculate_hardware_utilization(
embedding: &Embedding,
hardware: &HardwareTopology,
) -> f64 {
let used_qubits: std::collections::HashSet<usize> =
embedding.chains.values().flatten().copied().collect();
used_qubits.len() as f64 / StateActionProcessor::get_num_qubits(hardware) as f64
}
fn calculate_connectivity_preservation(
embedding: &Embedding,
hardware: &HardwareTopology,
) -> f64 {
if embedding.chains.is_empty() {
0.0
} else {
0.8 }
}
fn calculate_embedding_compactness(embedding: &Embedding, hardware: &HardwareTopology) -> f64 {
let avg_chain_length = Self::calculate_average_chain_length(embedding);
1.0 / (1.0 + avg_chain_length)
}
pub fn update_state(
old_state: &EmbeddingState,
action: &EmbeddingAction,
new_embedding: &Embedding,
hardware: &HardwareTopology,
) -> RLEmbeddingResult<EmbeddingState> {
let mut new_state = old_state.clone();
new_state.embedding_state.logical_to_physical = new_embedding.chains.clone();
new_state.embedding_state.chain_lengths = new_embedding
.chains
.values()
.map(std::vec::Vec::len)
.collect();
new_state
.embedding_state
.efficiency_metrics
.avg_chain_length = Self::calculate_average_chain_length(new_embedding);
new_state
.embedding_state
.efficiency_metrics
.max_chain_length = new_embedding
.chains
.values()
.map(std::vec::Vec::len)
.max()
.unwrap_or(0);
new_state
.embedding_state
.efficiency_metrics
.utilization_ratio = Self::calculate_hardware_utilization(new_embedding, hardware);
new_state.embedding_state.quality_score =
Self::evaluate_embedding_quality(new_embedding, hardware)?;
new_state
.performance_history
.push(new_state.embedding_state.quality_score);
if new_state.performance_history.len() > 10 {
new_state.performance_history.remove(0);
}
Ok(new_state)
}
#[must_use]
pub fn is_terminal_state(state: &EmbeddingState) -> bool {
state.embedding_state.quality_score > 0.95
|| (state.performance_history.len() >= 5
&& state
.performance_history
.windows(2)
.all(|w| (w[1] - w[0]).abs() < 0.001))
}
#[must_use]
pub fn calculate_problem_density(problem: &IsingModel) -> f64 {
let mut num_edges = 0;
for i in 0..problem.num_qubits {
for j in (i + 1)..problem.num_qubits {
if let Ok(coupling) = problem.get_coupling(i, j) {
if coupling.abs() > 1e-10 {
num_edges += 1;
}
}
}
}
2.0 * f64::from(num_edges) / (problem.num_qubits * (problem.num_qubits - 1)) as f64
}
#[must_use]
pub fn classify_problem_type(problem: &IsingModel) -> String {
let density = Self::calculate_problem_density(problem);
if density > 0.8 {
"dense_random".to_string()
} else if density < 0.1 {
"sparse_structured".to_string()
} else if problem.num_qubits < 50 {
"small_optimization".to_string()
} else {
"large_optimization".to_string()
}
}
}