Skip to content

Commit

Permalink
enable c_if for reset and raise exception if instruction does not s…
Browse files Browse the repository at this point in the history
…upport `c_if` (Qiskit#1868)

* enable c_if for reset and raise exception if instruction does not support c_if

* add reno

* use correct conditional_reg for reset

* support c_if for diagonal
  • Loading branch information
hhorii authored Oct 10, 2023
1 parent 92e16f7 commit 8818d6b
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 10 deletions.
24 changes: 21 additions & 3 deletions qiskit_aer/backends/aer_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,12 @@ def assemble_circuit(circuit: QuantumCircuit):
return aer_circ, index_map


def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, conditional_reg):
def _check_no_conditional(inst_name, conditional_reg):
if conditional_reg >= 0:
raise AerError(f"instruction {inst_name} does not support conditional")


def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, conditional_reg=-1):
operation = inst.operation
qubits = [qubit_indices[qubit] for qubit in inst.qubits]
clbits = [clbit_indices[clbit] for clbit in inst.clbits]
Expand Down Expand Up @@ -663,16 +668,18 @@ def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, c
else:
aer_circ.measure(qubits, clbits, [])
elif name == "reset":
aer_circ.reset(qubits)
aer_circ.reset(qubits, conditional_reg)
elif name == "diagonal":
aer_circ.diagonal(qubits, params, label if label else "diagonal")
aer_circ.diagonal(qubits, params, conditional_reg, label if label else "diagonal")
elif name == "unitary":
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":
_check_no_conditional(name, conditional_reg)
aer_circ.initialize(qubits, params)
elif name == "roerror":
_check_no_conditional(name, conditional_reg)
aer_circ.roerror(qubits, params)
elif name == "multiplexer":
aer_circ.multiplexer(qubits, params, conditional_reg, label if label else name)
Expand All @@ -691,10 +698,13 @@ def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, c
"save_state",
"save_stabilizer",
}:
_check_no_conditional(name, conditional_reg)
aer_circ.save_state(qubits, name, operation._subtype, label if label else name)
elif name in {"save_amplitudes", "save_amplitudes_sq"}:
_check_no_conditional(name, conditional_reg)
aer_circ.save_amplitudes(qubits, name, params, operation._subtype, label if label else name)
elif name in ("save_expval", "save_expval_var"):
_check_no_conditional(name, conditional_reg)
paulis = []
coeff_reals = []
coeff_imags = []
Expand All @@ -712,24 +722,32 @@ def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, c
label if label else name,
)
elif name == "set_statevector":
_check_no_conditional(name, conditional_reg)
aer_circ.set_statevector(qubits, params)
elif name == "set_unitary":
_check_no_conditional(name, conditional_reg)
aer_circ.set_unitary(qubits, params)
elif name == "set_density_matrix":
_check_no_conditional(name, conditional_reg)
aer_circ.set_density_matrix(qubits, params)
elif name == "set_stabilizer":
_check_no_conditional(name, conditional_reg)
aer_circ.set_clifford(qubits, params)
elif name == "set_superop":
_check_no_conditional(name, conditional_reg)
aer_circ.set_superop(qubits, params)
elif name == "set_matrix_product_state":
_check_no_conditional(name, conditional_reg)
aer_circ.set_matrix_product_state(qubits, params)
elif name == "superop":
aer_circ.superop(qubits, params[0], conditional_reg)
elif name == "barrier":
_check_no_conditional(name, conditional_reg)
num_of_aer_ops = 0
elif name == "jump":
aer_circ.jump(qubits, params, conditional_reg)
elif name == "mark":
_check_no_conditional(name, conditional_reg)
aer_circ.mark(qubits, params)
elif name == "qerror_loc":
aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg)
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/support_c_if_reset-1f0b8e84948fb3fc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
``c_if`` is supportted of ``reset`` instruction and an exception
is thrown if `c_if` is called for an instruction that does not
support ``c_if``.
8 changes: 4 additions & 4 deletions src/framework/circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class Circuit {
}

