-
Notifications
You must be signed in to change notification settings - Fork 370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simulate QuantumCircuit
without QObj
#1717
Changes from all commits
0993b72
4111ce8
d83ea88
3c0e6cc
2223888
c9585f5
b0f66dc
d26aeb5
76ed979
922c6af
87a99fb
f156b90
a0014ff
a60363b
cc735ac
184380a
eb1d739
4eef01b
5b537f7
0745eb8
ea586c5
48dc673
46e1da1
7d95adb
1137082
a6ee7c3
a68df4c
2b1c077
8f9629a
ff7deba
4748360
e75676d
5827b7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,12 @@ | |
""" | ||
|
||
import itertools | ||
from copy import copy | ||
from typing import List | ||
|
||
from qiskit.circuit import QuantumCircuit, Clbit | ||
from qiskit.circuit import QuantumCircuit, Clbit, ParameterExpression | ||
from qiskit.extensions import Initialize | ||
from qiskit.providers.options import Options | ||
from qiskit.pulse import Schedule, ScheduleBlock | ||
from qiskit.circuit.controlflow import ( | ||
WhileLoopOp, | ||
|
@@ -25,6 +28,10 @@ | |
BreakLoopOp, | ||
ContinueLoopOp) | ||
from qiskit.compiler import transpile | ||
from qiskit.qobj import QobjExperimentHeader | ||
from qiskit_aer.aererror import AerError | ||
# pylint: disable=import-error, no-name-in-module | ||
from qiskit_aer.backends.controller_wrappers import AerCircuit, AerConfig | ||
from .backend_utils import circuit_optypes | ||
from ..library.control_flow_instructions import AerMark, AerJump | ||
|
||
|
@@ -325,3 +332,221 @@ def compile_circuit(circuits, basis_gates=None, optypes=None): | |
compile a circuit that have control-flow instructions | ||
""" | ||
return AerCompiler().compile(circuits, basis_gates, optypes) | ||
|
||
|
||
def generate_aer_config( | ||
circuits: List[QuantumCircuit], | ||
backend_options: Options, | ||
**run_options | ||
) -> AerConfig: | ||
"""generates a configuration to run simulation. | ||
|
||
Args: | ||
circuits: circuit(s) to be converted | ||
backend_options: backend options | ||
run_options: run options | ||
|
||
Returns: | ||
AerConfig to run Aer | ||
""" | ||
num_qubits = max(circuit.num_qubits for circuit in circuits) | ||
memory_slots = max(circuit.num_clbits for circuit in circuits) | ||
|
||
config = AerConfig() | ||
config.memory_slots = memory_slots | ||
config.n_qubits = num_qubits | ||
for key, value in backend_options.__dict__.items(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should really have an API for doing this natively in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See: Qiskit/qiskit#9704 |
||
if hasattr(config, key) and value is not None: | ||
setattr(config, key, value) | ||
for key, value in run_options.items(): | ||
if hasattr(config, key) and value is not None: | ||
setattr(config, key, value) | ||
return config | ||
|
||
|
||
def assemble_circuit(circuit: QuantumCircuit): | ||
"""assemble circuit object mapped to AER::Circuit""" | ||
|
||
num_qubits = circuit.num_qubits | ||
num_memory = circuit.num_clbits | ||
max_conditional_idx = 0 | ||
|
||
qreg_sizes = [] | ||
creg_sizes = [] | ||
global_phase = float(circuit.global_phase) | ||
|
||
for qreg in circuit.qregs: | ||
qreg_sizes.append([qreg.name, qreg.size]) | ||
for creg in circuit.cregs: | ||
creg_sizes.append([creg.name, creg.size]) | ||
|
||
is_conditional = any( | ||
getattr(inst.operation, "condition", None) for inst in circuit.data | ||
) | ||
|
||
header = QobjExperimentHeader( | ||
n_qubits=num_qubits, | ||
qreg_sizes=qreg_sizes, | ||
memory_slots=num_memory, | ||
creg_sizes=creg_sizes, | ||
name=circuit.name, | ||
global_phase=global_phase, | ||
) | ||
|
||
if circuit.metadata is not None: | ||
header.metadata = circuit.metadata | ||
|
||
qubit_indices = {qubit: idx for idx, qubit in enumerate(circuit.qubits)} | ||
clbit_indices = {clbit: idx for idx, clbit in enumerate(circuit.clbits)} | ||
|
||
aer_circ = AerCircuit() | ||
aer_circ.set_header(header) | ||
aer_circ.num_qubits = num_qubits | ||
aer_circ.num_memory = num_memory | ||
aer_circ.global_phase_angle = global_phase | ||
|
||
for inst in circuit.data: | ||
# To convert to a qobj-style conditional, insert a bfunc prior | ||
# to the conditional instruction to map the creg ?= val condition | ||
# onto a gating register bit. | ||
conditional_reg = -1 | ||
if hasattr(inst.operation, "condition") and inst.operation.condition: | ||
ctrl_reg, ctrl_val = inst.operation.condition | ||
mask = 0 | ||
val = 0 | ||
if isinstance(ctrl_reg, Clbit): | ||
mask = 1 << clbit_indices[ctrl_reg] | ||
val = (ctrl_val & 1) << clbit_indices[ctrl_reg] | ||
else: | ||
for clbit, idx in clbit_indices.items(): | ||
if clbit in ctrl_reg: | ||
mask |= 1 << idx | ||
val |= ((ctrl_val >> list(ctrl_reg).index(clbit)) & 1) << idx | ||
conditional_reg = num_memory + max_conditional_idx | ||
aer_circ.bfunc(f"0x{mask:X}", f"0x{val:X}", "==", conditional_reg) | ||
max_conditional_idx += 1 | ||
|
||
_assemble_op(aer_circ, inst, qubit_indices, clbit_indices, | ||
is_conditional, conditional_reg) | ||
|
||
return aer_circ | ||
|
||
|
||
def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, conditional_reg): | ||
operation = inst.operation | ||
qubits = [qubit_indices[qubit] for qubit in inst.qubits] | ||
clbits = [clbit_indices[clbit] for clbit in inst.clbits] | ||
name = operation.name | ||
label = operation.label | ||
params = operation.params if hasattr(operation, "params") else None | ||
copied = False | ||
|
||
for i, param in enumerate(params): | ||
if (isinstance(param, ParameterExpression) and len(param.parameters) > 0): | ||
if not copied: | ||
params = copy(params) | ||
copied = True | ||
params[i] = 0.0 | ||
|
||
if name in {'ccx', 'ccz', 'cp', 'cswap', 'csx', 'cx', 'cy', 'cz', 'delay', 'ecr', | ||
'h', 'id', 'mcp', 'mcphase', 'mcr', 'mcrx', 'mcry', 'mcrz', 'mcswap', | ||
'mcsx', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcx', 'mcx_gray', 'mcy', 'mcz', | ||
'p', 'r', 'rx', 'rxx', 'ry', 'ryy', 'rz', 'rzx', 'rzz', 's', 'sdg', 'swap', | ||
'sx', 'sxdg', 't', 'tdg', 'u', 'x', 'y', 'z', 'u1', 'u2', 'u3', | ||
'cu', 'cu1', 'cu2', 'cu3'}: | ||
aer_circ.gate(name, qubits, params, [], conditional_reg, label if label else name) | ||
elif name == 'measure': | ||
if is_conditional: | ||
aer_circ.measure(qubits, clbits, clbits) | ||
else: | ||
aer_circ.measure(qubits, clbits, []) | ||
elif name == 'reset': | ||
aer_circ.reset(qubits) | ||
elif name == 'diagonal': | ||
aer_circ.diagonal(qubits, params, label if label else 'diagonal') | ||
elif name == 'unitary': | ||
hhorii marked this conversation as resolved.
Show resolved
Hide resolved
|
||
aer_circ.unitary(qubits, params[0], conditional_reg, label if label else 'unitary') | ||
elif name == 'pauli': | ||
aer_circ.gate(name, qubits, [], params, conditional_reg, label if label else name) | ||
elif name == 'initialize': | ||
aer_circ.initialize(qubits, params) | ||
elif name == 'roerror': | ||
aer_circ.roerror(qubits, params) | ||
elif name == 'multiplexer': | ||
aer_circ.multiplexer(qubits, params, conditional_reg, label if label else name) | ||
elif name == 'kraus': | ||
aer_circ.kraus(qubits, params, conditional_reg) | ||
elif name in {'save_statevector', 'save_statevector_dict', 'save_clifford', | ||
'save_probabilities', 'save_probabilities_dict', 'save_matrix_product_state', | ||
'save_unitary', 'save_superop', 'save_density_matrix', 'save_state', | ||
'save_stabilizer'}: | ||
aer_circ.save_state(qubits, name, operation._subtype, label if label else name) | ||
elif name in {'save_amplitudes', 'save_amplitudes_sq'}: | ||
aer_circ.save_amplitudes(qubits, name, params, operation._subtype, | ||
label if label else name) | ||
elif name in ('save_expval', 'save_expval_var'): | ||
paulis = [] | ||
coeff_reals = [] | ||
coeff_imags = [] | ||
for pauli, coeff in operation.params: | ||
paulis.append(pauli) | ||
coeff_reals.append(coeff[0]) | ||
coeff_imags.append(coeff[1]) | ||
aer_circ.save_expval(qubits, name, paulis, coeff_reals, coeff_imags, operation._subtype, | ||
label if label else name) | ||
elif name == 'set_statevector': | ||
aer_circ.set_statevector(qubits, params) | ||
elif name == 'set_unitary': | ||
aer_circ.set_unitary(qubits, params) | ||
elif name == 'set_density_matrix': | ||
aer_circ.set_density_matrix(qubits, params) | ||
elif name == 'set_stabilizer': | ||
aer_circ.set_clifford(qubits, params) | ||
elif name == 'set_superop': | ||
aer_circ.set_superop(qubits, params) | ||
elif name == 'set_matrix_product_state': | ||
aer_circ.set_matrix_product_state(qubits, params) | ||
elif name == 'superop': | ||
aer_circ.superop(qubits, params[0], conditional_reg) | ||
elif name == 'barrier': | ||
pass | ||
elif name == 'jump': | ||
aer_circ.jump(qubits, params, conditional_reg) | ||
elif name == 'mark': | ||
aer_circ.mark(qubits, params) | ||
elif name == 'qerror_loc': | ||
aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg) | ||
elif name in ('for_loop', 'while_loop', 'if_else'): | ||
raise AerError('control-flow instructions must be converted ' | ||
f'to jump and mark instructions: {name}') | ||
|
||
else: | ||
raise AerError(f'unknown instruction: {name}') | ||
|
||
|
||
def assemble_circuits( | ||
circuits: List[QuantumCircuit] | ||
) -> List[AerCircuit]: | ||
"""converts a list of Qiskit circuits into circuits mapped AER::Circuit | ||
|
||
Args: | ||
circuits: circuit(s) to be converted | ||
|
||
Returns: | ||
circuits to be run on the Aer backends | ||
|
||
Examples: | ||
|
||
.. code-block:: python | ||
|
||
from qiskit.circuit import QuantumCircuit | ||
from qiskit_aer.backends.aer_compiler import assemble_circuits | ||
# Create a circuit to be simulated | ||
qc = QuantumCircuit(2, 2) | ||
qc.h(0) | ||
qc.cx(0, 1) | ||
qc.measure_all() | ||
# Generate AerCircuit from the input circuit | ||
aer_qc_list = assemble_circuits(circuits=[qc]) | ||
""" | ||
return [assemble_circuit(circuit) for circuit in circuits] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that it really matters since this class really isn't user facing. I'm surprised you didn't just add these to the constructor for
AerConfig