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.
- quantum_debugger/__init__.py +22 -0
- quantum_debugger/core/__init__.py +7 -0
- quantum_debugger/core/circuit.py +269 -0
- quantum_debugger/core/gates.py +153 -0
- quantum_debugger/core/quantum_state.py +293 -0
- quantum_debugger/debugger/__init__.py +7 -0
- quantum_debugger/debugger/breakpoints.py +132 -0
- quantum_debugger/debugger/debugger.py +222 -0
- quantum_debugger/debugger/inspector.py +219 -0
- quantum_debugger/profiler/__init__.py +6 -0
- quantum_debugger/profiler/metrics.py +129 -0
- quantum_debugger/profiler/profiler.py +174 -0
- quantum_debugger/visualization/__init__.py +6 -0
- quantum_debugger/visualization/bloch_sphere.py +152 -0
- quantum_debugger/visualization/state_viz.py +205 -0
- quantum_debugger-0.1.1.dist-info/METADATA +176 -0
- quantum_debugger-0.1.1.dist-info/RECORD +20 -0
- quantum_debugger-0.1.1.dist-info/WHEEL +5 -0
- quantum_debugger-0.1.1.dist-info/licenses/LICENSE +21 -0
- quantum_debugger-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
State inspection utilities for debugging
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from typing import Dict, List, Tuple
|
|
7
|
+
from quantum_debugger.core.quantum_state import QuantumState
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StateInspector:
|
|
11
|
+
"""Utilities for inspecting quantum states during debugging"""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def get_state_summary(state: QuantumState) -> Dict:
|
|
15
|
+
"""
|
|
16
|
+
Get a comprehensive summary of a quantum state
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
state: Quantum state to inspect
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Dictionary with state information
|
|
23
|
+
"""
|
|
24
|
+
probabilities = state.get_probabilities()
|
|
25
|
+
|
|
26
|
+
summary = {
|
|
27
|
+
'num_qubits': state.num_qubits,
|
|
28
|
+
'dimension': state.dim,
|
|
29
|
+
'norm': np.linalg.norm(state.state_vector),
|
|
30
|
+
'entropy': state.entropy(),
|
|
31
|
+
'is_entangled': state.is_entangled() if state.num_qubits == 2 else None,
|
|
32
|
+
'max_amplitude': float(np.max(np.abs(state.state_vector))),
|
|
33
|
+
'nonzero_amplitudes': int(np.sum(np.abs(state.state_vector) > 1e-10)),
|
|
34
|
+
'most_likely_state': int(np.argmax(probabilities)),
|
|
35
|
+
'max_probability': float(np.max(probabilities))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return summary
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def get_measurement_stats(state: QuantumState) -> Dict[str, float]:
|
|
42
|
+
"""
|
|
43
|
+
Get measurement statistics for all computational basis states
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
state: Quantum state
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dictionary mapping basis states to probabilities
|
|
50
|
+
"""
|
|
51
|
+
probabilities = state.get_probabilities()
|
|
52
|
+
stats = {}
|
|
53
|
+
|
|
54
|
+
for i, prob in enumerate(probabilities):
|
|
55
|
+
if prob > 1e-10: # Only include non-negligible probabilities
|
|
56
|
+
binary = format(i, f'0{state.num_qubits}b')
|
|
57
|
+
stats[binary] = float(prob)
|
|
58
|
+
|
|
59
|
+
return dict(sorted(stats.items(), key=lambda x: x[1], reverse=True))
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_qubit_probabilities(state: QuantumState, qubit: int) -> Tuple[float, float]:
|
|
63
|
+
"""
|
|
64
|
+
Get measurement probabilities for a specific qubit
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
state: Quantum state
|
|
68
|
+
qubit: Qubit index
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
(probability of 0, probability of 1)
|
|
72
|
+
"""
|
|
73
|
+
prob_0 = state.get_measurement_probability(qubit, 0)
|
|
74
|
+
prob_1 = state.get_measurement_probability(qubit, 1)
|
|
75
|
+
return (prob_0, prob_1)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def get_amplitude_info(state: QuantumState) -> List[Dict]:
|
|
79
|
+
"""
|
|
80
|
+
Get detailed amplitude information for all basis states
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
state: Quantum state
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of dictionaries with amplitude information
|
|
87
|
+
"""
|
|
88
|
+
amplitudes = []
|
|
89
|
+
|
|
90
|
+
for i, amplitude in enumerate(state.state_vector):
|
|
91
|
+
if abs(amplitude) > 1e-10:
|
|
92
|
+
binary = format(i, f'0{state.num_qubits}b')
|
|
93
|
+
amplitudes.append({
|
|
94
|
+
'basis_state': binary,
|
|
95
|
+
'index': i,
|
|
96
|
+
'amplitude': complex(amplitude),
|
|
97
|
+
'magnitude': float(abs(amplitude)),
|
|
98
|
+
'phase': float(np.angle(amplitude)),
|
|
99
|
+
'probability': float(abs(amplitude) ** 2)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return sorted(amplitudes, key=lambda x: x['probability'], reverse=True)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def compare_states(state1: QuantumState, state2: QuantumState) -> Dict:
|
|
106
|
+
"""
|
|
107
|
+
Compare two quantum states
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
state1: First quantum state
|
|
111
|
+
state2: Second quantum state
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Dictionary with comparison metrics
|
|
115
|
+
"""
|
|
116
|
+
if state1.num_qubits != state2.num_qubits:
|
|
117
|
+
raise ValueError("States must have same number of qubits")
|
|
118
|
+
|
|
119
|
+
fidelity = state1.fidelity(state2)
|
|
120
|
+
|
|
121
|
+
# Calculate trace distance
|
|
122
|
+
trace_distance = np.linalg.norm(
|
|
123
|
+
state1.state_vector - state2.state_vector
|
|
124
|
+
) / np.sqrt(2)
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
'fidelity': float(fidelity),
|
|
128
|
+
'trace_distance': float(trace_distance),
|
|
129
|
+
'are_equal': fidelity > 0.9999
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def check_superposition(state: QuantumState, qubit: int, threshold: float = 0.1) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
Check if a qubit is in superposition
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
state: Quantum state
|
|
139
|
+
qubit: Qubit index
|
|
140
|
+
threshold: Threshold for considering a state in superposition
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if qubit is in superposition
|
|
144
|
+
"""
|
|
145
|
+
prob_0, prob_1 = StateInspector.get_qubit_probabilities(state, qubit)
|
|
146
|
+
|
|
147
|
+
# In superposition if neither probability is close to 0 or 1
|
|
148
|
+
return threshold < prob_0 < (1 - threshold) and threshold < prob_1 < (1 - threshold)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def format_state_string(state: QuantumState, max_terms: int = 10) -> str:
|
|
152
|
+
"""
|
|
153
|
+
Format quantum state as readable string
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
state: Quantum state
|
|
157
|
+
max_terms: Maximum number of terms to show
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Formatted string representation
|
|
161
|
+
"""
|
|
162
|
+
amplitudes = StateInspector.get_amplitude_info(state)
|
|
163
|
+
|
|
164
|
+
if not amplitudes:
|
|
165
|
+
return "|0>"
|
|
166
|
+
|
|
167
|
+
terms = []
|
|
168
|
+
for amp_info in amplitudes[:max_terms]:
|
|
169
|
+
magnitude = amp_info['magnitude']
|
|
170
|
+
phase = amp_info['phase']
|
|
171
|
+
basis = amp_info['basis_state']
|
|
172
|
+
|
|
173
|
+
# Format coefficient
|
|
174
|
+
if abs(phase) < 1e-10:
|
|
175
|
+
coef = f"{magnitude:.3f}"
|
|
176
|
+
elif abs(phase - np.pi) < 1e-10:
|
|
177
|
+
coef = f"-{magnitude:.3f}"
|
|
178
|
+
else:
|
|
179
|
+
coef = f"{magnitude:.3f}e^({phase:.2f}i)"
|
|
180
|
+
|
|
181
|
+
terms.append(f"{coef}|{basis}>")
|
|
182
|
+
|
|
183
|
+
if len(amplitudes) > max_terms:
|
|
184
|
+
terms.append("...")
|
|
185
|
+
|
|
186
|
+
return " + ".join(terms)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def print_state_info(state: QuantumState):
|
|
190
|
+
"""
|
|
191
|
+
Print comprehensive state information to console
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
state: Quantum state to inspect
|
|
195
|
+
"""
|
|
196
|
+
print("=" * 60)
|
|
197
|
+
print("QUANTUM STATE INSPECTION")
|
|
198
|
+
print("=" * 60)
|
|
199
|
+
|
|
200
|
+
summary = StateInspector.get_state_summary(state)
|
|
201
|
+
print(f"\nState Vector: {StateInspector.format_state_string(state)}")
|
|
202
|
+
print(f"\nNumber of Qubits: {summary['num_qubits']}")
|
|
203
|
+
print(f"Dimension: {summary['dimension']}")
|
|
204
|
+
print(f"Entropy: {summary['entropy']:.4f}")
|
|
205
|
+
|
|
206
|
+
if summary['is_entangled'] is not None:
|
|
207
|
+
print(f"Entangled: {summary['is_entangled']}")
|
|
208
|
+
|
|
209
|
+
print(f"\nMeasurement Statistics:")
|
|
210
|
+
stats = StateInspector.get_measurement_stats(state)
|
|
211
|
+
for basis_state, prob in list(stats.items())[:5]:
|
|
212
|
+
print(f" |{basis_state}>: {prob:.4f} ({prob*100:.2f}%)")
|
|
213
|
+
|
|
214
|
+
print(f"\nPer-Qubit Probabilities:")
|
|
215
|
+
for q in range(state.num_qubits):
|
|
216
|
+
prob_0, prob_1 = StateInspector.get_qubit_probabilities(state, q)
|
|
217
|
+
print(f" Qubit {q}: |0>={prob_0:.4f}, |1>={prob_1:.4f}")
|
|
218
|
+
|
|
219
|
+
print("=" * 60)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Circuit metrics and analysis
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
from quantum_debugger.core.circuit import QuantumCircuit
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CircuitMetrics:
|
|
10
|
+
"""Container for circuit analysis metrics"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, circuit: QuantumCircuit):
|
|
13
|
+
self.circuit = circuit
|
|
14
|
+
self._compute_metrics()
|
|
15
|
+
|
|
16
|
+
def _compute_metrics(self):
|
|
17
|
+
"""Compute all metrics"""
|
|
18
|
+
self.num_qubits = self.circuit.num_qubits
|
|
19
|
+
self.total_gates = len(self.circuit.gates)
|
|
20
|
+
self.depth = self.circuit.depth()
|
|
21
|
+
|
|
22
|
+
# Gate type counts
|
|
23
|
+
self.gate_counts = {}
|
|
24
|
+
for gate in self.circuit.gates:
|
|
25
|
+
name = gate.name
|
|
26
|
+
self.gate_counts[name] = self.gate_counts.get(name, 0) + 1
|
|
27
|
+
|
|
28
|
+
# Special counts
|
|
29
|
+
self.single_qubit_gates = sum(
|
|
30
|
+
1 for g in self.circuit.gates if g.num_qubits == 1
|
|
31
|
+
)
|
|
32
|
+
self.two_qubit_gates = sum(
|
|
33
|
+
1 for g in self.circuit.gates if g.num_qubits == 2
|
|
34
|
+
)
|
|
35
|
+
self.three_qubit_gates = sum(
|
|
36
|
+
1 for g in self.circuit.gates if g.num_qubits == 3
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self.cnot_count = self.gate_counts.get('CNOT', 0) + self.gate_counts.get('CX', 0)
|
|
40
|
+
self.t_count = self.gate_counts.get('T', 0)
|
|
41
|
+
|
|
42
|
+
# Critical path analysis
|
|
43
|
+
self.critical_path = self._find_critical_path()
|
|
44
|
+
|
|
45
|
+
# Parallelism factor
|
|
46
|
+
self.parallelism = self.total_gates / self.depth if self.depth > 0 else 0
|
|
47
|
+
|
|
48
|
+
def _find_critical_path(self) -> List[int]:
|
|
49
|
+
"""Find the critical path (longest dependency chain)"""
|
|
50
|
+
if not self.circuit.gates:
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
# Track dependencies
|
|
54
|
+
qubit_last_gate = {}
|
|
55
|
+
gate_start_time = []
|
|
56
|
+
|
|
57
|
+
for i, gate in enumerate(self.circuit.gates):
|
|
58
|
+
# Find latest dependency
|
|
59
|
+
start_time = 0
|
|
60
|
+
for q in gate.qubits:
|
|
61
|
+
if q in qubit_last_gate:
|
|
62
|
+
start_time = max(start_time, gate_start_time[qubit_last_gate[q]] + 1)
|
|
63
|
+
|
|
64
|
+
gate_start_time.append(start_time)
|
|
65
|
+
|
|
66
|
+
# Update last gate for each qubit
|
|
67
|
+
for q in gate.qubits:
|
|
68
|
+
qubit_last_gate[q] = i
|
|
69
|
+
|
|
70
|
+
# Find path with maximum start time
|
|
71
|
+
max_time = max(gate_start_time)
|
|
72
|
+
critical_gates = [i for i, t in enumerate(gate_start_time) if t == max_time]
|
|
73
|
+
|
|
74
|
+
return critical_gates
|
|
75
|
+
|
|
76
|
+
def get_summary(self) -> Dict:
|
|
77
|
+
"""Get summary dictionary of all metrics"""
|
|
78
|
+
return {
|
|
79
|
+
'num_qubits': self.num_qubits,
|
|
80
|
+
'total_gates': self.total_gates,
|
|
81
|
+
'depth': self.depth,
|
|
82
|
+
'single_qubit_gates': self.single_qubit_gates,
|
|
83
|
+
'two_qubit_gates': self.two_qubit_gates,
|
|
84
|
+
'three_qubit_gates': self.three_qubit_gates,
|
|
85
|
+
'cnot_count': self.cnot_count,
|
|
86
|
+
't_count': self.t_count,
|
|
87
|
+
'parallelism_factor': self.parallelism,
|
|
88
|
+
'gate_counts': self.gate_counts,
|
|
89
|
+
'critical_path_length': len(self.critical_path)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def estimate_execution_time(self, gate_time: float = 1.0,
|
|
93
|
+
cnot_time: float = 10.0) -> float:
|
|
94
|
+
"""
|
|
95
|
+
Estimate execution time on quantum hardware
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
gate_time: Time for single-qubit gate (μs)
|
|
99
|
+
cnot_time: Time for CNOT gate (μs)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Estimated time in microseconds
|
|
103
|
+
"""
|
|
104
|
+
single_time = self.single_qubit_gates * gate_time
|
|
105
|
+
two_time = self.two_qubit_gates * cnot_time
|
|
106
|
+
|
|
107
|
+
return single_time + two_time
|
|
108
|
+
|
|
109
|
+
def estimate_error_rate(self, single_error: float = 0.001,
|
|
110
|
+
cnot_error: float = 0.01) -> float:
|
|
111
|
+
"""
|
|
112
|
+
Estimate cumulative error rate
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
single_error: Error rate for single-qubit gates
|
|
116
|
+
cnot_error: Error rate for CNOT gates
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Estimated cumulative error rate
|
|
120
|
+
"""
|
|
121
|
+
# Simplified error model (multiplicative)
|
|
122
|
+
single_fidelity = (1 - single_error) ** self.single_qubit_gates
|
|
123
|
+
two_fidelity = (1 - cnot_error) ** self.two_qubit_gates
|
|
124
|
+
|
|
125
|
+
total_fidelity = single_fidelity * two_fidelity
|
|
126
|
+
return 1 - total_fidelity
|
|
127
|
+
|
|
128
|
+
def __repr__(self):
|
|
129
|
+
return f"CircuitMetrics({self.num_qubits} qubits, {self.total_gates} gates, depth {self.depth})"
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Circuit profiler for performance analysis
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
from quantum_debugger.core.circuit import QuantumCircuit
|
|
7
|
+
from quantum_debugger.profiler.metrics import CircuitMetrics
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CircuitProfiler:
|
|
11
|
+
"""Profiler for analyzing quantum circuit performance"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, circuit: QuantumCircuit):
|
|
14
|
+
"""
|
|
15
|
+
Initialize profiler
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
circuit: Quantum circuit to profile
|
|
19
|
+
"""
|
|
20
|
+
self.circuit = circuit
|
|
21
|
+
self.metrics = CircuitMetrics(circuit)
|
|
22
|
+
|
|
23
|
+
def analyze(self) -> CircuitMetrics:
|
|
24
|
+
"""
|
|
25
|
+
Analyze the circuit and return metrics
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
CircuitMetrics object with analysis results
|
|
29
|
+
"""
|
|
30
|
+
return self.metrics
|
|
31
|
+
|
|
32
|
+
def get_optimization_suggestions(self) -> List[str]:
|
|
33
|
+
"""
|
|
34
|
+
Get suggestions for circuit optimization
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of optimization suggestions
|
|
38
|
+
"""
|
|
39
|
+
suggestions = []
|
|
40
|
+
|
|
41
|
+
# High CNOT count
|
|
42
|
+
if self.metrics.cnot_count > self.metrics.num_qubits * 3:
|
|
43
|
+
suggestions.append(
|
|
44
|
+
f"⚠️ High CNOT count ({self.metrics.cnot_count}). "
|
|
45
|
+
"Consider CNOT reduction techniques."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# High T count
|
|
49
|
+
if self.metrics.t_count > self.metrics.num_qubits * 2:
|
|
50
|
+
suggestions.append(
|
|
51
|
+
f"⚠️ High T-gate count ({self.metrics.t_count}). "
|
|
52
|
+
"T-gates are expensive on fault-tolerant hardware."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# High depth
|
|
56
|
+
if self.metrics.depth > self.metrics.num_qubits * 5:
|
|
57
|
+
suggestions.append(
|
|
58
|
+
f"⚠️ Circuit depth ({self.metrics.depth}) is high. "
|
|
59
|
+
"Consider parallelizing gates."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Low parallelism
|
|
63
|
+
if self.metrics.parallelism < 1.5 and self.metrics.num_qubits > 2:
|
|
64
|
+
suggestions.append(
|
|
65
|
+
f"💡 Low parallelism factor ({self.metrics.parallelism:.2f}). "
|
|
66
|
+
"Look for opportunities to execute gates in parallel."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Consecutive gates on same qubit
|
|
70
|
+
consecutive = self._find_consecutive_single_qubit_gates()
|
|
71
|
+
if consecutive:
|
|
72
|
+
suggestions.append(
|
|
73
|
+
f"💡 Found {len(consecutive)} sequences of consecutive single-qubit gates. "
|
|
74
|
+
"These could be combined into single rotations."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if not suggestions:
|
|
78
|
+
suggestions.append("✅ Circuit appears well optimized!")
|
|
79
|
+
|
|
80
|
+
return suggestions
|
|
81
|
+
|
|
82
|
+
def _find_consecutive_single_qubit_gates(self) -> List[tuple]:
|
|
83
|
+
"""Find consecutive single-qubit gates on the same qubit"""
|
|
84
|
+
consecutive = []
|
|
85
|
+
|
|
86
|
+
for q in range(self.circuit.num_qubits):
|
|
87
|
+
sequence = []
|
|
88
|
+
for i, gate in enumerate(self.circuit.gates):
|
|
89
|
+
if gate.qubits == [q] and gate.num_qubits == 1:
|
|
90
|
+
sequence.append(i)
|
|
91
|
+
else:
|
|
92
|
+
if len(sequence) >= 2:
|
|
93
|
+
consecutive.append((q, sequence))
|
|
94
|
+
sequence = []
|
|
95
|
+
|
|
96
|
+
if len(sequence) >= 2:
|
|
97
|
+
consecutive.append((q, sequence))
|
|
98
|
+
|
|
99
|
+
return consecutive
|
|
100
|
+
|
|
101
|
+
def compare_with_ideal(self) -> Dict:
|
|
102
|
+
"""
|
|
103
|
+
Compare circuit with theoretical ideal
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Comparison metrics
|
|
107
|
+
"""
|
|
108
|
+
# Theoretical minimum for common algorithms
|
|
109
|
+
ideal_depth = self.metrics.num_qubits # Very rough estimate
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
'actual_depth': self.metrics.depth,
|
|
113
|
+
'ideal_depth': ideal_depth,
|
|
114
|
+
'depth_overhead': self.metrics.depth / ideal_depth if ideal_depth > 0 else 0,
|
|
115
|
+
'gate_efficiency': self.metrics.parallelism
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def print_report(self):
|
|
119
|
+
"""Print comprehensive profiling report"""
|
|
120
|
+
print("\n" + "=" * 70)
|
|
121
|
+
print(" " * 20 + "CIRCUIT PROFILING REPORT")
|
|
122
|
+
print("=" * 70)
|
|
123
|
+
|
|
124
|
+
# Basic metrics
|
|
125
|
+
print(f"\n📊 BASIC METRICS")
|
|
126
|
+
print(f" Number of Qubits: {self.metrics.num_qubits}")
|
|
127
|
+
print(f" Total Gates: {self.metrics.total_gates}")
|
|
128
|
+
print(f" Circuit Depth: {self.metrics.depth}")
|
|
129
|
+
print(f" Parallelism Factor: {self.metrics.parallelism:.2f}")
|
|
130
|
+
|
|
131
|
+
# Gate breakdown
|
|
132
|
+
print(f"\n🔧 GATE BREAKDOWN")
|
|
133
|
+
print(f" Single-Qubit Gates: {self.metrics.single_qubit_gates}")
|
|
134
|
+
print(f" Two-Qubit Gates: {self.metrics.two_qubit_gates}")
|
|
135
|
+
if self.metrics.three_qubit_gates > 0:
|
|
136
|
+
print(f" Three-Qubit Gates: {self.metrics.three_qubit_gates}")
|
|
137
|
+
|
|
138
|
+
print(f"\n Gate Type Counts:")
|
|
139
|
+
for gate_name, count in sorted(self.metrics.gate_counts.items(),
|
|
140
|
+
key=lambda x: x[1], reverse=True):
|
|
141
|
+
print(f" {gate_name}: {count}")
|
|
142
|
+
|
|
143
|
+
# Special metrics
|
|
144
|
+
print(f"\n⚡ SPECIAL METRICS")
|
|
145
|
+
print(f" CNOT Count: {self.metrics.cnot_count}")
|
|
146
|
+
print(f" T-Gate Count: {self.metrics.t_count}")
|
|
147
|
+
print(f" Critical Path Length: {len(self.metrics.critical_path)} gates")
|
|
148
|
+
|
|
149
|
+
# Performance estimates
|
|
150
|
+
exec_time = self.metrics.estimate_execution_time()
|
|
151
|
+
error_rate = self.metrics.estimate_error_rate()
|
|
152
|
+
|
|
153
|
+
print(f"\n⏱️ PERFORMANCE ESTIMATES")
|
|
154
|
+
print(f" Estimated Execution Time: {exec_time:.2f} μs")
|
|
155
|
+
print(f" Estimated Error Rate: {error_rate*100:.4f}%")
|
|
156
|
+
print(f" Estimated Fidelity: {(1-error_rate)*100:.4f}%")
|
|
157
|
+
|
|
158
|
+
# Optimization suggestions
|
|
159
|
+
suggestions = self.get_optimization_suggestions()
|
|
160
|
+
print(f"\n💡 OPTIMIZATION SUGGESTIONS")
|
|
161
|
+
for suggestion in suggestions:
|
|
162
|
+
print(f" {suggestion}")
|
|
163
|
+
|
|
164
|
+
# Comparison with ideal
|
|
165
|
+
comparison = self.compare_with_ideal()
|
|
166
|
+
print(f"\n📈 COMPARISON WITH IDEAL")
|
|
167
|
+
print(f" Actual Depth: {comparison['actual_depth']}")
|
|
168
|
+
print(f" Theoretical Minimum: {comparison['ideal_depth']}")
|
|
169
|
+
print(f" Depth Overhead: {comparison['depth_overhead']:.2f}x")
|
|
170
|
+
|
|
171
|
+
print("=" * 70 + "\n")
|
|
172
|
+
|
|
173
|
+
def __repr__(self):
|
|
174
|
+
return f"CircuitProfiler({self.metrics})"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bloch sphere visualization for single-qubit states
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
8
|
+
from quantum_debugger.core.quantum_state import QuantumState
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BlochSphere:
|
|
12
|
+
"""Bloch sphere visualization for single qubits"""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def plot(state: QuantumState, qubit: int = 0, figsize: tuple = (8, 8)):
|
|
16
|
+
"""
|
|
17
|
+
Plot qubit state on Bloch sphere
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
state: Quantum state
|
|
21
|
+
qubit: Index of qubit to visualize (for multi-qubit systems)
|
|
22
|
+
figsize: Figure size
|
|
23
|
+
"""
|
|
24
|
+
# Get Bloch vector
|
|
25
|
+
x, y, z = state.bloch_vector(qubit)
|
|
26
|
+
|
|
27
|
+
# Create figure
|
|
28
|
+
fig = plt.figure(figsize=figsize)
|
|
29
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
30
|
+
|
|
31
|
+
# Draw sphere
|
|
32
|
+
u = np.linspace(0, 2 * np.pi, 50)
|
|
33
|
+
v = np.linspace(0, np.pi, 50)
|
|
34
|
+
x_sphere = np.outer(np.cos(u), np.sin(v))
|
|
35
|
+
y_sphere = np.outer(np.sin(u), np.sin(v))
|
|
36
|
+
z_sphere = np.outer(np.ones(np.size(u)), np.cos(v))
|
|
37
|
+
|
|
38
|
+
ax.plot_surface(x_sphere, y_sphere, z_sphere,
|
|
39
|
+
color='lightblue', alpha=0.2, linewidth=0)
|
|
40
|
+
|
|
41
|
+
# Draw axes
|
|
42
|
+
axis_length = 1.3
|
|
43
|
+
ax.plot([0, axis_length], [0, 0], [0, 0], 'k-', linewidth=1, alpha=0.5)
|
|
44
|
+
ax.plot([0, 0], [0, axis_length], [0, 0], 'k-', linewidth=1, alpha=0.5)
|
|
45
|
+
ax.plot([0, 0], [0, 0], [0, axis_length], 'k-', linewidth=1, alpha=0.5)
|
|
46
|
+
|
|
47
|
+
# Labels
|
|
48
|
+
ax.text(axis_length, 0, 0, 'X', fontsize=14, fontweight='bold')
|
|
49
|
+
ax.text(0, axis_length, 0, 'Y', fontsize=14, fontweight='bold')
|
|
50
|
+
ax.text(0, 0, axis_length, '|0⟩', fontsize=14, fontweight='bold')
|
|
51
|
+
ax.text(0, 0, -axis_length, '|1⟩', fontsize=14, fontweight='bold')
|
|
52
|
+
|
|
53
|
+
# Draw state vector
|
|
54
|
+
ax.quiver(0, 0, 0, x, y, z, color='red', arrow_length_ratio=0.15,
|
|
55
|
+
linewidth=3, label='State Vector')
|
|
56
|
+
|
|
57
|
+
# Draw projection onto XY plane
|
|
58
|
+
ax.plot([x, x], [y, y], [0, z], 'r--', alpha=0.5, linewidth=1)
|
|
59
|
+
ax.plot([0, x], [0, y], [0, 0], 'r--', alpha=0.5, linewidth=1)
|
|
60
|
+
|
|
61
|
+
# Mark poles
|
|
62
|
+
ax.scatter([0], [0], [1], color='blue', s=100, marker='o', label='|0⟩')
|
|
63
|
+
ax.scatter([0], [0], [-1], color='orange', s=100, marker='o', label='|1⟩')
|
|
64
|
+
|
|
65
|
+
# Mark current state
|
|
66
|
+
ax.scatter([x], [y], [z], color='red', s=150, marker='*',
|
|
67
|
+
label=f'Qubit {qubit}', zorder=10)
|
|
68
|
+
|
|
69
|
+
# Settings
|
|
70
|
+
ax.set_xlim([-1.2, 1.2])
|
|
71
|
+
ax.set_ylim([-1.2, 1.2])
|
|
72
|
+
ax.set_zlim([-1.2, 1.2])
|
|
73
|
+
ax.set_xlabel('X', fontsize=12)
|
|
74
|
+
ax.set_ylabel('Y', fontsize=12)
|
|
75
|
+
ax.set_zlabel('Z', fontsize=12)
|
|
76
|
+
ax.set_title(f'Bloch Sphere - Qubit {qubit}', fontsize=16, fontweight='bold')
|
|
77
|
+
ax.legend(loc='upper right')
|
|
78
|
+
|
|
79
|
+
# Set viewing angle
|
|
80
|
+
ax.view_init(elev=20, azim=45)
|
|
81
|
+
|
|
82
|
+
plt.tight_layout()
|
|
83
|
+
plt.show()
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def plot_trajectory(states: list, qubit: int = 0, figsize: tuple = (8, 8)):
|
|
87
|
+
"""
|
|
88
|
+
Plot trajectory of state evolution on Bloch sphere
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
states: List of QuantumState objects
|
|
92
|
+
qubit: Qubit index to visualize
|
|
93
|
+
figsize: Figure size
|
|
94
|
+
"""
|
|
95
|
+
# Get Bloch vectors for all states
|
|
96
|
+
vectors = [s.bloch_vector(qubit) for s in states]
|
|
97
|
+
xs, ys, zs = zip(*vectors)
|
|
98
|
+
|
|
99
|
+
# Create figure
|
|
100
|
+
fig = plt.figure(figsize=figsize)
|
|
101
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
102
|
+
|
|
103
|
+
# Draw sphere
|
|
104
|
+
u = np.linspace(0, 2 * np.pi, 50)
|
|
105
|
+
v = np.linspace(0, np.pi, 50)
|
|
106
|
+
x_sphere = np.outer(np.cos(u), np.sin(v))
|
|
107
|
+
y_sphere = np.outer(np.sin(u), np.sin(v))
|
|
108
|
+
z_sphere = np.outer(np.ones(np.size(u)), np.cos(v))
|
|
109
|
+
|
|
110
|
+
ax.plot_surface(x_sphere, y_sphere, z_sphere,
|
|
111
|
+
color='lightblue', alpha=0.15, linewidth=0)
|
|
112
|
+
|
|
113
|
+
# Draw axes
|
|
114
|
+
axis_length = 1.3
|
|
115
|
+
ax.plot([0, axis_length], [0, 0], [0, 0], 'k-', linewidth=1, alpha=0.5)
|
|
116
|
+
ax.plot([0, 0], [0, axis_length], [0, 0], 'k-', linewidth=1, alpha=0.5)
|
|
117
|
+
ax.plot([0, 0], [0, 0], [0, axis_length], 'k-', linewidth=1, alpha=0.5)
|
|
118
|
+
|
|
119
|
+
# Labels
|
|
120
|
+
ax.text(axis_length, 0, 0, 'X', fontsize=14, fontweight='bold')
|
|
121
|
+
ax.text(0, axis_length, 0, 'Y', fontsize=14, fontweight='bold')
|
|
122
|
+
ax.text(0, 0, axis_length, '|0⟩', fontsize=14, fontweight='bold')
|
|
123
|
+
ax.text(0, 0, -axis_length, '|1⟩', fontsize=14, fontweight='bold')
|
|
124
|
+
|
|
125
|
+
# Draw trajectory
|
|
126
|
+
ax.plot(xs, ys, zs, 'r-', linewidth=2, alpha=0.7, label='Trajectory')
|
|
127
|
+
|
|
128
|
+
# Mark start and end
|
|
129
|
+
ax.scatter([xs[0]], [ys[0]], [zs[0]], color='green', s=150,
|
|
130
|
+
marker='o', label='Start', zorder=10)
|
|
131
|
+
ax.scatter([xs[-1]], [ys[-1]], [zs[-1]], color='red', s=150,
|
|
132
|
+
marker='*', label='End', zorder=10)
|
|
133
|
+
|
|
134
|
+
# Mark intermediate points
|
|
135
|
+
if len(states) > 2:
|
|
136
|
+
ax.scatter(xs[1:-1], ys[1:-1], zs[1:-1], color='orange',
|
|
137
|
+
s=50, alpha=0.6)
|
|
138
|
+
|
|
139
|
+
# Settings
|
|
140
|
+
ax.set_xlim([-1.2, 1.2])
|
|
141
|
+
ax.set_ylim([-1.2, 1.2])
|
|
142
|
+
ax.set_zlim([-1.2, 1.2])
|
|
143
|
+
ax.set_xlabel('X', fontsize=12)
|
|
144
|
+
ax.set_ylabel('Y', fontsize=12)
|
|
145
|
+
ax.set_zlabel('Z', fontsize=12)
|
|
146
|
+
ax.set_title(f'State Evolution - Qubit {qubit}', fontsize=16, fontweight='bold')
|
|
147
|
+
ax.legend(loc='upper right')
|
|
148
|
+
|
|
149
|
+
ax.view_init(elev=20, azim=45)
|
|
150
|
+
|
|
151
|
+
plt.tight_layout()
|
|
152
|
+
plt.show()
|