use crate::builder::Circuit;
use crate::dag::{circuit_to_dag, CircuitDag};
use crate::scirs2_matrices::SparseMatrix;
use quantrs2_core::{
error::{QuantRS2Error, QuantRS2Result},
gate::GateOp,
qubit::QubitId,
};
use scirs2_core::Complex64;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct SciRS2Graph {
pub nodes: Vec<usize>,
pub edges: Vec<(usize, usize, f64)>,
pub node_attributes: HashMap<usize, HashMap<String, String>>,
pub edge_attributes: HashMap<(usize, usize), HashMap<String, f64>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GraphSimilarityAlgorithm {
GraphEditDistance,
SpectralSimilarity,
GraphKernel { kernel_type: GraphKernelType },
NetworkAlignment,
SubgraphIsomorphism,
GraphNeuralNetwork { embedding_dim: usize },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GraphKernelType {
RandomWalk { steps: usize },
WeisfeilerLehman { iterations: usize },
ShortestPath,
Graphlet { size: usize },
}
#[derive(Debug, Clone)]
pub struct CircuitSimilarityMetrics {
pub structural_similarity: f64,
pub functional_similarity: f64,
pub sequence_similarity: f64,
pub topological_similarity: f64,
pub overall_similarity: f64,
pub detailed_metrics: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct CircuitDistanceMetrics {
pub edit_distance: usize,
pub normalized_edit_distance: f64,
pub wasserstein_distance: f64,
pub hausdorff_distance: f64,
pub earth_movers_distance: f64,
pub process_fidelity_distance: f64,
}
#[derive(Debug, Clone)]
pub struct SimilarityConfig {
pub algorithms: Vec<SimilarityAlgorithm>,
pub weights: SimilarityWeights,
pub tolerance: f64,
pub normalize: bool,
pub cache_results: bool,
pub parallel: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimilarityAlgorithm {
GateLevel,
DAGStructure,
UnitaryMatrix,
GraphBased { algorithm: GraphSimilarityAlgorithm },
Statistical,
MLEmbeddings { model_type: MLModelType },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MLModelType {
VAE { latent_dim: usize },
GCN { hidden_dims: Vec<usize> },
Transformer { num_heads: usize, num_layers: usize },
PreTrained { model_name: String },
}
#[derive(Debug, Clone)]
pub struct SimilarityWeights {
pub structural: f64,
pub functional: f64,
pub sequence: f64,
pub topological: f64,
}
impl Default for SimilarityWeights {
fn default() -> Self {
Self {
structural: 0.3,
functional: 0.4,
sequence: 0.2,
topological: 0.1,
}
}
}
impl Default for SimilarityConfig {
fn default() -> Self {
Self {
algorithms: vec![
SimilarityAlgorithm::GateLevel,
SimilarityAlgorithm::DAGStructure,
SimilarityAlgorithm::UnitaryMatrix,
],
weights: SimilarityWeights::default(),
tolerance: 1e-12,
normalize: true,
cache_results: true,
parallel: false,
}
}
}
pub struct CircuitSimilarityAnalyzer {
config: SimilarityConfig,
similarity_cache: HashMap<(String, String), CircuitSimilarityMetrics>,
embedding_cache: HashMap<String, Vec<f64>>,
feature_cache: HashMap<String, CircuitFeatures>,
}
#[derive(Debug, Clone)]
pub struct CircuitFeatures {
pub gate_histogram: HashMap<String, usize>,
pub depth: usize,
pub two_qubit_gates: usize,
pub connectivity_pattern: Vec<(usize, usize)>,
pub critical_path: Vec<String>,
pub parallelism_profile: Vec<usize>,
pub entanglement_structure: EntanglementStructure,
}
#[derive(Debug, Clone)]
pub struct EntanglementStructure {
pub entangling_layers: Vec<Vec<(usize, usize)>>,
pub max_entanglement_width: usize,
pub entanglement_graph: SciRS2Graph,
}
impl CircuitSimilarityAnalyzer {
#[must_use]
pub fn new(config: SimilarityConfig) -> Self {
Self {
config,
similarity_cache: HashMap::new(),
embedding_cache: HashMap::new(),
feature_cache: HashMap::new(),
}
}
#[must_use]
pub fn with_default_config() -> Self {
Self::new(SimilarityConfig::default())
}
pub fn compute_similarity<const N: usize, const M: usize>(
&mut self,
circuit1: &Circuit<N>,
circuit2: &Circuit<M>,
) -> QuantRS2Result<CircuitSimilarityMetrics> {
let id1 = Self::generate_circuit_id(circuit1);
let id2 = Self::generate_circuit_id(circuit2);
let cache_key = if id1 < id2 { (id1, id2) } else { (id2, id1) };
if self.config.cache_results {
if let Some(cached) = self.similarity_cache.get(&cache_key) {
return Ok(cached.clone());
}
}
let features1 = self.extract_circuit_features(circuit1)?;
let features2 = self.extract_circuit_features(circuit2)?;
let mut detailed_metrics = HashMap::new();
let mut similarities = Vec::new();
let algorithms = self.config.algorithms.clone();
for algorithm in &algorithms {
let similarity = match algorithm {
SimilarityAlgorithm::GateLevel => {
Self::compute_gate_level_similarity(&features1, &features2)?
}
SimilarityAlgorithm::DAGStructure => {
Self::compute_dag_similarity(circuit1, circuit2)?
}
SimilarityAlgorithm::UnitaryMatrix => {
Self::compute_unitary_similarity(circuit1, circuit2)?
}
SimilarityAlgorithm::GraphBased {
algorithm: graph_alg,
} => Self::compute_graph_similarity(&features1, &features2, graph_alg)?,
SimilarityAlgorithm::Statistical => {
Self::compute_statistical_similarity(&features1, &features2)?
}
SimilarityAlgorithm::MLEmbeddings { model_type } => {
self.compute_ml_similarity(circuit1, circuit2, model_type)?
}
};
detailed_metrics.insert(format!("{algorithm:?}"), similarity);
similarities.push(similarity);
}
let structural_similarity = Self::compute_structural_similarity(&features1, &features2)?;
let functional_similarity = Self::compute_functional_similarity(circuit1, circuit2)?;
let sequence_similarity = Self::compute_sequence_similarity(&features1, &features2)?;
let topological_similarity = Self::compute_topological_similarity(&features1, &features2)?;
let overall_similarity = self.config.weights.topological.mul_add(
topological_similarity,
self.config.weights.sequence.mul_add(
sequence_similarity,
self.config.weights.structural.mul_add(
structural_similarity,
self.config.weights.functional * functional_similarity,
),
),
);
let result = CircuitSimilarityMetrics {
structural_similarity,
functional_similarity,
sequence_similarity,
topological_similarity,
overall_similarity,
detailed_metrics,
};
if self.config.cache_results {
self.similarity_cache.insert(cache_key, result.clone());
}
Ok(result)
}
pub fn compute_distance<const N: usize, const M: usize>(
&mut self,
circuit1: &Circuit<N>,
circuit2: &Circuit<M>,
) -> QuantRS2Result<CircuitDistanceMetrics> {
let features1 = self.extract_circuit_features(circuit1)?;
let features2 = self.extract_circuit_features(circuit2)?;
let edit_distance = Self::compute_edit_distance(&features1, &features2)?;
let max_gates = features1
.gate_histogram
.values()
.sum::<usize>()
.max(features2.gate_histogram.values().sum::<usize>());
let normalized_edit_distance = if max_gates > 0 {
edit_distance as f64 / max_gates as f64
} else {
0.0
};
let wasserstein_distance = Self::compute_wasserstein_distance(&features1, &features2)?;
let hausdorff_distance = Self::compute_hausdorff_distance(circuit1, circuit2)?;
let earth_movers_distance = Self::compute_earth_movers_distance(&features1, &features2)?;
let process_fidelity_distance =
Self::compute_process_fidelity_distance(circuit1, circuit2)?;
Ok(CircuitDistanceMetrics {
edit_distance,
normalized_edit_distance,
wasserstein_distance,
hausdorff_distance,
earth_movers_distance,
process_fidelity_distance,
})
}
fn extract_circuit_features<const N: usize>(
&mut self,
circuit: &Circuit<N>,
) -> QuantRS2Result<CircuitFeatures> {
let id = Self::generate_circuit_id(circuit);
if let Some(cached) = self.feature_cache.get(&id) {
return Ok(cached.clone());
}
let mut gate_histogram = HashMap::new();
let mut connectivity_pattern = Vec::new();
let mut critical_path = Vec::new();
let mut two_qubit_gates = 0;
for gate in circuit.gates() {
let gate_name = gate.name();
*gate_histogram.entry(gate_name.to_string()).or_insert(0) += 1;
critical_path.push(gate_name.to_string());
if gate.qubits().len() == 2 {
two_qubit_gates += 1;
let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
connectivity_pattern.push((qubits[0], qubits[1]));
}
}
let parallelism_profile = Self::compute_parallelism_profile(circuit)?;
let entanglement_structure = Self::analyze_entanglement_structure(circuit)?;
let features = CircuitFeatures {
gate_histogram,
depth: circuit.gates().len(), two_qubit_gates,
connectivity_pattern,
critical_path,
parallelism_profile,
entanglement_structure,
};
self.feature_cache.insert(id, features.clone());
Ok(features)
}
fn compute_gate_level_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
let mut dot_product = 0.0;
let mut norm1 = 0.0;
let mut norm2 = 0.0;
let all_gates: HashSet<String> = features1
.gate_histogram
.keys()
.chain(features2.gate_histogram.keys())
.cloned()
.collect();
for gate in all_gates {
let count1 = *features1.gate_histogram.get(&gate).unwrap_or(&0) as f64;
let count2 = *features2.gate_histogram.get(&gate).unwrap_or(&0) as f64;
dot_product += count1 * count2;
norm1 += count1 * count1;
norm2 += count2 * count2;
}
let similarity = if norm1 > 0.0 && norm2 > 0.0 {
dot_product / (norm1.sqrt() * norm2.sqrt())
} else {
0.0
};
Ok(similarity)
}
fn compute_dag_similarity<const N: usize, const M: usize>(
circuit1: &Circuit<N>,
circuit2: &Circuit<M>,
) -> QuantRS2Result<f64> {
let dag1 = circuit_to_dag(circuit1);
let dag2 = circuit_to_dag(circuit2);
let nodes_similarity = if dag1.nodes().len() == dag2.nodes().len() {
1.0
} else {
let min_nodes = dag1.nodes().len().min(dag2.nodes().len()) as f64;
let max_nodes = dag1.nodes().len().max(dag2.nodes().len()) as f64;
min_nodes / max_nodes
};
let edges_similarity = if dag1.edges().len() == dag2.edges().len() {
1.0
} else {
let min_edges = dag1.edges().len().min(dag2.edges().len()) as f64;
let max_edges = dag1.edges().len().max(dag2.edges().len()) as f64;
min_edges / max_edges
};
Ok(f64::midpoint(nodes_similarity, edges_similarity))
}
const fn compute_unitary_similarity<const N: usize, const M: usize>(
_circuit1: &Circuit<N>,
_circuit2: &Circuit<M>,
) -> QuantRS2Result<f64> {
if N != M {
return Ok(0.0);
}
let fidelity = 0.9;
Ok(fidelity)
}
fn compute_graph_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
algorithm: &GraphSimilarityAlgorithm,
) -> QuantRS2Result<f64> {
match algorithm {
GraphSimilarityAlgorithm::GraphEditDistance => Self::compute_graph_edit_distance(
&features1.entanglement_structure.entanglement_graph,
&features2.entanglement_structure.entanglement_graph,
),
GraphSimilarityAlgorithm::SpectralSimilarity => Self::compute_spectral_similarity(
&features1.entanglement_structure.entanglement_graph,
&features2.entanglement_structure.entanglement_graph,
),
_ => {
Ok(0.5) }
}
}
fn compute_statistical_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
let max_depth = features1.depth.max(features2.depth);
let depth_similarity = if max_depth > 0 {
1.0 - (features1.depth as f64 - features2.depth as f64).abs() / (max_depth as f64)
} else {
1.0 };
let max_two_qubit = features1.two_qubit_gates.max(features2.two_qubit_gates);
let two_qubit_similarity = if max_two_qubit > 0 {
1.0 - (features1.two_qubit_gates as f64 - features2.two_qubit_gates as f64).abs()
/ (max_two_qubit as f64)
} else {
1.0 };
Ok(f64::midpoint(depth_similarity, two_qubit_similarity))
}
fn compute_ml_similarity<const N: usize, const M: usize>(
&mut self,
circuit1: &Circuit<N>,
circuit2: &Circuit<M>,
model_type: &MLModelType,
) -> QuantRS2Result<f64> {
let embedding1 = self.generate_circuit_embedding(circuit1, model_type)?;
let embedding2 = self.generate_circuit_embedding(circuit2, model_type)?;
let similarity = Self::cosine_similarity(&embedding1, &embedding2);
Ok(similarity)
}
fn generate_circuit_embedding<const N: usize>(
&mut self,
circuit: &Circuit<N>,
model_type: &MLModelType,
) -> QuantRS2Result<Vec<f64>> {
let id = format!("{}_{:?}", Self::generate_circuit_id(circuit), model_type);
if let Some(cached) = self.embedding_cache.get(&id) {
return Ok(cached.clone());
}
let embedding = match model_type {
MLModelType::VAE { latent_dim } => Self::generate_vae_embedding(circuit, *latent_dim)?,
MLModelType::GCN { hidden_dims } => Self::generate_gcn_embedding(circuit, hidden_dims)?,
MLModelType::Transformer {
num_heads,
num_layers,
} => Self::generate_transformer_embedding(circuit, *num_heads, *num_layers)?,
MLModelType::PreTrained { model_name } => {
Self::generate_pretrained_embedding(circuit, model_name)?
}
};
self.embedding_cache.insert(id, embedding.clone());
Ok(embedding)
}
fn generate_vae_embedding<const N: usize>(
_circuit: &Circuit<N>,
latent_dim: usize,
) -> QuantRS2Result<Vec<f64>> {
Ok(vec![0.5; latent_dim])
}
fn generate_gcn_embedding<const N: usize>(
_circuit: &Circuit<N>,
hidden_dims: &[usize],
) -> QuantRS2Result<Vec<f64>> {
let output_dim = hidden_dims.last().unwrap_or(&64);
Ok(vec![0.5; *output_dim])
}
fn generate_transformer_embedding<const N: usize>(
_circuit: &Circuit<N>,
num_heads: usize,
_num_layers: usize,
) -> QuantRS2Result<Vec<f64>> {
let embedding_dim = num_heads * 64; Ok(vec![0.5; embedding_dim])
}
fn generate_pretrained_embedding<const N: usize>(
_circuit: &Circuit<N>,
model_name: &str,
) -> QuantRS2Result<Vec<f64>> {
let embedding_dim = match model_name {
"circuit_bert" => 768,
"quantum_gpt" => 512,
_ => 256,
};
Ok(vec![0.5; embedding_dim])
}
fn compute_structural_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
let connectivity_similarity = Self::compare_connectivity_patterns(
&features1.connectivity_pattern,
&features2.connectivity_pattern,
);
let max_depth = features1.depth.max(features2.depth);
let depth_similarity = if max_depth > 0 {
1.0 - (features1.depth as f64 - features2.depth as f64).abs() / (max_depth as f64)
} else {
1.0
};
Ok(f64::midpoint(connectivity_similarity, depth_similarity))
}
const fn compute_functional_similarity<const N: usize, const M: usize>(
_circuit1: &Circuit<N>,
_circuit2: &Circuit<M>,
) -> QuantRS2Result<f64> {
if N != M {
return Ok(0.0);
}
Ok(0.8) }
fn compute_sequence_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
let edit_distance =
Self::string_edit_distance(&features1.critical_path, &features2.critical_path);
let max_length = features1
.critical_path
.len()
.max(features2.critical_path.len());
let similarity = if max_length > 0 {
1.0 - (edit_distance as f64 / max_length as f64)
} else {
1.0
};
Ok(similarity)
}
fn compute_topological_similarity(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
let max_width = features1
.entanglement_structure
.max_entanglement_width
.max(features2.entanglement_structure.max_entanglement_width);
let width_similarity = if max_width > 0 {
1.0 - (features1.entanglement_structure.max_entanglement_width as f64
- features2.entanglement_structure.max_entanglement_width as f64)
.abs()
/ (max_width as f64)
} else {
1.0 };
Ok(width_similarity)
}
fn generate_circuit_id<const N: usize>(circuit: &Circuit<N>) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
N.hash(&mut hasher);
for gate in circuit.gates() {
gate.name().hash(&mut hasher);
for qubit in gate.qubits() {
qubit.id().hash(&mut hasher);
}
}
format!("{:x}", hasher.finish())
}
fn compute_parallelism_profile<const N: usize>(
circuit: &Circuit<N>,
) -> QuantRS2Result<Vec<usize>> {
let mut profile = Vec::new();
let gate_count = circuit.gates().len();
for _ in 0..gate_count {
profile.push(1);
}
Ok(profile)
}
fn analyze_entanglement_structure<const N: usize>(
circuit: &Circuit<N>,
) -> QuantRS2Result<EntanglementStructure> {
let mut entangling_layers = Vec::new();
let mut current_layer = Vec::new();
let mut max_width = 0;
for gate in circuit.gates() {
if gate.qubits().len() == 2 {
let qubits: Vec<usize> = gate.qubits().iter().map(|q| q.id() as usize).collect();
current_layer.push((qubits[0], qubits[1]));
max_width = max_width.max(current_layer.len());
} else if !current_layer.is_empty() {
entangling_layers.push(current_layer);
current_layer = Vec::new();
}
}
if !current_layer.is_empty() {
entangling_layers.push(current_layer);
}
let mut graph = SciRS2Graph {
nodes: (0..N).collect(),
edges: Vec::new(),
node_attributes: HashMap::new(),
edge_attributes: HashMap::new(),
};
for layer in &entangling_layers {
for &(q1, q2) in layer {
graph.edges.push((q1, q2, 1.0));
}
}
Ok(EntanglementStructure {
entangling_layers,
max_entanglement_width: max_width,
entanglement_graph: graph,
})
}
fn compare_connectivity_patterns(
pattern1: &[(usize, usize)],
pattern2: &[(usize, usize)],
) -> f64 {
let set1: HashSet<_> = pattern1.iter().collect();
let set2: HashSet<_> = pattern2.iter().collect();
let intersection = set1.intersection(&set2).count();
let union = set1.union(&set2).count();
if union > 0 {
intersection as f64 / union as f64
} else {
1.0
}
}
fn string_edit_distance(seq1: &[String], seq2: &[String]) -> usize {
let m = seq1.len();
let n = seq2.len();
let mut dp = vec![vec![0; n + 1]; m + 1];
for i in 0..=m {
dp[i][0] = i;
}
for j in 0..=n {
dp[0][j] = j;
}
for i in 1..=m {
for j in 1..=n {
if seq1[i - 1] == seq2[j - 1] {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + dp[i - 1][j].min(dp[i][j - 1]).min(dp[i - 1][j - 1]);
}
}
}
dp[m][n]
}
fn cosine_similarity(vec1: &[f64], vec2: &[f64]) -> f64 {
if vec1.len() != vec2.len() {
return 0.0;
}
let dot_product: f64 = vec1.iter().zip(vec2.iter()).map(|(a, b)| a * b).sum();
let norm1: f64 = vec1.iter().map(|x| x * x).sum::<f64>().sqrt();
let norm2: f64 = vec2.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm1 > 0.0 && norm2 > 0.0 {
dot_product / (norm1 * norm2)
} else {
0.0
}
}
fn compute_edit_distance(
features1: &CircuitFeatures,
features2: &CircuitFeatures,
) -> QuantRS2Result<usize> {
let distance =
Self::string_edit_distance(&features1.critical_path, &features2.critical_path);
Ok(distance)
}
const fn compute_wasserstein_distance(
_features1: &CircuitFeatures,
_features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
Ok(0.3) }
const fn compute_hausdorff_distance<const N: usize, const M: usize>(
_circuit1: &Circuit<N>,
_circuit2: &Circuit<M>,
) -> QuantRS2Result<f64> {
Ok(0.25) }
const fn compute_earth_movers_distance(
_features1: &CircuitFeatures,
_features2: &CircuitFeatures,
) -> QuantRS2Result<f64> {
Ok(0.2) }
const fn compute_process_fidelity_distance<const N: usize, const M: usize>(
_circuit1: &Circuit<N>,
_circuit2: &Circuit<M>,
) -> QuantRS2Result<f64> {
if N != M {
return Ok(1.0); }
Ok(0.1) }
fn compute_graph_edit_distance(
graph1: &SciRS2Graph,
graph2: &SciRS2Graph,
) -> QuantRS2Result<f64> {
let node_diff = (graph1.nodes.len() as f64 - graph2.nodes.len() as f64).abs();
let edge_diff = (graph1.edges.len() as f64 - graph2.edges.len() as f64).abs();
let max_size = (graph1.nodes.len() + graph1.edges.len())
.max(graph2.nodes.len() + graph2.edges.len()) as f64;
let distance = if max_size > 0.0 {
(node_diff + edge_diff) / max_size
} else {
0.0 };
Ok(1.0 - distance) }
const fn compute_spectral_similarity(
_graph1: &SciRS2Graph,
_graph2: &SciRS2Graph,
) -> QuantRS2Result<f64> {
Ok(0.7) }
}
pub struct BatchSimilarityComputer {
analyzer: CircuitSimilarityAnalyzer,
}
impl BatchSimilarityComputer {
#[must_use]
pub fn new(config: SimilarityConfig) -> Self {
Self {
analyzer: CircuitSimilarityAnalyzer::new(config),
}
}
pub fn compute_pairwise_similarities<const N: usize>(
&mut self,
circuits: &[Circuit<N>],
) -> QuantRS2Result<Vec<Vec<f64>>> {
let n_circuits = circuits.len();
let mut similarity_matrix = vec![vec![0.0; n_circuits]; n_circuits];
for i in 0..n_circuits {
similarity_matrix[i][i] = 1.0;
for j in (i + 1)..n_circuits {
let similarity = self
.analyzer
.compute_similarity(&circuits[i], &circuits[j])?;
similarity_matrix[i][j] = similarity.overall_similarity;
similarity_matrix[j][i] = similarity.overall_similarity; }
}
Ok(similarity_matrix)
}
pub fn find_most_similar<const N: usize>(
&mut self,
query_circuit: &Circuit<N>,
dataset: &[Circuit<N>],
top_k: usize,
) -> QuantRS2Result<Vec<(usize, f64)>> {
let mut similarities = Vec::new();
for (i, circuit) in dataset.iter().enumerate() {
let similarity = self.analyzer.compute_similarity(query_circuit, circuit)?;
similarities.push((i, similarity.overall_similarity));
}
similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
similarities.truncate(top_k);
Ok(similarities)
}
}
#[cfg(test)]
mod tests {
use super::*;
use quantrs2_core::gate::multi::CNOT;
use quantrs2_core::gate::single::Hadamard;
#[test]
fn test_similarity_analyzer_creation() {
let analyzer = CircuitSimilarityAnalyzer::with_default_config();
assert_eq!(analyzer.config.algorithms.len(), 3);
}
#[test]
fn test_identical_circuits_similarity() {
let mut analyzer = CircuitSimilarityAnalyzer::with_default_config();
let mut circuit = Circuit::<2>::new();
circuit
.add_gate(Hadamard { target: QubitId(0) })
.expect("Failed to add Hadamard gate");
let similarity = analyzer
.compute_similarity(&circuit, &circuit)
.expect("Failed to compute similarity for identical circuits");
assert!(
!similarity.overall_similarity.is_nan(),
"Similarity should not be NaN for identical circuits. Actual value: {}",
similarity.overall_similarity
);
assert!(
!similarity.overall_similarity.is_infinite(),
"Similarity should not be infinite for identical circuits. Actual value: {}",
similarity.overall_similarity
);
assert!(
similarity.structural_similarity >= 0.9,
"Structural similarity should be high for identical circuits: {}",
similarity.structural_similarity
);
assert!(
similarity.sequence_similarity >= 0.9,
"Sequence similarity should be high for identical circuits: {}",
similarity.sequence_similarity
);
assert!(
similarity.topological_similarity >= 0.9,
"Topological similarity should be high for identical circuits: {}",
similarity.topological_similarity
);
assert!(
similarity.overall_similarity >= 0.8,
"Overall similarity should be high for identical circuits: {}",
similarity.overall_similarity
);
}
#[test]
fn test_different_circuits_similarity() {
let mut analyzer = CircuitSimilarityAnalyzer::with_default_config();
let mut circuit1 = Circuit::<2>::new();
circuit1
.add_gate(Hadamard { target: QubitId(0) })
.expect("Failed to add Hadamard gate to circuit1");
let mut circuit2 = Circuit::<2>::new();
circuit2
.add_gate(CNOT {
control: QubitId(0),
target: QubitId(1),
})
.expect("Failed to add CNOT gate to circuit2");
let similarity = analyzer
.compute_similarity(&circuit1, &circuit2)
.expect("Failed to compute similarity for different circuits");
assert!(similarity.overall_similarity < 1.0);
}
#[test]
fn test_distance_computation() {
let mut analyzer = CircuitSimilarityAnalyzer::with_default_config();
let mut circuit1 = Circuit::<2>::new();
circuit1
.add_gate(Hadamard { target: QubitId(0) })
.expect("Failed to add Hadamard gate to circuit1");
let mut circuit2 = Circuit::<2>::new();
circuit2
.add_gate(CNOT {
control: QubitId(0),
target: QubitId(1),
})
.expect("Failed to add CNOT gate to circuit2");
let distance = analyzer
.compute_distance(&circuit1, &circuit2)
.expect("Failed to compute distance between circuits");
assert!(distance.edit_distance > 0);
assert!(
distance.normalized_edit_distance >= 0.0 && distance.normalized_edit_distance <= 1.0
);
}
#[test]
fn test_feature_extraction() {
let mut analyzer = CircuitSimilarityAnalyzer::with_default_config();
let mut circuit = Circuit::<2>::new();
circuit
.add_gate(Hadamard { target: QubitId(0) })
.expect("Failed to add Hadamard gate");
circuit
.add_gate(CNOT {
control: QubitId(0),
target: QubitId(1),
})
.expect("Failed to add CNOT gate");
let features = analyzer
.extract_circuit_features(&circuit)
.expect("Failed to extract circuit features");
assert_eq!(features.gate_histogram.get("H"), Some(&1));
assert_eq!(features.gate_histogram.get("CNOT"), Some(&1));
assert_eq!(features.two_qubit_gates, 1);
}
#[test]
fn test_batch_similarity_computation() {
let mut computer = BatchSimilarityComputer::new(SimilarityConfig::default());
let mut circuit1 = Circuit::<2>::new();
circuit1
.add_gate(Hadamard { target: QubitId(0) })
.expect("Failed to add Hadamard gate to circuit1");
let mut circuit2 = Circuit::<2>::new();
circuit2
.add_gate(CNOT {
control: QubitId(0),
target: QubitId(1),
})
.expect("Failed to add CNOT gate to circuit2");
let circuits = vec![circuit1, circuit2];
let similarity_matrix = computer
.compute_pairwise_similarities(&circuits)
.expect("Failed to compute pairwise similarities");
assert_eq!(similarity_matrix.len(), 2);
assert_eq!(similarity_matrix[0].len(), 2);
assert_eq!(similarity_matrix[0][0], 1.0); assert_eq!(similarity_matrix[1][1], 1.0); assert_eq!(similarity_matrix[0][1], similarity_matrix[1][0]); }
}