use crate::circuit_interfaces::{InterfaceCircuit, InterfaceGate, InterfaceGateType};
use crate::error::Result;
#[cfg(feature = "optimize")]
use crate::optirs_integration::{OptiRSConfig, OptiRSQuantumOptimizer};
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::prelude::*;
use scirs2_core::Complex64;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use super::types::{
ParameterDatabase, QAOAConfig, QAOAConstraint, QAOAGraph, QAOAInitializationStrategy,
QAOAOptimizationStrategy, QAOAProblemType, QAOAResult, QAOAStats, QuantumAdvantageMetrics,
};
use super::qaoaoptimizer_type::QAOAOptimizer;
impl QAOAOptimizer {
pub fn new(
config: QAOAConfig,
graph: QAOAGraph,
problem_type: QAOAProblemType,
) -> Result<Self> {
let gammas = Self::initialize_gammas(&config, &graph)?;
let betas = Self::initialize_betas(&config, &graph)?;
Ok(Self {
config,
graph,
problem_type,
gammas: gammas.clone(),
betas: betas.clone(),
best_gammas: gammas,
best_betas: betas,
best_cost: f64::NEG_INFINITY,
classical_optimum: None,
stats: QAOAStats {
total_time: Duration::new(0, 0),
layer_times: Vec::new(),
circuit_depths: Vec::new(),
parameter_sensitivity: HashMap::new(),
quantum_advantage: QuantumAdvantageMetrics {
classical_time: Duration::new(0, 0),
speedup_factor: 1.0,
success_probability: 0.0,
quantum_volume: 0,
},
},
parameter_database: Arc::new(Mutex::new(ParameterDatabase {
parameters: HashMap::new(),
})),
#[cfg(feature = "optimize")]
optirs_optimizer: None,
})
}
pub fn optimize(&mut self) -> Result<QAOAResult> {
let start_time = Instant::now();
let mut cost_history = Vec::new();
let mut parameter_history = Vec::new();
if self.config.parameter_transfer {
self.apply_parameter_transfer()?;
}
let classical_start = Instant::now();
let classical_result = self.solve_classically()?;
self.stats.quantum_advantage.classical_time = classical_start.elapsed();
self.classical_optimum = Some(classical_result);
let mut current_layers = self.config.num_layers;
for iteration in 0..self.config.max_iterations {
let cost = self.evaluate_qaoa_cost(&self.gammas, &self.betas)?;
cost_history.push(cost);
parameter_history.push((self.gammas.clone(), self.betas.clone()));
if cost > self.best_cost {
self.best_cost = cost;
self.best_gammas = self.gammas.clone();
self.best_betas = self.betas.clone();
}
if iteration > 10 {
let recent_improvement = cost_history[iteration] - cost_history[iteration - 10];
if recent_improvement.abs() < self.config.convergence_tolerance {
break;
}
}
match self.config.optimization_strategy {
QAOAOptimizationStrategy::Classical => {
self.classical_parameter_optimization()?;
}
QAOAOptimizationStrategy::Quantum => {
self.quantum_parameter_optimization()?;
}
QAOAOptimizationStrategy::Hybrid => {
self.hybrid_parameter_optimization()?;
}
QAOAOptimizationStrategy::MLGuided => {
self.ml_guided_optimization()?;
}
QAOAOptimizationStrategy::Adaptive => {
self.adaptive_parameter_optimization(&cost_history)?;
}
#[cfg(feature = "optimize")]
QAOAOptimizationStrategy::OptiRS => {
self.optirs_parameter_optimization()?;
}
}
if self.config.adaptive_layers
&& iteration % 20 == 19
&& self.should_add_layer(&cost_history)?
&& current_layers < self.config.max_adaptive_layers
{
current_layers += 1;
self.add_qaoa_layer()?;
}
}
let total_time = start_time.elapsed();
self.stats.total_time = total_time;
let final_circuit = self.generate_qaoa_circuit(&self.best_gammas, &self.best_betas)?;
let final_state = self.simulate_circuit(&final_circuit)?;
let probabilities = self.extract_probabilities(&final_state)?;
let best_solution = self.extract_best_solution(&probabilities)?;
let solution_quality = self.evaluate_solution_quality(&best_solution, &probabilities)?;
let approximation_ratio = if let Some(classical_opt) = self.classical_optimum {
self.best_cost / classical_opt
} else {
1.0
};
if self.config.parameter_transfer {
self.store_parameters_for_transfer()?;
}
let function_evaluations = cost_history.len();
Ok(QAOAResult {
optimal_gammas: self.best_gammas.clone(),
optimal_betas: self.best_betas.clone(),
best_cost: self.best_cost,
approximation_ratio,
cost_history,
parameter_history,
final_probabilities: probabilities,
best_solution,
solution_quality,
optimization_time: total_time,
function_evaluations,
converged: true,
})
}
pub(super) fn generate_qaoa_circuit(
&self,
gammas: &[f64],
betas: &[f64],
) -> Result<InterfaceCircuit> {
let num_qubits = self.graph.num_vertices;
let mut circuit = InterfaceCircuit::new(num_qubits, 0);
match self.config.initialization {
QAOAInitializationStrategy::UniformSuperposition => {
for qubit in 0..num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![qubit]));
}
}
QAOAInitializationStrategy::WarmStart => {
self.prepare_warm_start_state(&mut circuit)?;
}
QAOAInitializationStrategy::AdiabaticStart => {
self.prepare_adiabatic_state(&mut circuit)?;
}
QAOAInitializationStrategy::Random => {
self.prepare_random_state(&mut circuit)?;
}
QAOAInitializationStrategy::ProblemSpecific => {
self.prepare_problem_specific_state(&mut circuit)?;
}
}
for layer in 0..gammas.len() {
self.apply_cost_layer(&mut circuit, gammas[layer])?;
self.apply_mixer_layer(&mut circuit, betas[layer])?;
}
Ok(circuit)
}
pub(super) fn apply_maxcut_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
for i in 0..self.graph.num_vertices {
for j in i + 1..self.graph.num_vertices {
let weight = self
.graph
.edge_weights
.get(&(i, j))
.or_else(|| self.graph.edge_weights.get(&(j, i)))
.unwrap_or(&self.graph.adjacency_matrix[[i, j]]);
if weight.abs() > 1e-10 {
let angle = gamma * weight;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
}
}
}
Ok(())
}
pub(super) fn apply_mwis_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
for i in 0..self.graph.num_vertices {
let weight = self.graph.vertex_weights.get(i).unwrap_or(&1.0);
let angle = gamma * weight;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![i]));
}
let penalty = 10.0;
for i in 0..self.graph.num_vertices {
for j in i + 1..self.graph.num_vertices {
if self.graph.adjacency_matrix[[i, j]] > 0.0 {
let angle = gamma * penalty;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
}
}
}
Ok(())
}
pub(super) fn apply_tsp_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
let num_cities = (self.graph.num_vertices as f64).sqrt() as usize;
for t in 0..num_cities {
for i in 0..num_cities {
for j in 0..num_cities {
if i != j {
let distance = self.graph.adjacency_matrix[[i, j]];
if distance > 0.0 {
let angle = gamma * distance;
let qubit_i_t = i * num_cities + t;
let qubit_j_t1 = j * num_cities + ((t + 1) % num_cities);
if qubit_i_t < circuit.num_qubits && qubit_j_t1 < circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit_i_t, qubit_j_t1],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RZ(angle),
vec![qubit_j_t1],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit_i_t, qubit_j_t1],
));
}
}
}
}
}
}
let penalty = 10.0;
for i in 0..num_cities {
for t1 in 0..num_cities {
for t2 in t1 + 1..num_cities {
let qubit1 = i * num_cities + t1;
let qubit2 = i * num_cities + t2;
if qubit1 < circuit.num_qubits && qubit2 < circuit.num_qubits {
let angle = gamma * penalty;
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit1, qubit2],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RZ(angle),
vec![qubit2],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit1, qubit2],
));
}
}
}
}
Ok(())
}
pub(super) fn apply_portfolio_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
let lambda = 1.0;
for i in 0..self.graph.num_vertices {
let return_rate = self.graph.vertex_weights.get(i).unwrap_or(&0.1);
let angle = -gamma * return_rate;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![i]));
}
for i in 0..self.graph.num_vertices {
for j in i..self.graph.num_vertices {
let covariance = self.graph.adjacency_matrix[[i, j]];
if covariance.abs() > 1e-10 {
let angle = gamma * lambda * covariance;
if i == j {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![i]));
} else {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
}
}
}
}
Ok(())
}
pub(super) fn apply_3sat_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
for constraint in &self.graph.constraints {
if let QAOAConstraint::LinearConstraint {
coefficients,
bound,
} = constraint
{
let angle = gamma * bound;
for (i, &coeff) in coefficients.iter().enumerate() {
if i < circuit.num_qubits && coeff.abs() > 1e-10 {
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RZ(angle * coeff),
vec![i],
));
}
}
}
}
Ok(())
}
pub(super) fn apply_qubo_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
for i in 0..self.graph.num_vertices {
let coeff = self.graph.adjacency_matrix[[i, i]];
if coeff.abs() > 1e-10 {
let angle = gamma * coeff;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![i]));
}
}
for i in 0..self.graph.num_vertices {
for j in i + 1..self.graph.num_vertices {
let coeff = self.graph.adjacency_matrix[[i, j]];
if coeff.abs() > 1e-10 {
let angle = gamma * coeff;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
}
}
}
Ok(())
}
pub(super) fn apply_generic_cost_layer(
&self,
circuit: &mut InterfaceCircuit,
gamma: f64,
) -> Result<()> {
for i in 0..self.graph.num_vertices {
for j in i + 1..self.graph.num_vertices {
let weight = self.graph.adjacency_matrix[[i, j]];
if weight.abs() > 1e-10 {
let angle = gamma * weight;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(angle), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
}
}
}
Ok(())
}
pub(super) fn apply_standard_mixer(
&self,
circuit: &mut InterfaceCircuit,
beta: f64,
) -> Result<()> {
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RX(beta), vec![qubit]));
}
Ok(())
}
pub(super) fn apply_xy_mixer(&self, circuit: &mut InterfaceCircuit, beta: f64) -> Result<()> {
for i in 0..circuit.num_qubits {
for j in i + 1..circuit.num_qubits {
if self.graph.adjacency_matrix[[i, j]] > 0.0 {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![i]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(beta), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![i]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![j]));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(std::f64::consts::PI / 2.0),
vec![i],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(std::f64::consts::PI / 2.0),
vec![j],
));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(beta), vec![j]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, j]));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(-std::f64::consts::PI / 2.0),
vec![i],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(-std::f64::consts::PI / 2.0),
vec![j],
));
}
}
}
Ok(())
}
pub(super) fn apply_ring_mixer(&self, circuit: &mut InterfaceCircuit, beta: f64) -> Result<()> {
for i in 0..circuit.num_qubits {
let next = (i + 1) % circuit.num_qubits;
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![i]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![next]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, next]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RZ(beta), vec![next]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, next]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![i]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![next]));
}
Ok(())
}
pub(super) fn apply_grover_mixer(
&self,
circuit: &mut InterfaceCircuit,
beta: f64,
) -> Result<()> {
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![qubit]));
}
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::PauliZ, vec![qubit]));
}
if circuit.num_qubits > 1 {
let controls: Vec<usize> = (0..circuit.num_qubits - 1).collect();
let target = circuit.num_qubits - 1;
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![controls[0], target],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RZ(beta),
vec![target],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![controls[0], target],
));
}
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::PauliZ, vec![qubit]));
}
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![qubit]));
}
Ok(())
}
pub(super) fn apply_dicke_mixer(
&self,
circuit: &mut InterfaceCircuit,
beta: f64,
) -> Result<()> {
for i in 0..circuit.num_qubits - 1 {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, i + 1]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RY(beta), vec![i]));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i + 1, i]));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(-beta),
vec![i + 1],
));
circuit.add_gate(InterfaceGate::new(InterfaceGateType::CNOT, vec![i, i + 1]));
}
Ok(())
}
pub(super) fn apply_tsp_custom_mixer(
&self,
circuit: &mut InterfaceCircuit,
beta: f64,
) -> Result<()> {
let num_cities = (circuit.num_qubits as f64).sqrt() as usize;
for t in 0..num_cities {
for i in 0..num_cities {
for j in i + 1..num_cities {
let qubit_i = i * num_cities + t;
let qubit_j = j * num_cities + t;
if qubit_i < circuit.num_qubits && qubit_j < circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit_i, qubit_j],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(beta),
vec![qubit_i],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit_j, qubit_i],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(-beta),
vec![qubit_j],
));
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CNOT,
vec![qubit_i, qubit_j],
));
}
}
}
}
Ok(())
}
pub(super) fn apply_portfolio_custom_mixer(
&self,
circuit: &mut InterfaceCircuit,
beta: f64,
) -> Result<()> {
for i in 0..circuit.num_qubits - 1 {
for j in i + 1..circuit.num_qubits {
let correlation = self.graph.adjacency_matrix[[i, j]].abs();
if correlation > 0.1 {
let angle = beta * correlation;
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::CRY(angle),
vec![i, j],
));
}
}
}
Ok(())
}
pub(super) fn initialize_gammas(config: &QAOAConfig, _graph: &QAOAGraph) -> Result<Vec<f64>> {
let mut gammas = Vec::with_capacity(config.num_layers);
for i in 0..config.num_layers {
let gamma = match config.initialization {
QAOAInitializationStrategy::Random => {
(thread_rng().random::<f64>() - 0.5) * std::f64::consts::PI
}
QAOAInitializationStrategy::AdiabaticStart => {
0.1 * (i + 1) as f64 / config.num_layers as f64
}
_ => 0.5 * (i + 1) as f64 / config.num_layers as f64,
};
gammas.push(gamma);
}
Ok(gammas)
}
pub(super) fn initialize_betas(config: &QAOAConfig, _graph: &QAOAGraph) -> Result<Vec<f64>> {
let mut betas = Vec::with_capacity(config.num_layers);
for i in 0..config.num_layers {
let beta = match config.initialization {
QAOAInitializationStrategy::Random => {
(thread_rng().random::<f64>() - 0.5) * std::f64::consts::PI
}
QAOAInitializationStrategy::AdiabaticStart => {
std::f64::consts::PI * (config.num_layers - i) as f64 / config.num_layers as f64
}
_ => {
0.5 * std::f64::consts::PI * (config.num_layers - i) as f64
/ config.num_layers as f64
}
};
betas.push(beta);
}
Ok(betas)
}
#[cfg(feature = "optimize")]
pub(super) fn optirs_parameter_optimization(&mut self) -> Result<()> {
if self.optirs_optimizer.is_none() {
let config = OptiRSConfig {
optimizer_type: crate::optirs_integration::OptiRSOptimizerType::Adam,
learning_rate: self.config.learning_rate,
convergence_tolerance: self.config.convergence_tolerance,
max_iterations: self.config.max_iterations,
..Default::default()
};
self.optirs_optimizer = Some(OptiRSQuantumOptimizer::new(config)?);
}
let mut all_params = Vec::new();
all_params.extend_from_slice(&self.gammas);
all_params.extend_from_slice(&self.betas);
let epsilon = 1e-4;
let mut all_gradients = vec![0.0; all_params.len()];
let num_gammas = self.gammas.len();
for i in 0..num_gammas {
let mut gammas_plus = self.gammas.clone();
let mut gammas_minus = self.gammas.clone();
gammas_plus[i] += epsilon;
gammas_minus[i] -= epsilon;
let cost_plus = self.evaluate_qaoa_cost(&gammas_plus, &self.betas)?;
let cost_minus = self.evaluate_qaoa_cost(&gammas_minus, &self.betas)?;
all_gradients[i] = (cost_plus - cost_minus) / (2.0 * epsilon);
}
for i in 0..self.betas.len() {
let mut betas_plus = self.betas.clone();
let mut betas_minus = self.betas.clone();
betas_plus[i] += epsilon;
betas_minus[i] -= epsilon;
let cost_plus = self.evaluate_qaoa_cost(&self.gammas, &betas_plus)?;
let cost_minus = self.evaluate_qaoa_cost(&self.gammas, &betas_minus)?;
all_gradients[num_gammas + i] = (cost_plus - cost_minus) / (2.0 * epsilon);
}
let current_cost = self.evaluate_qaoa_cost(&self.gammas, &self.betas)?;
let optimizer = self.optirs_optimizer.as_mut().ok_or_else(|| {
crate::error::SimulatorError::InvalidInput(
"OptiRS optimizer not initialized".to_string(),
)
})?;
let new_params = optimizer.optimize_step(&all_params, &all_gradients, -current_cost)?;
self.gammas = new_params[..num_gammas].to_vec();
self.betas = new_params[num_gammas..].to_vec();
Ok(())
}
pub(super) fn simulate_circuit(&self, circuit: &InterfaceCircuit) -> Result<Array1<Complex64>> {
let state_size = 1 << circuit.num_qubits;
let mut state = Array1::zeros(state_size);
state[0] = Complex64::new(1.0, 0.0);
Ok(state)
}
pub(super) fn extract_probabilities(
&self,
state: &Array1<Complex64>,
) -> Result<HashMap<String, f64>> {
let mut probabilities = HashMap::new();
for (idx, amplitude) in state.iter().enumerate() {
let probability = amplitude.norm_sqr();
if probability > 1e-10 {
let bitstring = format!("{:0width$b}", idx, width = self.graph.num_vertices);
probabilities.insert(bitstring, probability);
}
}
Ok(probabilities)
}
pub(super) fn extract_best_solution(
&self,
probabilities: &HashMap<String, f64>,
) -> Result<String> {
let mut best_solution = String::new();
let mut best_cost = f64::NEG_INFINITY;
for bitstring in probabilities.keys() {
let cost = self.evaluate_classical_cost(bitstring)?;
if cost > best_cost {
best_cost = cost;
best_solution = bitstring.clone();
}
}
Ok(best_solution)
}
pub(super) fn prepare_warm_start_state(&self, circuit: &mut InterfaceCircuit) -> Result<()> {
let classical_solution = self.get_classical_solution()?;
for (i, bit) in classical_solution.chars().enumerate() {
if bit == '1' && i < circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::PauliX, vec![i]));
}
}
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::RY(0.1), vec![qubit]));
}
Ok(())
}
pub(super) fn prepare_adiabatic_state(&self, circuit: &mut InterfaceCircuit) -> Result<()> {
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![qubit]));
}
self.apply_cost_layer(circuit, 0.01)?;
Ok(())
}
pub(super) fn prepare_random_state(&self, circuit: &mut InterfaceCircuit) -> Result<()> {
for qubit in 0..circuit.num_qubits {
let angle = (thread_rng().random::<f64>() - 0.5) * std::f64::consts::PI;
circuit.add_gate(InterfaceGate::new(
InterfaceGateType::RY(angle),
vec![qubit],
));
}
Ok(())
}
pub(super) fn prepare_problem_specific_state(
&self,
circuit: &mut InterfaceCircuit,
) -> Result<()> {
match self.problem_type {
QAOAProblemType::MaxCut => {
for qubit in 0..circuit.num_qubits {
if qubit % 2 == 0 {
circuit
.add_gate(InterfaceGate::new(InterfaceGateType::PauliX, vec![qubit]));
}
}
}
QAOAProblemType::TSP => {
let num_cities = (circuit.num_qubits as f64).sqrt() as usize;
for time in 0..num_cities {
let city = time;
let qubit = city * num_cities + time;
if qubit < circuit.num_qubits {
circuit
.add_gate(InterfaceGate::new(InterfaceGateType::PauliX, vec![qubit]));
}
}
}
_ => {
for qubit in 0..circuit.num_qubits {
circuit.add_gate(InterfaceGate::new(InterfaceGateType::Hadamard, vec![qubit]));
}
}
}
Ok(())
}
pub(super) fn get_classical_solution(&self) -> Result<String> {
let classical_cost = self.solve_classically()?;
let mut solution = String::new();
for _ in 0..self.graph.num_vertices {
solution.push(if thread_rng().random::<bool>() {
'1'
} else {
'0'
});
}
Ok(solution)
}
}