Skip to main content

quantrs2_sim/pennylane/
wire.rs

1//! Wire/qubit mapping types for the PennyLane JSON protocol.
2//!
3//! PennyLane represents qubits as "wires" (integers or strings).
4//! This module provides the mapping between PennyLane wires and
5//! QuantRS2 `QubitId` values.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// A PennyLane wire identifier (integer wire index in the device JSON).
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct WireId(pub usize);
13
14impl WireId {
15    /// The wire index as a `usize`.
16    pub const fn index(self) -> usize {
17        self.0
18    }
19}
20
21impl std::fmt::Display for WireId {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        write!(f, "{}", self.0)
24    }
25}
26
27/// Bidirectional mapping between PennyLane wire labels and qubit indices.
28///
29/// PennyLane uses zero-based integer wires by default.  When a circuit
30/// uses wires `[0, 1, 2, ...]` (dense mapping) the `WireMap` is trivial.
31/// Non-contiguous or string-labelled wires must be remapped to a contiguous
32/// qubit range before simulation.
33#[derive(Debug, Clone, Default)]
34pub struct WireMap {
35    wire_to_qubit: HashMap<usize, u32>,
36    qubit_to_wire: HashMap<u32, usize>,
37}
38
39impl WireMap {
40    /// Build a wire map from a sorted list of unique wire indices.
41    ///
42    /// Wire `wires[i]` maps to qubit `i`.
43    pub fn from_wires(wires: &[usize]) -> Self {
44        let mut wire_to_qubit = HashMap::with_capacity(wires.len());
45        let mut qubit_to_wire = HashMap::with_capacity(wires.len());
46        for (qubit_idx, &wire) in wires.iter().enumerate() {
47            wire_to_qubit.insert(wire, qubit_idx as u32);
48            qubit_to_wire.insert(qubit_idx as u32, wire);
49        }
50        Self {
51            wire_to_qubit,
52            qubit_to_wire,
53        }
54    }
55
56    /// Map a PennyLane wire index to a QuantRS2 `QubitId`.
57    pub fn wire_to_qubit(&self, wire: usize) -> Option<quantrs2_core::qubit::QubitId> {
58        self.wire_to_qubit
59            .get(&wire)
60            .copied()
61            .map(quantrs2_core::qubit::QubitId)
62    }
63
64    /// Map a QuantRS2 qubit index back to a PennyLane wire index.
65    pub fn qubit_to_wire(&self, qubit: u32) -> Option<usize> {
66        self.qubit_to_wire.get(&qubit).copied()
67    }
68
69    /// Total number of wires (= total number of qubits).
70    pub fn num_wires(&self) -> usize {
71        self.wire_to_qubit.len()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_dense_wire_map() {
81        let map = WireMap::from_wires(&[0, 1, 2]);
82        assert_eq!(map.num_wires(), 3);
83        assert_eq!(map.wire_to_qubit(0).map(|q| q.0), Some(0));
84        assert_eq!(map.wire_to_qubit(2).map(|q| q.0), Some(2));
85        assert_eq!(map.qubit_to_wire(1), Some(1));
86    }
87
88    #[test]
89    fn test_sparse_wire_map() {
90        let map = WireMap::from_wires(&[3, 7, 12]);
91        // wire 3 → qubit 0, wire 7 → qubit 1, wire 12 → qubit 2
92        assert_eq!(map.wire_to_qubit(3).map(|q| q.0), Some(0));
93        assert_eq!(map.wire_to_qubit(7).map(|q| q.0), Some(1));
94        assert_eq!(map.wire_to_qubit(12).map(|q| q.0), Some(2));
95        assert_eq!(map.qubit_to_wire(0), Some(3));
96    }
97
98    #[test]
99    fn test_missing_wire() {
100        let map = WireMap::from_wires(&[0, 1]);
101        assert!(map.wire_to_qubit(99).is_none());
102    }
103}