qumat 0.0.1__py3-none-any.whl → 0.5.0__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.
- qumat/__init__.py +20 -0
- qumat/amazon_braket_backend.py +122 -17
- qumat/cirq_backend.py +105 -16
- qumat/qdp.py +63 -0
- qumat/qiskit_backend.py +125 -24
- qumat/qumat.py +508 -13
- qumat-0.5.0.dist-info/METADATA +111 -0
- qumat-0.5.0.dist-info/RECORD +11 -0
- {qumat-0.0.1.dist-info → qumat-0.5.0.dist-info}/WHEEL +1 -1
- {qumat-0.0.1.dist-info → qumat-0.5.0.dist-info/licenses}/LICENSE +0 -112
- qumat-0.5.0.dist-info/licenses/NOTICE +5 -0
- qumat-0.0.1.dist-info/METADATA +0 -77
- qumat-0.0.1.dist-info/NOTICE +0 -41
- qumat-0.0.1.dist-info/RECORD +0 -10
qumat/qiskit_backend.py
CHANGED
|
@@ -15,103 +15,204 @@
|
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
#
|
|
17
17
|
import qiskit
|
|
18
|
+
from qiskit_aer import AerSimulator
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
def initialize_backend(backend_config):
|
|
20
|
-
backend_options = backend_config[
|
|
21
|
-
simulator_type = backend_options[
|
|
22
|
-
shots = backend_options
|
|
23
|
-
|
|
22
|
+
backend_options = backend_config["backend_options"]
|
|
23
|
+
simulator_type = backend_options["simulator_type"]
|
|
24
|
+
shots = backend_options.get("shots", 1)
|
|
25
|
+
|
|
26
|
+
# Map legacy simulator types to AerSimulator methods
|
|
27
|
+
simulator_methods = {
|
|
28
|
+
"aer_simulator": "automatic",
|
|
29
|
+
"statevector_simulator": "statevector",
|
|
30
|
+
"qasm_simulator": "automatic",
|
|
31
|
+
"unitary_simulator": "unitary",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if simulator_type in simulator_methods:
|
|
35
|
+
backend = AerSimulator(method=simulator_methods[simulator_type])
|
|
36
|
+
else:
|
|
37
|
+
backend = AerSimulator(method=simulator_type)
|
|
38
|
+
|
|
24
39
|
backend.shots = shots
|
|
25
40
|
return backend
|
|
26
41
|
|
|
27
42
|
|
|
28
|
-
def create_empty_circuit(num_qubits):
|
|
29
|
-
|
|
43
|
+
def create_empty_circuit(num_qubits: int | None = None):
|
|
44
|
+
if num_qubits is not None:
|
|
45
|
+
return qiskit.QuantumCircuit(num_qubits)
|
|
46
|
+
else:
|
|
47
|
+
return qiskit.QuantumCircuit()
|
|
48
|
+
|
|
30
49
|
|
|
31
50
|
def apply_not_gate(circuit, qubit_index):
|
|
32
51
|
# Apply a NOT gate (X gate) on the specified qubit
|
|
33
52
|
circuit.x(qubit_index)
|
|
34
53
|
|
|
54
|
+
|
|
35
55
|
def apply_hadamard_gate(circuit, qubit_index):
|
|
36
56
|
# Apply a Hadamard gate on the specified qubit
|
|
37
57
|
circuit.h(qubit_index)
|
|
38
58
|
|
|
59
|
+
|
|
39
60
|
def apply_cnot_gate(circuit, control_qubit_index, target_qubit_index):
|
|
40
61
|
# Apply a CNOT gate (controlled-X gate) with the specified control and
|
|
41
62
|
# target qubits
|
|
42
63
|
circuit.cx(control_qubit_index, target_qubit_index)
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
|
|
66
|
+
def apply_toffoli_gate(
|
|
67
|
+
circuit, control_qubit_index1, control_qubit_index2, target_qubit_index
|
|
68
|
+
):
|
|
47
69
|
# Apply a Toffoli gate (controlled-controlled-X gate) with the
|
|
48
70
|
# specified control and target qubits
|
|
49
|
-
circuit.ccx(control_qubit_index1,
|
|
50
|
-
|
|
51
|
-
target_qubit_index)
|
|
71
|
+
circuit.ccx(control_qubit_index1, control_qubit_index2, target_qubit_index)
|
|
72
|
+
|
|
52
73
|
|
|
53
74
|
def apply_swap_gate(circuit, qubit_index1, qubit_index2):
|
|
54
75
|
# Apply a SWAP gate to exchange the states of two qubits
|
|
55
76
|
circuit.swap(qubit_index1, qubit_index2)
|
|
56
77
|
|
|
78
|
+
|
|
79
|
+
def apply_cswap_gate(
|
|
80
|
+
circuit, control_qubit_index, target_qubit_index1, target_qubit_index2
|
|
81
|
+
):
|
|
82
|
+
# Apply a controlled-SWAP (Fredkin) gate with the specified control and target qubits
|
|
83
|
+
circuit.cswap(control_qubit_index, target_qubit_index1, target_qubit_index2)
|
|
84
|
+
|
|
85
|
+
|
|
57
86
|
def apply_pauli_x_gate(circuit, qubit_index):
|
|
58
87
|
# Apply a Pauli X gate on the specified qubit
|
|
59
88
|
circuit.x(qubit_index)
|
|
60
89
|
|
|
90
|
+
|
|
61
91
|
def apply_pauli_y_gate(circuit, qubit_index):
|
|
62
92
|
# Apply a Pauli Y gate on the specified qubit
|
|
63
93
|
circuit.y(qubit_index)
|
|
64
94
|
|
|
95
|
+
|
|
65
96
|
def apply_pauli_z_gate(circuit, qubit_index):
|
|
66
97
|
# Apply a Pauli Z gate on the specified qubit
|
|
67
98
|
circuit.z(qubit_index)
|
|
68
99
|
|
|
100
|
+
|
|
101
|
+
def apply_t_gate(circuit, qubit_index):
|
|
102
|
+
# Apply a T gate (π/8 gate) on the specified qubit
|
|
103
|
+
circuit.t(qubit_index)
|
|
104
|
+
|
|
105
|
+
|
|
69
106
|
def execute_circuit(circuit, backend, backend_config):
|
|
107
|
+
working_circuit = circuit.copy()
|
|
108
|
+
|
|
70
109
|
# Add measurements if they are not already present
|
|
71
|
-
if
|
|
72
|
-
|
|
110
|
+
# Check if circuit already has measurement operations
|
|
111
|
+
has_measurements = any(
|
|
112
|
+
isinstance(inst.operation, qiskit.circuit.Measure)
|
|
113
|
+
for inst in working_circuit.data
|
|
114
|
+
)
|
|
115
|
+
if not has_measurements:
|
|
116
|
+
working_circuit.measure_all()
|
|
73
117
|
|
|
74
118
|
# Ensure the circuit is parameterized properly
|
|
75
|
-
if
|
|
119
|
+
if working_circuit.parameters:
|
|
76
120
|
# Parse the global parameter configuration
|
|
77
|
-
parameter_bindings = {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
121
|
+
parameter_bindings = {
|
|
122
|
+
param: backend_config["parameter_values"][str(param)]
|
|
123
|
+
for param in working_circuit.parameters
|
|
124
|
+
}
|
|
125
|
+
transpiled_circuit = qiskit.transpile(working_circuit, backend)
|
|
126
|
+
bound_circuit = transpiled_circuit.assign_parameters(parameter_bindings)
|
|
127
|
+
job = backend.run(
|
|
128
|
+
bound_circuit, shots=backend_config["backend_options"].get("shots", 1)
|
|
129
|
+
)
|
|
81
130
|
result = job.result()
|
|
82
131
|
return result.get_counts()
|
|
83
132
|
else:
|
|
84
|
-
transpiled_circuit = qiskit.transpile(
|
|
85
|
-
job =
|
|
133
|
+
transpiled_circuit = qiskit.transpile(working_circuit, backend)
|
|
134
|
+
job = backend.run(
|
|
135
|
+
transpiled_circuit, shots=backend_config["backend_options"].get("shots", 1)
|
|
136
|
+
)
|
|
86
137
|
result = job.result()
|
|
87
138
|
return result.get_counts()
|
|
88
139
|
|
|
140
|
+
|
|
89
141
|
# placeholder method for use in the testing suite
|
|
90
142
|
def get_final_state_vector(circuit, backend, backend_config):
|
|
91
|
-
|
|
143
|
+
working_circuit = circuit.copy()
|
|
144
|
+
|
|
145
|
+
simulator = AerSimulator(method="statevector")
|
|
146
|
+
|
|
147
|
+
# Add save_statevector instruction
|
|
148
|
+
working_circuit.save_statevector()
|
|
149
|
+
|
|
150
|
+
# Bind parameters if present
|
|
151
|
+
if working_circuit.parameters:
|
|
152
|
+
parameter_values = backend_config.get("parameter_values", {})
|
|
153
|
+
parameter_bindings = {
|
|
154
|
+
param: parameter_values[str(param)] for param in working_circuit.parameters
|
|
155
|
+
}
|
|
156
|
+
working_circuit = working_circuit.assign_parameters(parameter_bindings)
|
|
92
157
|
|
|
93
158
|
# Simulate the circuit
|
|
94
|
-
|
|
159
|
+
transpiled_circuit = qiskit.transpile(working_circuit, simulator)
|
|
160
|
+
job = simulator.run(transpiled_circuit)
|
|
95
161
|
result = job.result()
|
|
96
162
|
|
|
97
163
|
return result.get_statevector()
|
|
98
164
|
|
|
165
|
+
|
|
99
166
|
def draw_circuit(circuit):
|
|
100
167
|
# Use Qiskit's built-in drawing function
|
|
101
|
-
|
|
168
|
+
return circuit.draw()
|
|
169
|
+
|
|
102
170
|
|
|
103
171
|
def apply_rx_gate(circuit, qubit_index, angle):
|
|
104
172
|
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
|
|
105
173
|
circuit.rx(param, qubit_index)
|
|
106
174
|
|
|
175
|
+
|
|
107
176
|
def apply_ry_gate(circuit, qubit_index, angle):
|
|
108
177
|
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
|
|
109
178
|
circuit.ry(param, qubit_index)
|
|
110
179
|
|
|
180
|
+
|
|
111
181
|
def apply_rz_gate(circuit, qubit_index, angle):
|
|
112
182
|
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
|
|
113
183
|
circuit.rz(param, qubit_index)
|
|
114
184
|
|
|
185
|
+
|
|
115
186
|
def apply_u_gate(circuit, qubit_index, theta, phi, lambd):
|
|
116
187
|
# Apply the U gate directly with specified parameters
|
|
117
188
|
circuit.u(theta, phi, lambd, qubit_index)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def calculate_prob_zero(results, ancilla_qubit, num_qubits):
|
|
192
|
+
"""
|
|
193
|
+
Calculate the probability of measuring the ancilla qubit in |0> state.
|
|
194
|
+
|
|
195
|
+
Qiskit uses little-endian qubit ordering with string format results,
|
|
196
|
+
where the rightmost bit corresponds to qubit 0.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
results: Measurement results from execute_circuit() (dict with string keys)
|
|
200
|
+
ancilla_qubit: Index of the ancilla qubit
|
|
201
|
+
num_qubits: Total number of qubits in the circuit
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
float: Probability of measuring ancilla in |0> state
|
|
205
|
+
"""
|
|
206
|
+
# Handle different result formats from different backends
|
|
207
|
+
if isinstance(results, list):
|
|
208
|
+
results = results[0]
|
|
209
|
+
|
|
210
|
+
total_shots = sum(results.values())
|
|
211
|
+
count_zero = 0
|
|
212
|
+
|
|
213
|
+
for state, count in results.items():
|
|
214
|
+
# Qiskit: little-endian, rightmost bit is qubit 0
|
|
215
|
+
if len(state) > ancilla_qubit and state[-(ancilla_qubit + 1)] == "0":
|
|
216
|
+
count_zero += count
|
|
217
|
+
|
|
218
|
+
return count_zero / total_shots if total_shots > 0 else 0.0
|