From 0062f646a306827e3b620661c30d56cfc58d4d40 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Thu, 30 May 2024 11:22:15 +0900 Subject: [PATCH] Handle gates with ctrl_stete=0 (#2148) * handle cx gate with ctrl_stete=0 * add handling *_o0, add test for cx_o0 * format * fix test * fix test * fix test cx * fix to handle multiple control states * fix ctrl_state * format * resolve conflict again * fix aer_compiler --- qiskit_aer/backends/aer_compiler.py | 36 +++++++++++++++---- .../backends/aer_simulator/test_cliffords.py | 1 + test/terra/reference/ref_2q_clifford.py | 26 ++++++++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index 59ce6a3c0e..43bff9123c 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -812,18 +812,43 @@ def _assemble_op( aer_cond_expr = conditional_expr.accept(_AssembleExprImpl(circ)) if conditional_expr else None + # check if there is ctrl_state option + ctrl_state_pos = name.find("_o") + if ctrl_state_pos > 0: + gate_name = name[0:ctrl_state_pos] + else: + gate_name = name + num_of_aer_ops = 1 # fmt: off - if basis_gates is None and name in { + if (gate_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", "crx", "cry", "crz", - }: - aer_circ.gate(name, qubits, params, [], conditional_reg, aer_cond_expr, - label if label else name) + }) and (basis_gates is None or gate_name in basis_gates): + if ctrl_state_pos > 0: + # Add x gates for ctrl qubits which state=0 + ctrl_state = int(name[ctrl_state_pos+2:len(name)]) + for i in range(len(qubits)): + if (ctrl_state >> i) & 1 == 0: + qubits_i = [qubits[len(qubits) - 1 - i]] + aer_circ.gate("x", qubits_i, params, [], conditional_reg, aer_cond_expr, + label if label else "x") + num_of_aer_ops += 1 + aer_circ.gate(gate_name, qubits, params, [], conditional_reg, aer_cond_expr, + label if label else gate_name) + for i in range(len(qubits)): + if (ctrl_state >> i) & 1 == 0: + qubits_i = [qubits[len(qubits) - 1 - i]] + aer_circ.gate("x", qubits_i, params, [], conditional_reg, aer_cond_expr, + label if label else "x") + num_of_aer_ops += 1 + else: + aer_circ.gate(name, qubits, params, [], conditional_reg, aer_cond_expr, + label if label else name) elif name == "measure": if is_conditional: aer_circ.measure(qubits, clbits, clbits) @@ -915,9 +940,6 @@ def _assemble_op( aer_circ.mark(qubits, params) elif name == "qerror_loc": aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg, aer_cond_expr) - elif basis_gates is not None and name in basis_gates: - aer_circ.gate(name, qubits, params, [], conditional_reg, aer_cond_expr, - label if label else name) elif name in ("for_loop", "while_loop", "if_else"): raise AerError( "control-flow instructions must be converted " f"to jump and mark instructions: {name}" diff --git a/test/terra/backends/aer_simulator/test_cliffords.py b/test/terra/backends/aer_simulator/test_cliffords.py index df0c2994d0..4a92ba27fa 100644 --- a/test/terra/backends/aer_simulator/test_cliffords.py +++ b/test/terra/backends/aer_simulator/test_cliffords.py @@ -16,6 +16,7 @@ from test.terra.reference import ref_1q_clifford from test.terra.reference import ref_2q_clifford from qiskit import transpile +from qiskit import QuantumCircuit from test.terra.backends.simulator_test_case import SimulatorTestCase, supported_methods SUPPORTED_METHODS = [ diff --git a/test/terra/reference/ref_2q_clifford.py b/test/terra/reference/ref_2q_clifford.py index 59cc2b0629..e368984eca 100644 --- a/test/terra/reference/ref_2q_clifford.py +++ b/test/terra/reference/ref_2q_clifford.py @@ -109,6 +109,14 @@ def cx_gate_circuits_deterministic(final_measure=True): circuit.measure(qr, cr) circuits.append(circuit) + # test ctrl_states=0 + circuit = QuantumCircuit(*regs) + circuit.cx(qr[0], qr[1], ctrl_state=0) + if final_measure: + circuit.barrier(qr) + circuit.measure(qr, cr) + circuits.append(circuit) + return circuits @@ -132,6 +140,8 @@ def cx_gate_counts_deterministic(shots, hex_counts=True): targets.append({"0x1": shots}) # {"00": shots} # CX10.(X^X), |10> state targets.append({"0x2": shots}) # {"00": shots} + # test ctrl_states=0 + targets.append({"0x2": shots}) # {"00": shots} else: # CX01, |00> state targets.append({"00": shots}) # {"00": shots} @@ -149,6 +159,8 @@ def cx_gate_counts_deterministic(shots, hex_counts=True): targets.append({"01": shots}) # {"00": shots} # CX10.(X^X), |10> state targets.append({"10": shots}) # {"00": shots} + # test ctrl_states=0 + targets.append({"10": shots}) # {"00": shots} return targets @@ -225,6 +237,16 @@ def cx_gate_circuits_nondeterministic(final_measure=True): circuit.barrier(qr) circuit.measure(qr, cr) circuits.append(circuit) + + # test ctrl_states=0 + circuit = QuantumCircuit(*regs) + circuit.h(qr[0]) + circuit.cx(qr[0], qr[1], ctrl_state=0) + if final_measure: + circuit.barrier(qr) + circuit.measure(qr, cr) + circuits.append(circuit) + return circuits @@ -236,11 +258,15 @@ def cx_gate_counts_nondeterministic(shots, hex_counts=True): targets.append({"0x0": shots / 2, "0x3": shots / 2}) # CX10.(I^H), Bell state targets.append({"0x0": shots / 2, "0x3": shots / 2}) + # test ctrl_states=0 + targets.append({"0x1": shots / 2, "0x2": shots / 2}) else: # CX01.(I^H), Bell state targets.append({"00": shots / 2, "11": shots / 2}) # CX10.(I^H), Bell state targets.append({"00": shots / 2, "11": shots / 2}) + # test ctrl_states=0 + targets.append({"01": shots / 2, "10": shots / 2}) return targets