quantum-debugger 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ """
2
+ QuantumDebugger - Interactive debugging and profiling for quantum circuits
3
+
4
+ A powerful Python library for step-through debugging, state inspection,
5
+ and performance analysis of quantum circuits.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+ __author__ = "Your Name"
10
+ __license__ = "MIT"
11
+
12
+ from quantum_debugger.core.circuit import QuantumCircuit
13
+ from quantum_debugger.core.quantum_state import QuantumState
14
+ from quantum_debugger.debugger.debugger import QuantumDebugger
15
+ from quantum_debugger.profiler.profiler import CircuitProfiler
16
+
17
+ __all__ = [
18
+ "QuantumCircuit",
19
+ "QuantumState",
20
+ "QuantumDebugger",
21
+ "CircuitProfiler",
22
+ ]
@@ -0,0 +1,7 @@
1
+ """Core quantum computing components"""
2
+
3
+ from quantum_debugger.core.quantum_state import QuantumState
4
+ from quantum_debugger.core.circuit import QuantumCircuit
5
+ from quantum_debugger.core.gates import GateLibrary
6
+
7
+ __all__ = ["QuantumState", "QuantumCircuit", "GateLibrary"]
@@ -0,0 +1,269 @@
1
+ """
2
+ Quantum circuit representation and execution
3
+ """
4
+
5
+ import numpy as np
6
+ from typing import List, Optional, Union
7
+ from quantum_debugger.core.quantum_state import QuantumState
8
+ from quantum_debugger.core.gates import GateLibrary, Gate
9
+
10
+
11
+ class QuantumCircuit:
12
+ """Quantum circuit with gate operations"""
13
+
14
+ def __init__(self, num_qubits: int, num_classical: int = 0):
15
+ """
16
+ Initialize a quantum circuit
17
+
18
+ Args:
19
+ num_qubits: Number of quantum bits
20
+ num_classical: Number of classical bits for measurements
21
+ """
22
+ self.num_qubits = num_qubits
23
+ self.num_classical = num_classical if num_classical > 0 else num_qubits
24
+ self.gates: List[Gate] = []
25
+ self.measurements: List[tuple] = []
26
+ self._initial_state = QuantumState(num_qubits)
27
+
28
+ def _add_gate(self, name: str, matrix: np.ndarray, qubits: Union[int, List[int]], params: dict = None):
29
+ """Add a gate to the circuit"""
30
+ if isinstance(qubits, int):
31
+ qubits = [qubits]
32
+
33
+ gate = Gate(name, matrix, qubits, params)
34
+ self.gates.append(gate)
35
+ return self
36
+
37
+ # Single-qubit gates
38
+ def h(self, qubit: int):
39
+ """Apply Hadamard gate"""
40
+ return self._add_gate('H', GateLibrary.H, qubit)
41
+
42
+ def x(self, qubit: int):
43
+ """Apply Pauli-X (NOT) gate"""
44
+ return self._add_gate('X', GateLibrary.X, qubit)
45
+
46
+ def y(self, qubit: int):
47
+ """Apply Pauli-Y gate"""
48
+ return self._add_gate('Y', GateLibrary.Y, qubit)
49
+
50
+ def z(self, qubit: int):
51
+ """Apply Pauli-Z gate"""
52
+ return self._add_gate('Z', GateLibrary.Z, qubit)
53
+
54
+ def s(self, qubit: int):
55
+ """Apply S (phase) gate"""
56
+ return self._add_gate('S', GateLibrary.S, qubit)
57
+
58
+ def t(self, qubit: int):
59
+ """Apply T gate"""
60
+ return self._add_gate('T', GateLibrary.T, qubit)
61
+
62
+ def rx(self, theta: float, qubit: int):
63
+ """Apply RX rotation gate"""
64
+ return self._add_gate('RX', GateLibrary.RX(theta), qubit, {'theta': theta})
65
+
66
+ def ry(self, theta: float, qubit: int):
67
+ """Apply RY rotation gate"""
68
+ return self._add_gate('RY', GateLibrary.RY(theta), qubit, {'theta': theta})
69
+
70
+ def rz(self, theta: float, qubit: int):
71
+ """Apply RZ rotation gate"""
72
+ return self._add_gate('RZ', GateLibrary.RZ(theta), qubit, {'theta': theta})
73
+
74
+ def phase(self, theta: float, qubit: int):
75
+ """Apply phase shift gate"""
76
+ return self._add_gate('PHASE', GateLibrary.PHASE(theta), qubit, {'theta': theta})
77
+
78
+ # Two-qubit gates
79
+ def cnot(self, control: int, target: int):
80
+ """Apply CNOT (controlled-NOT) gate"""
81
+ return self._add_gate('CNOT', GateLibrary.CNOT, [control, target])
82
+
83
+ def cx(self, control: int, target: int):
84
+ """Alias for CNOT"""
85
+ return self.cnot(control, target)
86
+
87
+ def cz(self, control: int, target: int):
88
+ """Apply CZ (controlled-Z) gate"""
89
+ return self._add_gate('CZ', GateLibrary.CZ, [control, target])
90
+
91
+ def swap(self, qubit1: int, qubit2: int):
92
+ """Apply SWAP gate"""
93
+ return self._add_gate('SWAP', GateLibrary.SWAP, [qubit1, qubit2])
94
+
95
+ # Three-qubit gates
96
+ def toffoli(self, control1: int, control2: int, target: int):
97
+ """Apply Toffoli (CCNOT) gate"""
98
+ return self._add_gate('TOFFOLI', GateLibrary.TOFFOLI, [control1, control2, target])
99
+
100
+ def ccx(self, control1: int, control2: int, target: int):
101
+ """Alias for Toffoli"""
102
+ return self.toffoli(control1, control2, target)
103
+
104
+ # Measurements
105
+ def measure(self, qubit: int, classical_bit: int = None):
106
+ """
107
+ Measure a qubit
108
+
109
+ Args:
110
+ qubit: Qubit index to measure
111
+ classical_bit: Classical bit to store result (defaults to same as qubit)
112
+ """
113
+ if classical_bit is None:
114
+ classical_bit = qubit
115
+ self.measurements.append((qubit, classical_bit))
116
+ return self
117
+
118
+ def measure_all(self):
119
+ """Measure all qubits"""
120
+ for i in range(self.num_qubits):
121
+ self.measure(i, i)
122
+ return self
123
+
124
+ # Circuit information
125
+ def depth(self) -> int:
126
+ """Calculate circuit depth (number of gate layers)"""
127
+ if not self.gates:
128
+ return 0
129
+
130
+ # Track when each qubit is last used
131
+ qubit_times = [0] * self.num_qubits
132
+
133
+ for gate in self.gates:
134
+ # Get max time of qubits involved
135
+ max_time = max(qubit_times[q] for q in gate.qubits)
136
+
137
+ # Update all involved qubits
138
+ for q in gate.qubits:
139
+ qubit_times[q] = max_time + 1
140
+
141
+ return max(qubit_times)
142
+
143
+ def size(self) -> int:
144
+ """Total number of gates"""
145
+ return len(self.gates)
146
+
147
+ def count_gates(self, gate_name: str = None) -> int:
148
+ """
149
+ Count gates of specific type
150
+
151
+ Args:
152
+ gate_name: Name of gate to count (None for all gates)
153
+ """
154
+ if gate_name is None:
155
+ return len(self.gates)
156
+ return sum(1 for g in self.gates if g.name == gate_name)
157
+
158
+ # Execution
159
+ def run(self, shots: int = 1, initial_state: Optional[QuantumState] = None) -> dict:
160
+ """
161
+ Execute the circuit
162
+
163
+ Args:
164
+ shots: Number of times to run the circuit
165
+ initial_state: Optional initial state (defaults to |0...0>)
166
+
167
+ Returns:
168
+ Dictionary with measurement results and statistics
169
+ """
170
+ results = []
171
+
172
+ for _ in range(shots):
173
+ state = initial_state.copy() if initial_state else QuantumState(self.num_qubits)
174
+
175
+ # Apply all gates
176
+ for gate in self.gates:
177
+ state.apply_gate(gate.matrix, gate.qubits)
178
+
179
+ # Perform measurements
180
+ classical_bits = [0] * self.num_classical
181
+ for qubit, classical_bit in self.measurements:
182
+ classical_bits[classical_bit] = state.measure(qubit)
183
+
184
+ results.append(classical_bits)
185
+
186
+ # Analyze results
187
+ counts = {}
188
+ for result in results:
189
+ key = ''.join(map(str, result))
190
+ counts[key] = counts.get(key, 0) + 1
191
+
192
+ return {
193
+ 'counts': counts,
194
+ 'results': results,
195
+ 'shots': shots
196
+ }
197
+
198
+ def get_statevector(self, initial_state: Optional[QuantumState] = None) -> QuantumState:
199
+ """
200
+ Get final state vector without measurements
201
+
202
+ Args:
203
+ initial_state: Optional initial state
204
+
205
+ Returns:
206
+ Final quantum state
207
+ """
208
+ state = initial_state.copy() if initial_state else QuantumState(self.num_qubits)
209
+
210
+ for gate in self.gates:
211
+ state.apply_gate(gate.matrix, gate.qubits)
212
+
213
+ return state
214
+
215
+ # Visualization helpers
216
+ def draw(self, output: str = 'text') -> str:
217
+ """
218
+ Draw the circuit
219
+
220
+ Args:
221
+ output: Output format ('text' for ASCII art)
222
+
223
+ Returns:
224
+ String representation of circuit
225
+ """
226
+ if output == 'text':
227
+ return self._draw_text()
228
+ return str(self)
229
+
230
+ def _draw_text(self) -> str:
231
+ """Draw circuit as ASCII art"""
232
+ lines = []
233
+
234
+ # Header
235
+ lines.append(f"Circuit with {self.num_qubits} qubits, {len(self.gates)} gates")
236
+ lines.append("")
237
+
238
+ # Qubit lines
239
+ for q in range(self.num_qubits):
240
+ line = f"q{q}: |0>─"
241
+
242
+ for i, gate in enumerate(self.gates):
243
+ if q in gate.qubits:
244
+ # This qubit is involved in this gate
245
+ if len(gate.qubits) == 1:
246
+ # Single-qubit gate
247
+ gate_str = f"[{gate.name}]"
248
+ elif gate.qubits[0] == q:
249
+ # First qubit (control or first target)
250
+ gate_str = "●" if gate.name in ['CNOT', 'CZ'] else "┬"
251
+ else:
252
+ # Target qubit
253
+ gate_str = "⊕" if gate.name == 'CNOT' else "┴"
254
+
255
+ line += gate_str + "─"
256
+ else:
257
+ # Not involved, just continue line
258
+ line += "─" * (len(gate.name) + 3)
259
+
260
+ lines.append(line)
261
+
262
+ return "\n".join(lines)
263
+
264
+ def __repr__(self):
265
+ gate_str = ", ".join(str(g) for g in self.gates)
266
+ return f"QuantumCircuit({self.num_qubits} qubits, gates=[{gate_str}])"
267
+
268
+ def __str__(self):
269
+ return self.draw()
@@ -0,0 +1,153 @@
1
+ """
2
+ Quantum gate definitions and operations
3
+ """
4
+
5
+ import numpy as np
6
+ from typing import Dict, Callable
7
+
8
+
9
+ class GateLibrary:
10
+ """Library of standard quantum gates"""
11
+
12
+ # Single-qubit gates
13
+ I = np.array([[1, 0], [0, 1]], dtype=complex) # Identity
14
+ X = np.array([[0, 1], [1, 0]], dtype=complex) # Pauli-X (NOT)
15
+ Y = np.array([[0, -1j], [1j, 0]], dtype=complex) # Pauli-Y
16
+ Z = np.array([[1, 0], [0, -1]], dtype=complex) # Pauli-Z
17
+ H = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) # Hadamard
18
+ S = np.array([[1, 0], [0, 1j]], dtype=complex) # Phase gate
19
+ T = np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex) # T gate
20
+
21
+ # Rotation gates (parameterized)
22
+ @staticmethod
23
+ def RX(theta: float) -> np.ndarray:
24
+ """Rotation around X-axis"""
25
+ return np.array([
26
+ [np.cos(theta / 2), -1j * np.sin(theta / 2)],
27
+ [-1j * np.sin(theta / 2), np.cos(theta / 2)]
28
+ ], dtype=complex)
29
+
30
+ @staticmethod
31
+ def RY(theta: float) -> np.ndarray:
32
+ """Rotation around Y-axis"""
33
+ return np.array([
34
+ [np.cos(theta / 2), -np.sin(theta / 2)],
35
+ [np.sin(theta / 2), np.cos(theta / 2)]
36
+ ], dtype=complex)
37
+
38
+ @staticmethod
39
+ def RZ(theta: float) -> np.ndarray:
40
+ """Rotation around Z-axis"""
41
+ return np.array([
42
+ [np.exp(-1j * theta / 2), 0],
43
+ [0, np.exp(1j * theta / 2)]
44
+ ], dtype=complex)
45
+
46
+ @staticmethod
47
+ def PHASE(theta: float) -> np.ndarray:
48
+ """Phase shift gate"""
49
+ return np.array([
50
+ [1, 0],
51
+ [0, np.exp(1j * theta)]
52
+ ], dtype=complex)
53
+
54
+ # Two-qubit gates (little-endian: qubit 0 is LSB)
55
+ # CNOT: control=qubit 0, target=qubit 1
56
+ # Flips target when control is 1: |10⟩↔|11⟩ (indices 1↔3)
57
+ CNOT = np.array([
58
+ [1, 0, 0, 0], # |00⟩ → |00⟩
59
+ [0, 0, 0, 1], # |10⟩ → |11⟩
60
+ [0, 0, 1, 0], # |01⟩ → |01⟩
61
+ [0, 1, 0, 0], # |11⟩ → |10⟩
62
+ ], dtype=complex)
63
+
64
+ CZ = np.array([
65
+ [1, 0, 0, 0],
66
+ [0, 1, 0, 0],
67
+ [0, 0, 1, 0],
68
+ [0, 0, 0, -1]
69
+ ], dtype=complex)
70
+
71
+ SWAP = np.array([
72
+ [1, 0, 0, 0],
73
+ [0, 0, 1, 0],
74
+ [0, 1, 0, 0],
75
+ [0, 0, 0, 1]
76
+ ], dtype=complex)
77
+
78
+ # Three-qubit gates
79
+ # Toffoli (CCNOT) for little-endian: controls on qubits 0,1; target is qubit 2
80
+ # Flips target when both controls are 1 (indices 3↔7 in little-endian)
81
+ TOFFOLI = np.array([
82
+ [1, 0, 0, 0, 0, 0, 0, 0],
83
+ [0, 1, 0, 0, 0, 0, 0, 0],
84
+ [0, 0, 1, 0, 0, 0, 0, 0],
85
+ [0, 0, 0, 0, 0, 0, 0, 1], # |110⟩ ↔ |111⟩
86
+ [0, 0, 0, 0, 1, 0, 0, 0],
87
+ [0, 0, 0, 0, 0, 1, 0, 0],
88
+ [0, 0, 0, 0, 0, 0, 1, 0],
89
+ [0, 0, 0, 1, 0, 0, 0, 0], # |111⟩ ↔ |110⟩
90
+ ], dtype=complex)
91
+
92
+ @staticmethod
93
+ def controlled_gate(gate: np.ndarray) -> np.ndarray:
94
+ """Create a controlled version of a single-qubit gate"""
95
+ n = gate.shape[0]
96
+ controlled = np.eye(2 * n, dtype=complex)
97
+ controlled[n:, n:] = gate
98
+ return controlled
99
+
100
+ @staticmethod
101
+ def tensor_product(*gates) -> np.ndarray:
102
+ """Compute tensor product of multiple gates"""
103
+ result = gates[0]
104
+ for gate in gates[1:]:
105
+ result = np.kron(result, gate)
106
+ return result
107
+
108
+
109
+ class Gate:
110
+ """Represents a quantum gate operation"""
111
+
112
+ def __init__(self, name: str, matrix: np.ndarray, qubits: list, params: dict = None):
113
+ """
114
+ Initialize a gate
115
+
116
+ Args:
117
+ name: Gate name (e.g., 'H', 'CNOT', 'RX')
118
+ matrix: Unitary matrix representing the gate
119
+ qubits: List of qubit indices the gate acts on
120
+ params: Optional parameters (e.g., rotation angles)
121
+ """
122
+ self.name = name
123
+ self.matrix = matrix
124
+ self.qubits = qubits if isinstance(qubits, list) else [qubits]
125
+ self.params = params or {}
126
+
127
+ def __repr__(self):
128
+ qubit_str = ','.join(map(str, self.qubits))
129
+ if self.params:
130
+ param_str = ','.join(f"{k}={v}" for k, v in self.params.items())
131
+ return f"{self.name}({param_str})[q{qubit_str}]"
132
+ return f"{self.name}[q{qubit_str}]"
133
+
134
+ def __str__(self):
135
+ return self.__repr__()
136
+
137
+ @property
138
+ def num_qubits(self):
139
+ """Number of qubits this gate acts on"""
140
+ return len(self.qubits)
141
+
142
+ def is_controlled(self):
143
+ """Check if this is a controlled gate"""
144
+ return self.num_qubits > 1 and self.name.startswith('C')
145
+
146
+ def dagger(self):
147
+ """Return the conjugate transpose of this gate"""
148
+ return Gate(
149
+ name=f"{self.name}†",
150
+ matrix=self.matrix.conj().T,
151
+ qubits=self.qubits,
152
+ params=self.params
153
+ )