quantrs2_core/
mbqc.rs

1//! Measurement-based quantum computing (MBQC)
2//!
3//! This module provides implementations for one-way quantum computing using
4//! cluster states, graph states, and measurement patterns.
5
6use crate::error::{QuantRS2Error, QuantRS2Result};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::Complex64;
9use std::collections::{HashMap, HashSet};
10use std::f64::consts::PI;
11
12/// Measurement basis for MBQC
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub enum MeasurementBasis {
15    /// Computational basis (Z)
16    Computational,
17    /// X basis
18    X,
19    /// Y basis
20    Y,
21    /// XY-plane measurement at angle θ
22    XY(f64),
23    /// XZ-plane measurement at angle θ
24    XZ(f64),
25    /// YZ-plane measurement at angle θ
26    YZ(f64),
27}
28
29impl MeasurementBasis {
30    /// Get the measurement operator for this basis
31    pub fn operator(&self) -> Array2<Complex64> {
32        match self {
33            MeasurementBasis::Computational => {
34                // |0⟩⟨0|
35                Array2::from_shape_vec(
36                    (2, 2),
37                    vec![
38                        Complex64::new(1.0, 0.0),
39                        Complex64::new(0.0, 0.0),
40                        Complex64::new(0.0, 0.0),
41                        Complex64::new(0.0, 0.0),
42                    ],
43                )
44                .expect(
45                    "Failed to create computational basis operator in MeasurementBasis::operator",
46                )
47            }
48            MeasurementBasis::X => {
49                // |+⟩⟨+|
50                Array2::from_shape_vec(
51                    (2, 2),
52                    vec![
53                        Complex64::new(0.5, 0.0),
54                        Complex64::new(0.5, 0.0),
55                        Complex64::new(0.5, 0.0),
56                        Complex64::new(0.5, 0.0),
57                    ],
58                )
59                .expect("Failed to create X basis operator in MeasurementBasis::operator")
60            }
61            MeasurementBasis::Y => {
62                // |i⟩⟨i| where |i⟩ = (|0⟩ + i|1⟩)/√2
63                Array2::from_shape_vec(
64                    (2, 2),
65                    vec![
66                        Complex64::new(0.5, 0.0),
67                        Complex64::new(0.0, -0.5),
68                        Complex64::new(0.0, 0.5),
69                        Complex64::new(0.5, 0.0),
70                    ],
71                )
72                .expect("Failed to create Y basis operator in MeasurementBasis::operator")
73            }
74            MeasurementBasis::XY(theta) => {
75                // |θ⟩⟨θ| where |θ⟩ = cos(θ/2)|0⟩ + sin(θ/2)|1⟩
76                let c = (theta / 2.0).cos();
77                let s = (theta / 2.0).sin();
78                Array2::from_shape_vec(
79                    (2, 2),
80                    vec![
81                        Complex64::new(c * c, 0.0),
82                        Complex64::new(c * s, 0.0),
83                        Complex64::new(c * s, 0.0),
84                        Complex64::new(s * s, 0.0),
85                    ],
86                )
87                .expect("Failed to create XY basis operator in MeasurementBasis::operator")
88            }
89            MeasurementBasis::XZ(theta) => {
90                // Rotation in XZ plane
91                let c = (theta / 2.0).cos();
92                let s = (theta / 2.0).sin();
93                Array2::from_shape_vec(
94                    (2, 2),
95                    vec![
96                        Complex64::new(c * c, 0.0),
97                        Complex64::new(c, 0.0) * Complex64::new(0.0, -s),
98                        Complex64::new(c, 0.0) * Complex64::new(0.0, s),
99                        Complex64::new(s * s, 0.0),
100                    ],
101                )
102                .expect("Failed to create XZ basis operator in MeasurementBasis::operator")
103            }
104            MeasurementBasis::YZ(theta) => {
105                // Rotation in YZ plane
106                let c = (theta / 2.0).cos();
107                let s = (theta / 2.0).sin();
108                Array2::from_shape_vec(
109                    (2, 2),
110                    vec![
111                        Complex64::new(c * c, 0.0),
112                        Complex64::new(s, 0.0) * Complex64::new(1.0, 0.0),
113                        Complex64::new(s, 0.0) * Complex64::new(1.0, 0.0),
114                        Complex64::new(s * s, 0.0),
115                    ],
116                )
117                .expect("Failed to create YZ basis operator in MeasurementBasis::operator")
118            }
119        }
120    }
121}
122
123/// Graph structure for graph states
124#[derive(Debug, Clone)]
125pub struct Graph {
126    /// Number of vertices
127    pub num_vertices: usize,
128    /// Adjacency list representation
129    pub edges: HashMap<usize, HashSet<usize>>,
130}
131
132impl Graph {
133    /// Create a new empty graph
134    pub fn new(num_vertices: usize) -> Self {
135        let mut edges = HashMap::new();
136        for i in 0..num_vertices {
137            edges.insert(i, HashSet::new());
138        }
139
140        Self {
141            num_vertices,
142            edges,
143        }
144    }
145
146    /// Add an edge between two vertices
147    pub fn add_edge(&mut self, u: usize, v: usize) -> QuantRS2Result<()> {
148        if u >= self.num_vertices || v >= self.num_vertices {
149            return Err(QuantRS2Error::InvalidInput(
150                "Vertex index out of bounds".to_string(),
151            ));
152        }
153
154        if u != v {
155            self.edges
156                .get_mut(&u)
157                .expect("Vertex u should exist in edges map in Graph::add_edge")
158                .insert(v);
159            self.edges
160                .get_mut(&v)
161                .expect("Vertex v should exist in edges map in Graph::add_edge")
162                .insert(u);
163        }
164
165        Ok(())
166    }
167
168    /// Get neighbors of a vertex
169    pub fn neighbors(&self, v: usize) -> Option<&HashSet<usize>> {
170        self.edges.get(&v)
171    }
172
173    /// Create a linear cluster (1D chain)
174    pub fn linear_cluster(n: usize) -> Self {
175        let mut graph = Self::new(n);
176        for i in 0..n - 1 {
177            graph
178                .add_edge(i, i + 1)
179                .expect("Failed to add edge in Graph::linear_cluster (indices should be valid)");
180        }
181        graph
182    }
183
184    /// Create a 2D rectangular cluster
185    pub fn rectangular_cluster(rows: usize, cols: usize) -> Self {
186        let n = rows * cols;
187        let mut graph = Self::new(n);
188
189        for r in 0..rows {
190            for c in 0..cols {
191                let idx = r * cols + c;
192
193                // Horizontal edges
194                if c < cols - 1 {
195                    graph
196                        .add_edge(idx, idx + 1)
197                        .expect("Failed to add horizontal edge in Graph::rectangular_cluster");
198                }
199
200                // Vertical edges
201                if r < rows - 1 {
202                    graph
203                        .add_edge(idx, idx + cols)
204                        .expect("Failed to add vertical edge in Graph::rectangular_cluster");
205                }
206            }
207        }
208
209        graph
210    }
211
212    /// Create a complete graph
213    pub fn complete(n: usize) -> Self {
214        let mut graph = Self::new(n);
215        for i in 0..n {
216            for j in i + 1..n {
217                graph
218                    .add_edge(i, j)
219                    .expect("Failed to add edge in Graph::complete");
220            }
221        }
222        graph
223    }
224
225    /// Create a star graph (one central node connected to all others)
226    pub fn star(n: usize) -> Self {
227        let mut graph = Self::new(n);
228        for i in 1..n {
229            graph
230                .add_edge(0, i)
231                .expect("Failed to add edge in Graph::star");
232        }
233        graph
234    }
235}
236
237/// Measurement pattern for MBQC
238#[derive(Debug, Clone)]
239pub struct MeasurementPattern {
240    /// Measurement basis for each qubit
241    pub measurements: HashMap<usize, MeasurementBasis>,
242    /// Measurement order (important for adaptivity)
243    pub order: Vec<usize>,
244    /// Corrections to apply based on measurement outcomes
245    pub x_corrections: HashMap<usize, Vec<(usize, bool)>>, // (source, sign)
246    pub z_corrections: HashMap<usize, Vec<(usize, bool)>>,
247    /// Input qubits (not measured)
248    pub inputs: HashSet<usize>,
249    /// Output qubits (measured last or not measured)
250    pub outputs: HashSet<usize>,
251}
252
253impl MeasurementPattern {
254    /// Create a new measurement pattern
255    pub fn new() -> Self {
256        Self {
257            measurements: HashMap::new(),
258            order: Vec::new(),
259            x_corrections: HashMap::new(),
260            z_corrections: HashMap::new(),
261            inputs: HashSet::new(),
262            outputs: HashSet::new(),
263        }
264    }
265
266    /// Add a measurement
267    pub fn add_measurement(&mut self, qubit: usize, basis: MeasurementBasis) {
268        self.measurements.insert(qubit, basis);
269        if !self.order.contains(&qubit) {
270            self.order.push(qubit);
271        }
272    }
273
274    /// Add X correction dependency
275    pub fn add_x_correction(&mut self, target: usize, source: usize, sign: bool) {
276        self.x_corrections
277            .entry(target)
278            .or_insert_with(Vec::new)
279            .push((source, sign));
280    }
281
282    /// Add Z correction dependency
283    pub fn add_z_correction(&mut self, target: usize, source: usize, sign: bool) {
284        self.z_corrections
285            .entry(target)
286            .or_insert_with(Vec::new)
287            .push((source, sign));
288    }
289
290    /// Set input qubits
291    pub fn set_inputs(&mut self, inputs: Vec<usize>) {
292        self.inputs = inputs.into_iter().collect();
293    }
294
295    /// Set output qubits
296    pub fn set_outputs(&mut self, outputs: Vec<usize>) {
297        self.outputs = outputs.into_iter().collect();
298    }
299
300    /// Create pattern for single-qubit rotation
301    pub fn single_qubit_rotation(angle: f64) -> Self {
302        let mut pattern = Self::new();
303
304        // Three qubits: input (0), auxiliary (1), output (2)
305        pattern.set_inputs(vec![0]);
306        pattern.set_outputs(vec![2]);
307
308        // Measure auxiliary qubit at angle
309        pattern.add_measurement(1, MeasurementBasis::XY(angle));
310
311        // Measure input qubit in X basis
312        pattern.add_measurement(0, MeasurementBasis::X);
313
314        // Corrections
315        pattern.add_x_correction(2, 0, true);
316        pattern.add_z_correction(2, 1, true);
317
318        pattern
319    }
320
321    /// Create pattern for CNOT gate
322    pub fn cnot() -> Self {
323        let mut pattern = Self::new();
324
325        // 15 qubits in standard CNOT pattern
326        // Inputs: control (0), target (1)
327        // Outputs: control (13), target (14)
328        pattern.set_inputs(vec![0, 1]);
329        pattern.set_outputs(vec![13, 14]);
330
331        // Measurement order and bases (simplified)
332        for i in 2..13 {
333            pattern.add_measurement(i, MeasurementBasis::XY(PI / 2.0));
334        }
335
336        // Add corrections (simplified - full pattern is complex)
337        pattern.add_x_correction(13, 0, true);
338        pattern.add_x_correction(14, 1, true);
339
340        pattern
341    }
342}
343
344impl Default for MeasurementPattern {
345    fn default() -> Self {
346        Self::new()
347    }
348}
349
350/// Cluster state for MBQC
351pub struct ClusterState {
352    /// Underlying graph structure
353    pub graph: Graph,
354    /// State vector (2^n complex amplitudes)
355    pub state: Array1<Complex64>,
356    /// Measured qubits and their outcomes
357    pub measurements: HashMap<usize, bool>,
358}
359
360impl ClusterState {
361    /// Create a cluster state from a graph
362    pub fn from_graph(graph: Graph) -> QuantRS2Result<Self> {
363        let n = graph.num_vertices;
364        let dim = 1 << n;
365
366        // Initialize all qubits in |+⟩ state
367        let mut state = Array1::zeros(dim);
368        state[0] = Complex64::new(1.0, 0.0);
369
370        // Apply Hadamard to all qubits
371        for i in 0..n {
372            state = Self::apply_hadamard(&state, i, n)?;
373        }
374
375        // Apply CZ gates for each edge
376        for (u, neighbors) in &graph.edges {
377            for &v in neighbors {
378                if u < &v {
379                    state = Self::apply_cz(&state, *u, v, n)?;
380                }
381            }
382        }
383
384        // Normalize
385        let norm = state.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
386        state = state / Complex64::new(norm, 0.0);
387
388        Ok(Self {
389            graph,
390            state,
391            measurements: HashMap::new(),
392        })
393    }
394
395    /// Apply Hadamard gate to a qubit in the state vector
396    fn apply_hadamard(
397        state: &Array1<Complex64>,
398        qubit: usize,
399        n: usize,
400    ) -> QuantRS2Result<Array1<Complex64>> {
401        let dim = 1 << n;
402        let mut new_state = Array1::zeros(dim);
403        let h_factor = Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
404
405        for i in 0..dim {
406            let bit = (i >> qubit) & 1;
407            if bit == 0 {
408                // |0⟩ -> |+⟩
409                new_state[i] += h_factor * state[i];
410                new_state[i | (1 << qubit)] += h_factor * state[i];
411            } else {
412                // |1⟩ -> |−⟩
413                new_state[i & !(1 << qubit)] += h_factor * state[i];
414                new_state[i] -= h_factor * state[i];
415            }
416        }
417
418        Ok(new_state)
419    }
420
421    /// Apply CZ gate between two qubits
422    fn apply_cz(
423        state: &Array1<Complex64>,
424        q1: usize,
425        q2: usize,
426        n: usize,
427    ) -> QuantRS2Result<Array1<Complex64>> {
428        let dim = 1 << n;
429        let mut new_state = state.clone();
430
431        for i in 0..dim {
432            let bit1 = (i >> q1) & 1;
433            let bit2 = (i >> q2) & 1;
434            if bit1 == 1 && bit2 == 1 {
435                new_state[i] *= -1.0;
436            }
437        }
438
439        Ok(new_state)
440    }
441
442    /// Measure a qubit in a given basis
443    pub fn measure(&mut self, qubit: usize, basis: MeasurementBasis) -> QuantRS2Result<bool> {
444        if qubit >= self.graph.num_vertices {
445            return Err(QuantRS2Error::InvalidInput(
446                "Qubit index out of bounds".to_string(),
447            ));
448        }
449
450        if self.measurements.contains_key(&qubit) {
451            return Err(QuantRS2Error::InvalidInput(
452                "Qubit already measured".to_string(),
453            ));
454        }
455
456        // Apply basis rotation if needed
457        let state = match basis {
458            MeasurementBasis::Computational => self.state.clone(),
459            MeasurementBasis::X => {
460                Self::apply_hadamard(&self.state, qubit, self.graph.num_vertices)?
461            }
462            MeasurementBasis::Y => {
463                // Apply S† then H
464                let mut state = self.state.clone();
465                for i in 0..state.len() {
466                    if (i >> qubit) & 1 == 1 {
467                        state[i] *= Complex64::new(0.0, -1.0);
468                    }
469                }
470                Self::apply_hadamard(&state, qubit, self.graph.num_vertices)?
471            }
472            MeasurementBasis::XY(theta) => {
473                // Apply rotation R_z(-θ) then H
474                let mut state = self.state.clone();
475                for i in 0..state.len() {
476                    if (i >> qubit) & 1 == 1 {
477                        state[i] *= Complex64::from_polar(1.0, -theta);
478                    }
479                }
480                Self::apply_hadamard(&state, qubit, self.graph.num_vertices)?
481            }
482            _ => {
483                return Err(QuantRS2Error::UnsupportedOperation(
484                    "Measurement basis not yet implemented".to_string(),
485                ));
486            }
487        };
488
489        // Calculate probabilities
490        let mut prob_0 = 0.0;
491        let mut prob_1 = 0.0;
492
493        for i in 0..state.len() {
494            let bit = (i >> qubit) & 1;
495            let prob = state[i].norm_sqr();
496            if bit == 0 {
497                prob_0 += prob;
498            } else {
499                prob_1 += prob;
500            }
501        }
502
503        // Randomly choose outcome
504        use scirs2_core::random::prelude::*;
505        let outcome = if thread_rng().gen::<f64>() < prob_0 / (prob_0 + prob_1) {
506            false
507        } else {
508            true
509        };
510
511        // Project state
512        let norm = if outcome {
513            prob_1.sqrt()
514        } else {
515            prob_0.sqrt()
516        };
517        let mut new_state = Array1::zeros(state.len());
518
519        for i in 0..state.len() {
520            let bit = (i >> qubit) & 1;
521            if (bit == 1) == outcome {
522                new_state[i] = state[i] / norm;
523            }
524        }
525
526        self.state = new_state;
527        self.measurements.insert(qubit, outcome);
528
529        Ok(outcome)
530    }
531
532    /// Apply Pauli corrections based on measurement outcomes
533    pub fn apply_corrections(
534        &mut self,
535        x_corrections: &HashMap<usize, Vec<(usize, bool)>>,
536        z_corrections: &HashMap<usize, Vec<(usize, bool)>>,
537    ) -> QuantRS2Result<()> {
538        let _n = self.graph.num_vertices;
539
540        // Apply X corrections
541        for (target, sources) in x_corrections {
542            let mut apply_x = false;
543            for (source, sign) in sources {
544                if let Some(&outcome) = self.measurements.get(source) {
545                    if outcome && *sign {
546                        apply_x = !apply_x;
547                    }
548                }
549            }
550
551            if apply_x && !self.measurements.contains_key(target) {
552                // Apply X gate
553                for i in 0..self.state.len() {
554                    let bit = (i >> target) & 1;
555                    if bit == 0 {
556                        let j = i | (1 << target);
557                        self.state.swap(i, j);
558                    }
559                }
560            }
561        }
562
563        // Apply Z corrections
564        for (target, sources) in z_corrections {
565            let mut apply_z = false;
566            for (source, sign) in sources {
567                if let Some(&outcome) = self.measurements.get(source) {
568                    if outcome && *sign {
569                        apply_z = !apply_z;
570                    }
571                }
572            }
573
574            if apply_z && !self.measurements.contains_key(target) {
575                // Apply Z gate
576                for i in 0..self.state.len() {
577                    if (i >> target) & 1 == 1 {
578                        self.state[i] *= -1.0;
579                    }
580                }
581            }
582        }
583
584        Ok(())
585    }
586
587    /// Get the reduced state of unmeasured qubits
588    pub fn get_output_state(&self, output_qubits: &[usize]) -> QuantRS2Result<Array1<Complex64>> {
589        let n_out = output_qubits.len();
590        let dim_out = 1 << n_out;
591        let mut output_state = Array1::zeros(dim_out);
592
593        // Map output qubits to indices
594        let mut qubit_map = HashMap::new();
595        for (i, &q) in output_qubits.iter().enumerate() {
596            qubit_map.insert(q, i);
597        }
598
599        // Trace out measured qubits
600        for i in 0..self.state.len() {
601            let mut out_idx = 0;
602            let mut valid = true;
603
604            // Check measured qubits match their outcomes
605            for (&q, &outcome) in &self.measurements {
606                let bit = (i >> q) & 1;
607                if (bit == 1) != outcome {
608                    valid = false;
609                    break;
610                }
611            }
612
613            if valid {
614                // Extract output qubit values
615                for (j, &q) in output_qubits.iter().enumerate() {
616                    if (i >> q) & 1 == 1 {
617                        out_idx |= 1 << j;
618                    }
619                }
620
621                output_state[out_idx] += self.state[i];
622            }
623        }
624
625        // Normalize
626        let norm = output_state
627            .iter()
628            .map(|c: &Complex64| c.norm_sqr())
629            .sum::<f64>()
630            .sqrt();
631        if norm > 0.0 {
632            output_state = output_state / Complex64::new(norm, 0.0);
633        }
634
635        Ok(output_state)
636    }
637}
638
639/// MBQC computation flow
640pub struct MBQCComputation {
641    /// Cluster state
642    pub cluster: ClusterState,
643    /// Measurement pattern
644    pub pattern: MeasurementPattern,
645    /// Current step in computation
646    pub current_step: usize,
647}
648
649impl MBQCComputation {
650    /// Create a new MBQC computation
651    pub fn new(graph: Graph, pattern: MeasurementPattern) -> QuantRS2Result<Self> {
652        let cluster = ClusterState::from_graph(graph)?;
653
654        Ok(Self {
655            cluster,
656            pattern,
657            current_step: 0,
658        })
659    }
660
661    /// Execute one measurement step
662    pub fn step(&mut self) -> QuantRS2Result<Option<(usize, bool)>> {
663        if self.current_step >= self.pattern.order.len() {
664            return Ok(None);
665        }
666
667        let qubit = self.pattern.order[self.current_step];
668        self.current_step += 1;
669
670        // Skip if this is an output qubit that shouldn't be measured
671        if self.pattern.outputs.contains(&qubit) && self.current_step == self.pattern.order.len() {
672            return self.step();
673        }
674
675        // Get measurement basis
676        let basis = self
677            .pattern
678            .measurements
679            .get(&qubit)
680            .copied()
681            .unwrap_or(MeasurementBasis::Computational);
682
683        // Perform measurement
684        let outcome = self.cluster.measure(qubit, basis)?;
685
686        // Apply corrections
687        self.cluster
688            .apply_corrections(&self.pattern.x_corrections, &self.pattern.z_corrections)?;
689
690        Ok(Some((qubit, outcome)))
691    }
692
693    /// Execute all measurements
694    pub fn run(&mut self) -> QuantRS2Result<HashMap<usize, bool>> {
695        while self.step()?.is_some() {}
696        Ok(self.cluster.measurements.clone())
697    }
698
699    /// Get the final output state
700    pub fn output_state(&self) -> QuantRS2Result<Array1<Complex64>> {
701        let outputs: Vec<usize> = self.pattern.outputs.iter().copied().collect();
702        self.cluster.get_output_state(&outputs)
703    }
704}
705
706/// Convert a quantum circuit to MBQC pattern
707pub struct CircuitToMBQC {
708    /// Qubit mapping from circuit to cluster
709    #[allow(dead_code)]
710    qubit_map: HashMap<usize, usize>,
711    /// Current cluster size
712    #[allow(dead_code)]
713    cluster_size: usize,
714}
715
716impl CircuitToMBQC {
717    /// Create a new converter
718    pub fn new() -> Self {
719        Self {
720            qubit_map: HashMap::new(),
721            cluster_size: 0,
722        }
723    }
724
725    /// Convert a single-qubit gate to measurement pattern
726    pub fn convert_single_qubit_gate(
727        &mut self,
728        _qubit: usize,
729        angle: f64,
730    ) -> (Graph, MeasurementPattern) {
731        let mut graph = Graph::new(3);
732        graph
733            .add_edge(0, 1)
734            .expect("Failed to add edge 0-1 in CircuitToMBQC::convert_single_qubit_gate");
735        graph
736            .add_edge(1, 2)
737            .expect("Failed to add edge 1-2 in CircuitToMBQC::convert_single_qubit_gate");
738
739        let pattern = MeasurementPattern::single_qubit_rotation(angle);
740
741        (graph, pattern)
742    }
743
744    /// Convert CNOT gate to measurement pattern
745    pub fn convert_cnot(&mut self, _control: usize, _target: usize) -> (Graph, MeasurementPattern) {
746        // Standard 15-qubit CNOT pattern
747        let mut graph = Graph::new(15);
748
749        // Build the brickwork pattern
750        for i in 0..5 {
751            for j in 0..3 {
752                let idx = i * 3 + j;
753                if j < 2 {
754                    graph
755                        .add_edge(idx, idx + 1)
756                        .expect("Failed to add horizontal edge in CircuitToMBQC::convert_cnot");
757                }
758                if i < 4 {
759                    graph
760                        .add_edge(idx, idx + 3)
761                        .expect("Failed to add vertical edge in CircuitToMBQC::convert_cnot");
762                }
763            }
764        }
765
766        let pattern = MeasurementPattern::cnot();
767
768        (graph, pattern)
769    }
770}
771
772impl Default for CircuitToMBQC {
773    fn default() -> Self {
774        Self::new()
775    }
776}
777
778#[cfg(test)]
779mod tests {
780    use super::*;
781
782    #[test]
783    fn test_graph_construction() {
784        let mut graph = Graph::new(4);
785        graph
786            .add_edge(0, 1)
787            .expect("Failed to add edge 0-1 in test_graph_construction");
788        graph
789            .add_edge(1, 2)
790            .expect("Failed to add edge 1-2 in test_graph_construction");
791        graph
792            .add_edge(2, 3)
793            .expect("Failed to add edge 2-3 in test_graph_construction");
794
795        assert_eq!(
796            graph
797                .neighbors(1)
798                .expect("Failed to get neighbors of vertex 1 in test_graph_construction")
799                .len(),
800            2
801        );
802        assert!(graph
803            .neighbors(1)
804            .expect(
805                "Failed to get neighbors of vertex 1 for contains check in test_graph_construction"
806            )
807            .contains(&0));
808        assert!(graph.neighbors(1).expect("Failed to get neighbors of vertex 1 for second contains check in test_graph_construction").contains(&2));
809    }
810
811    #[test]
812    fn test_linear_cluster() {
813        let graph = Graph::linear_cluster(5);
814        assert_eq!(graph.num_vertices, 5);
815        assert_eq!(
816            graph
817                .neighbors(2)
818                .expect("Failed to get neighbors of vertex 2 in test_linear_cluster")
819                .len(),
820            2
821        );
822        assert_eq!(
823            graph
824                .neighbors(0)
825                .expect("Failed to get neighbors of vertex 0 in test_linear_cluster")
826                .len(),
827            1
828        );
829        assert_eq!(
830            graph
831                .neighbors(4)
832                .expect("Failed to get neighbors of vertex 4 in test_linear_cluster")
833                .len(),
834            1
835        );
836    }
837
838    #[test]
839    fn test_rectangular_cluster() {
840        let graph = Graph::rectangular_cluster(3, 3);
841        assert_eq!(graph.num_vertices, 9);
842
843        // Corner vertex has 2 neighbors
844        assert_eq!(
845            graph
846                .neighbors(0)
847                .expect("Failed to get neighbors of vertex 0 in test_rectangular_cluster")
848                .len(),
849            2
850        );
851
852        // Center vertex has 4 neighbors
853        assert_eq!(
854            graph
855                .neighbors(4)
856                .expect("Failed to get neighbors of vertex 4 in test_rectangular_cluster")
857                .len(),
858            4
859        );
860    }
861
862    #[test]
863    fn test_cluster_state_creation() {
864        let graph = Graph::linear_cluster(3);
865        let cluster = ClusterState::from_graph(graph)
866            .expect("Failed to create cluster state in test_cluster_state_creation");
867
868        // Check state is normalized
869        let norm: f64 = cluster.state.iter().map(|c| c.norm_sqr()).sum();
870        assert!((norm - 1.0).abs() < 1e-10);
871
872        // Check dimension
873        assert_eq!(cluster.state.len(), 8); // 2^3
874    }
875
876    #[test]
877    fn test_measurement_pattern() {
878        let mut pattern = MeasurementPattern::new();
879        pattern.add_measurement(0, MeasurementBasis::X);
880        pattern.add_measurement(1, MeasurementBasis::XY(PI / 4.0));
881        pattern.add_x_correction(2, 0, true);
882        pattern.add_z_correction(2, 1, true);
883
884        assert_eq!(pattern.measurements.len(), 2);
885        assert_eq!(pattern.order.len(), 2);
886        assert!(pattern.x_corrections.contains_key(&2));
887    }
888
889    #[test]
890    fn test_single_qubit_measurement() {
891        let graph = Graph::new(1);
892        let mut cluster = ClusterState::from_graph(graph)
893            .expect("Failed to create cluster state in test_single_qubit_measurement");
894
895        // Measure in X basis
896        let outcome = cluster
897            .measure(0, MeasurementBasis::X)
898            .expect("Failed to measure qubit 0 in test_single_qubit_measurement");
899
900        // Check qubit is marked as measured
901        assert!(cluster.measurements.contains_key(&0));
902        assert_eq!(cluster.measurements[&0], outcome);
903    }
904
905    #[test]
906    fn test_mbqc_computation() {
907        let graph = Graph::linear_cluster(3);
908        let pattern = MeasurementPattern::single_qubit_rotation(PI / 4.0);
909
910        let mut computation = MBQCComputation::new(graph, pattern)
911            .expect("Failed to create MBQC computation in test_mbqc_computation");
912
913        // Run computation
914        let outcomes = computation
915            .run()
916            .expect("Failed to run MBQC computation in test_mbqc_computation");
917
918        // Check measurements were performed
919        assert!(outcomes.contains_key(&0));
920        assert!(outcomes.contains_key(&1));
921    }
922
923    #[test]
924    fn test_circuit_conversion() {
925        let mut converter = CircuitToMBQC::new();
926
927        // Convert single-qubit gate
928        let (graph, pattern) = converter.convert_single_qubit_gate(0, PI / 2.0);
929        assert_eq!(graph.num_vertices, 3);
930        assert_eq!(pattern.measurements.len(), 2);
931
932        // Convert CNOT
933        let (graph, _pattern) = converter.convert_cnot(0, 1);
934        assert_eq!(graph.num_vertices, 15);
935    }
936}