void diagonal(const reg_t &qubits, const cvector_t &vec,
const std::string &label) {
ops.push_back(Operations::make_diagonal(qubits, vec, label));
const int_t cond_regidx = -1, const std::string label = "") {
ops.push_back(Operations::make_diagonal(qubits, vec, cond_regidx, label));
}

void unitary(const reg_t &qubits, const cmatrix_t &mat,
Expand Down Expand Up @@ -259,8 +259,8 @@ class Circuit {
ops.push_back(Operations::make_measure(qubits, memory, registers));
}

void reset(const reg_t &qubits) {
ops.push_back(Operations::make_reset(qubits));
void reset(const reg_t &qubits, const int_t cond_regidx = -1) {
ops.push_back(Operations::make_reset(qubits, cond_regidx));
}

private:
Expand Down
20 changes: 19 additions & 1 deletion src/framework/operations.hpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -468,27 +468,39 @@ inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat,
}

inline Op make_diagonal(const reg_t &qubits, const cvector_t &vec,
const int_t conditional = -1,
const std::string label = "") {
Op op;
op.type = OpType::diagonal_matrix;
op.name = "diagonal";
op.qubits = qubits;
op.params = vec;

if (conditional >= 0) {
op.conditional = true;
op.conditional_reg = conditional;
}

if (label != "")
op.string_params = {label};

return op;
}

inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec,
const int_t conditional = -1,
const std::string label = "") {
Op op;
op.type = OpType::diagonal_matrix;
op.name = "diagonal";
op.qubits = qubits;
op.params = std::move(vec);

if (conditional >= 0) {
op.conditional = true;
op.conditional_reg = conditional;
}

if (label != "")
op.string_params = {label};

Expand Down Expand Up @@ -658,11 +670,17 @@ inline Op make_u3(uint_t qubit, T theta, T phi, T lam) {
return op;
}

inline Op make_reset(const reg_t &qubits, uint_t state = 0) {
inline Op make_reset(const reg_t &qubits, const int_t conditional) {
Op op;
op.type = OpType::reset;
op.name = "reset";
op.qubits = qubits;

if (conditional >= 0) {
op.conditional = true;
op.conditional_reg = conditional;
}

return op;
}

Expand Down
4 changes: 2 additions & 2 deletions src/transpile/fusion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class FusionMethod {
for (size_t i = 0; i < vec.size(); ++i)
vec[i] = fusioned_op.mats[0](i, i);
fusioned_op = Operations::make_diagonal(
fusioned_op.qubits, std::move(vec), std::string("fusion"));
fusioned_op.qubits, std::move(vec), -1, std::string("fusion"));
}
} else {
// loop for runtime parameter binding
Expand All @@ -83,7 +83,7 @@ class FusionMethod {
vec.assign((1UL << new_op.qubits.size()), 0);
for (size_t i = 0; i < vec.size(); ++i)
vec[i] = new_op.mats[0](i, i);
new_op = Operations::make_diagonal(new_op.qubits, std::move(vec),
new_op = Operations::make_diagonal(new_op.qubits, std::move(vec), -1,
std::string("fusion"));
}

Expand Down
70 changes: 70 additions & 0 deletions test/terra/backends/aer_simulator/test_conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from test.terra.reference import ref_conditionals
from test.terra.backends.simulator_test_case import SimulatorTestCase, supported_methods

from qiskit import QuantumCircuit


@ddt
class TestConditionalGates(SimulatorTestCase):
Expand Down Expand Up @@ -310,3 +312,71 @@ def test_conditional_superop_132bit(self, method, device):
result = backend.run(circuits, shots=shots).result()
self.assertSuccess(result)
self.compare_counts(result, circuits, targets, hex_counts=False, delta=0)


@ddt
class TestConditionalReset(SimulatorTestCase):
"""AerSimulator conditional reset tests."""

SUPPORTED_METHODS = [
"automatic",
"statevector",
"density_matrix",
"matrix_product_state",
"tensor_network",
]

# ---------------------------------------------------------------------
# Test conditional
# ---------------------------------------------------------------------
@supported_methods(SUPPORTED_METHODS)
def test_conditional_reset_1bit(self, method, device):
"""Test conditional reset on 1-bit conditional register."""
shots = 100
backend = self.backend(method=method, device=device)
backend.set_options(max_parallel_experiments=0)

circuits = ref_conditionals.conditional_circuits_1bit(
final_measure=True, conditional_type="reset"
)
targets = ref_conditionals.conditional_counts_1bit_with_reset(shots)
result = backend.run(circuits, shots=shots).result()
self.assertSuccess(result)
self.compare_counts(result, circuits, targets, delta=0)


@ddt
class TestConditionalDiagonal(SimulatorTestCase):
"""AerSimulator conditional diagonal tests."""

# ---------------------------------------------------------------------
# Test conditional
# ---------------------------------------------------------------------
def test_conditional_diagonal(self):
"""Test conditional diagonal with statevector."""
shots = 100
backend = self.backend(method="statevector", device="CPU")
backend.set_options(max_parallel_experiments=0)

circuit = QuantumCircuit(4, 4)
for i in range(1, 4):
circuit.h(i)
circuit.save_statevector(label="base")

circuit0 = QuantumCircuit(4, 4)
for i in range(1, 4):
circuit0.h(i)
circuit0.diagonal([-1, -1], [1]).c_if(circuit0.clbits[0], 0)
circuit0.save_statevector(label="diff")

circuit1 = QuantumCircuit(4, 4)
for i in range(1, 4):
circuit1.h(i)
circuit1.diagonal([-1, -1], [1]).c_if(circuit1.clbits[0], 1)
circuit1.save_statevector(label="equal")

result = backend.run([circuit, circuit0, circuit1], shots=1).result()
self.assertSuccess(result)

self.assertNotEqual(result.data(circuit)["base"], result.data(circuit0)["diff"])
self.assertEqual(result.data(circuit)["base"], result.data(circuit1)["equal"])
28 changes: 28 additions & 0 deletions test/terra/reference/ref_conditionals.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Test circuits and reference outputs for conditional gates.
"""

import math
import numpy as np
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.circuit import Instruction
Expand Down Expand Up @@ -44,6 +45,9 @@ def add_conditional_x(circuit, qreg, creg, val, conditional_type):
circuit.append(x_kraus, [qreg]).c_if(creg, val)
elif conditional_type == "superop":
circuit.append(x_superop, [qreg]).c_if(creg, val)
elif conditional_type == "reset":
circuit.x(qreg).c_if(creg, val)
circuit.reset(qreg).c_if(creg, val)
else:
circuit.x(qreg).c_if(creg, val)

Expand Down Expand Up @@ -133,6 +137,30 @@ def conditional_counts_1bit(shots, hex_counts=True):
return targets


def conditional_counts_1bit_with_reset(shots, hex_counts=True):
"""Conditional circuits reference counts."""
targets = []
if hex_counts:
# Conditional on 0 (cond = 0), "0 1" -> "0 0"
targets.append({"0x0": shots})
# Conditional on 0 (cond = 1), result "1 0" -> "1 0"
targets.append({"0x2": shots})
# Conditional on 1 (cond = 0), # result "0 0" -> "0 0"
targets.append({"0x0": shots})
# Conditional on 1 (cond = 1), # result "1 1" -> "1 0"
targets.append({"0x2": shots})
else:
# Conditional on 0 (cond = 0), "0 1" -> "0 0"
targets.append({"0 0": shots})
# Conditional on 0 (cond = 1), result "1 0" -> "1 0"
targets.append({"1 0": shots})
# Conditional on 1 (cond = 0), # result "0 0" -> "0 0"
targets.append({"0 0": shots})
# Conditional on 1 (cond = 1), # result "1 1" -> "1 0"
targets.append({"1 0": shots})
return targets


def conditional_statevector_1bit():
"""Conditional circuits reference statevector."""
targets = []
Expand Down

0 comments on commit 8818d6b

Please sign in to comment.