quantrs2_anneal/rl_embedding_optimizer/
embedding.rs1use std::collections::HashMap;
4use std::time::Instant;
5
6use super::error::{RLEmbeddingError, RLEmbeddingResult};
7use super::state_action::StateActionProcessor;
8use super::types::{EmbeddingAction, EmbeddingState, ObjectiveWeights};
9use crate::embedding::{Embedding, HardwareTopology};
10use crate::ising::IsingModel;
11
12pub struct EmbeddingOptimizer;
14
15impl EmbeddingOptimizer {
16 pub fn generate_initial_embedding(
18 problem: &IsingModel,
19 hardware: &HardwareTopology,
20 ) -> RLEmbeddingResult<Embedding> {
21 let mut embedding = Embedding {
23 chains: HashMap::new(),
24 qubit_to_variable: HashMap::new(),
25 };
26
27 for logical in 0..problem.num_qubits {
29 let physical = logical % StateActionProcessor::get_num_qubits(hardware);
30 embedding.chains.insert(logical, vec![physical]);
31 }
33
34 Ok(embedding)
35 }
36
37 pub fn calculate_reward(
39 old_embedding: &Embedding,
40 new_embedding: &Embedding,
41 hardware: &HardwareTopology,
42 objective_weights: &ObjectiveWeights,
43 ) -> RLEmbeddingResult<f64> {
44 let old_quality = Self::evaluate_embedding_quality(old_embedding, hardware)?;
45 let new_quality = Self::evaluate_embedding_quality(new_embedding, hardware)?;
46
47 let improvement = new_quality - old_quality;
48
49 let mut reward = 0.0;
51
52 reward += improvement * objective_weights.efficiency_weight;
54
55 let old_avg_chain_length = Self::calculate_average_chain_length(old_embedding);
57 let new_avg_chain_length = Self::calculate_average_chain_length(new_embedding);
58 let chain_penalty =
59 (new_avg_chain_length - old_avg_chain_length) * objective_weights.chain_length_weight;
60 reward -= chain_penalty;
61
62 let old_utilization = Self::calculate_hardware_utilization(old_embedding, hardware);
64 let new_utilization = Self::calculate_hardware_utilization(new_embedding, hardware);
65 let utilization_reward =
66 (new_utilization - old_utilization) * objective_weights.utilization_weight;
67 reward += utilization_reward;
68
69 Ok(reward)
70 }
71
72 pub fn evaluate_embedding_quality(
74 embedding: &Embedding,
75 hardware: &HardwareTopology,
76 ) -> RLEmbeddingResult<f64> {
77 let mut quality = 0.0;
78
79 let avg_chain_length = Self::calculate_average_chain_length(embedding);
81 quality -= avg_chain_length * 0.1;
82
83 let utilization = Self::calculate_hardware_utilization(embedding, hardware);
85 quality += utilization * 0.5;
86
87 let connectivity = Self::calculate_connectivity_preservation(embedding, hardware);
89 quality += connectivity * 0.3;
90
91 let compactness = Self::calculate_embedding_compactness(embedding, hardware);
93 quality += compactness * 0.1;
94
95 Ok(quality)
96 }
97
98 pub fn calculate_average_chain_length(embedding: &Embedding) -> f64 {
100 if embedding.chains.is_empty() {
101 return 0.0;
102 }
103
104 let total_length: usize = embedding.chains.values().map(std::vec::Vec::len).sum();
105 total_length as f64 / embedding.chains.len() as f64
106 }
107
108 #[must_use]
110 pub fn calculate_hardware_utilization(
111 embedding: &Embedding,
112 hardware: &HardwareTopology,
113 ) -> f64 {
114 let used_qubits: std::collections::HashSet<usize> =
115 embedding.chains.values().flatten().copied().collect();
116
117 used_qubits.len() as f64 / StateActionProcessor::get_num_qubits(hardware) as f64
118 }
119
120 fn calculate_connectivity_preservation(
122 embedding: &Embedding,
123 hardware: &HardwareTopology,
124 ) -> f64 {
125 if embedding.chains.is_empty() {
127 0.0
128 } else {
129 0.8 }
131 }
132
133 fn calculate_embedding_compactness(embedding: &Embedding, hardware: &HardwareTopology) -> f64 {
135 let avg_chain_length = Self::calculate_average_chain_length(embedding);
137 1.0 / (1.0 + avg_chain_length)
138 }
139
140 pub fn update_state(
142 old_state: &EmbeddingState,
143 action: &EmbeddingAction,
144 new_embedding: &Embedding,
145 hardware: &HardwareTopology,
146 ) -> RLEmbeddingResult<EmbeddingState> {
147 let mut new_state = old_state.clone();
148
149 new_state.embedding_state.logical_to_physical = new_embedding.chains.clone();
151 new_state.embedding_state.chain_lengths = new_embedding
152 .chains
153 .values()
154 .map(std::vec::Vec::len)
155 .collect();
156
157 new_state
159 .embedding_state
160 .efficiency_metrics
161 .avg_chain_length = Self::calculate_average_chain_length(new_embedding);
162 new_state
163 .embedding_state
164 .efficiency_metrics
165 .max_chain_length = new_embedding
166 .chains
167 .values()
168 .map(std::vec::Vec::len)
169 .max()
170 .unwrap_or(0);
171 new_state
172 .embedding_state
173 .efficiency_metrics
174 .utilization_ratio = Self::calculate_hardware_utilization(new_embedding, hardware);
175
176 new_state.embedding_state.quality_score =
178 Self::evaluate_embedding_quality(new_embedding, hardware)?;
179
180 new_state
182 .performance_history
183 .push(new_state.embedding_state.quality_score);
184 if new_state.performance_history.len() > 10 {
185 new_state.performance_history.remove(0);
186 }
187
188 Ok(new_state)
189 }
190
191 #[must_use]
193 pub fn is_terminal_state(state: &EmbeddingState) -> bool {
194 state.embedding_state.quality_score > 0.95
196 || (state.performance_history.len() >= 5
197 && state
198 .performance_history
199 .windows(2)
200 .all(|w| (w[1] - w[0]).abs() < 0.001))
201 }
202
203 #[must_use]
205 pub fn calculate_problem_density(problem: &IsingModel) -> f64 {
206 let mut num_edges = 0;
207 for i in 0..problem.num_qubits {
208 for j in (i + 1)..problem.num_qubits {
209 if let Ok(coupling) = problem.get_coupling(i, j) {
210 if coupling.abs() > 1e-10 {
211 num_edges += 1;
212 }
213 }
214 }
215 }
216
217 2.0 * f64::from(num_edges) / (problem.num_qubits * (problem.num_qubits - 1)) as f64
218 }
219
220 #[must_use]
222 pub fn classify_problem_type(problem: &IsingModel) -> String {
223 let density = Self::calculate_problem_density(problem);
224
225 if density > 0.8 {
226 "dense_random".to_string()
227 } else if density < 0.1 {
228 "sparse_structured".to_string()
229 } else if problem.num_qubits < 50 {
230 "small_optimization".to_string()
231 } else {
232 "large_optimization".to_string()
233 }
234 }
235}