Skip to content
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

Add support for ECR gate to MPS #2137

Merged
merged 8 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions qiskit_aer/backends/aer_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,9 @@ def __init__(
backend_options=backend_options,
)

if "basis_gates" in backend_options.items():
self._check_basis_gates(backend_options["basis_gates"])

@classmethod
def _default_options(cls):
return Options(
Expand Down Expand Up @@ -898,11 +901,12 @@ def configuration(self):
config = copy.copy(self._configuration)
for key, val in self._options_configuration.items():
setattr(config, key, val)

method = getattr(self.options, "method", "automatic")

# Update basis gates based on custom options, config, method,
# and noise model
config.custom_instructions = self._CUSTOM_INSTR[
getattr(self.options, "method", "automatic")
]
config.custom_instructions = self._CUSTOM_INSTR[method]
config.basis_gates = self._cached_basis_gates + config.custom_instructions
return config

Expand Down Expand Up @@ -933,6 +937,9 @@ def set_option(self, key, value):
f" are: {self.available_methods()}"
)
self._set_method_config(value)
if key == "basis_gates":
self._check_basis_gates(value)

super().set_option(key, value)
if key in ["method", "noise_model", "basis_gates"]:
self._cached_basis_gates = self._basis_gates()
Expand Down Expand Up @@ -1047,3 +1054,11 @@ def _set_method_config(self, method=None):

self._set_configuration_option("description", description)
self._set_configuration_option("n_qubits", n_qubits)

def _check_basis_gates(self, basis_gates):
method = getattr(self.options, "method", "automatic")
# check if basis_gates contains non-supported gates
if method != "automatic":
for gate in basis_gates:
if gate not in self._BASIS_GATES[method]:
raise AerError(f"Invalid gate {gate} for simulation method {method}.")
3 changes: 3 additions & 0 deletions qiskit_aer/backends/backend_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
"cswap",
"diagonal",
"initialize",
"ecr",
]
),
"stabilizer": sorted(
Expand Down Expand Up @@ -245,6 +246,7 @@
"ccz",
"delay",
"pauli",
"ecr",
]
),
"unitary": sorted(
Expand Down Expand Up @@ -409,6 +411,7 @@
"delay",
"pauli",
"mcx_gray",
"ecr",
]
),
}
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/add_ecr_to_mps-0eec56596fc486c7.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
`matrix_product_state`, `extended_stabilizer` and `tensor_network` methods
now support ecr gate.
Add check if `basis_gates` backend option has unsupported gate for
the `method`
17 changes: 14 additions & 3 deletions src/simulators/extended_stabilizer/extended_stabilizer_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ const Operations::OpSet StateOpSet(
Operations::OpType::save_statevec,
}, // Operations::OpType::save_expval, Operations::OpType::save_expval_var},
// Gates
{"CX", "u0", "u1", "p", "cx", "cz", "swap", "id",
"x", "y", "z", "h", "s", "sdg", "sx", "sxdg",
"t", "tdg", "ccx", "ccz", "delay", "pauli"});
{"CX", "u0", "u1", "p", "cx", "cz", "swap", "id",
"x", "y", "z", "h", "s", "sdg", "sx", "sxdg",
"t", "tdg", "ccx", "ccz", "delay", "pauli", "ecr"});

using chpauli_t = CHSimulator::pauli_t;
using chstate_t = CHSimulator::Runner;
Expand Down Expand Up @@ -220,6 +220,7 @@ const stringmap_t<Gates> State::gateset_({
{"cx", Gates::cx}, // Controlled-X gate (CNOT)
{"cz", Gates::cz}, // Controlled-Z gate
{"swap", Gates::swap}, // SWAP gate
{"ecr", Gates::ecr}, // ECR Gate
// Three-qubit gates
{"ccx", Gates::ccx}, // Controlled-CX gate (Toffoli)
{"ccz", Gates::ccz}, // Constrolled-CZ gate (H3 Toff H3)
Expand Down Expand Up @@ -694,6 +695,16 @@ void State::apply_gate(const Operations::Op &op, RngEngine &rng, uint_t rank) {
case Gates::pauli:
apply_pauli(op.qubits, op.string_params[0], rank);
break;
case Gates::ecr:
BaseState::qreg_.apply_h(op.qubits[1], rank);
BaseState::qreg_.apply_s(op.qubits[0], rank);
BaseState::qreg_.apply_z(op.qubits[1], rank); // sdg(1)
BaseState::qreg_.apply_s(op.qubits[1], rank); // sdg(1)
BaseState::qreg_.apply_h(op.qubits[1], rank);
BaseState::qreg_.apply_cx(op.qubits[0], op.qubits[1], rank);
BaseState::qreg_.apply_x(op.qubits[0], rank);
BaseState::qreg_.apply_x(op.qubits[1], rank);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation differs to ecr by a -pi/4 global phase. is it generally ok ?

Copy link
Contributor

@enum-class enum-class May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am not mistaken this one doesn't have phase difference

qc.h(1)
qc.cx(0, 1)
qc.t(1)
qc.cx(0, 1)
qc.x(0)
qc.cx(0, 1)
qc.tdg(1)
qc.cx(0, 1)
qc.h(1)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I coped from stabilizer

case Gates::ecr:
BaseState::qreg_.append_h(op.qubits[1]);
BaseState::qreg_.append_s(op.qubits[0]);
BaseState::qreg_.append_z(op.qubits[1]); // sdg(1)
BaseState::qreg_.append_s(op.qubits[1]); // sdg(1)
BaseState::qreg_.append_h(op.qubits[1]);
BaseState::qreg_.append_cx(op.qubits[0], op.qubits[1]);
BaseState::qreg_.append_x(op.qubits[0]);
BaseState::qreg_.append_x(op.qubits[1]);

But it seems it was not correct, ad I get correct circuit by using transpiler

from qiskit import transpile
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
sim = AerSimulator(basis_gates = ["h", "cx", "x", "s", "z"])
qc = QuantumCircuit(2)
qc.ecr(0, 1)
qc = transpile(qc,backend=sim,optimization_level=0)
qc.draw('mpl')

image

I will fix by using this circuit

break;
default: // u0 or Identity
break;
}
Expand Down
4 changes: 3 additions & 1 deletion src/simulators/extended_stabilizer/gates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ enum class Gates {
swap,
ccx,
ccz,
pauli
pauli,
ecr,
};

enum class Gatetypes { pauli, clifford, non_clifford };
Expand All @@ -85,6 +86,7 @@ const AER::stringmap_t<Gatetypes> gate_types_ = {
{"cx", Gatetypes::clifford}, // Controlled-X gate (CNOT)
{"cz", Gatetypes::clifford}, // Controlled-Z gate
{"swap", Gatetypes::clifford}, // SWAP gate
{"ecr", Gatetypes::clifford}, // ECR Gate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I am a junior in the realm of Quantum, Just out of curiosity, Is ECR gate a clifford gate ?

// Three-qubit gates
{"ccx", Gatetypes::non_clifford}, // Controlled-CX gate (Toffoli)
{"ccz", Gatetypes::non_clifford}, // Controlled-CZ gate (H3 Toff H3)
Expand Down
12 changes: 8 additions & 4 deletions src/simulators/matrix_product_state/matrix_product_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ const Operations::OpSet StateOpSet(
OpType::jump,
OpType::mark},
// Gates
{"id", "x", "y", "z", "s", "sdg", "h", "t", "tdg", "p",
"u1", "u2", "u3", "u", "U", "CX", "cx", "cy", "cz", "cp",
"cu1", "swap", "ccx", "sx", "sxdg", "r", "rx", "ry", "rz", "rxx",
"ryy", "rzz", "rzx", "csx", "delay", "cswap", "pauli"});
{"id", "x", "y", "z", "s", "sdg", "h", "t", "tdg", "p",
"u1", "u2", "u3", "u", "U", "CX", "cx", "cy", "cz", "cp",
"cu1", "swap", "ccx", "sx", "sxdg", "r", "rx", "ry", "rz", "rxx",
"ryy", "rzz", "rzx", "csx", "delay", "cswap", "pauli", "ecr"});

//=========================================================================
// Matrix Product State subclass
Expand Down Expand Up @@ -296,6 +296,7 @@ const stringmap_t<Gates>
{"ryy", Gates::ryy}, // Pauli-YY rotation gate
{"rzz", Gates::rzz}, // Pauli-ZZ rotation gate
{"rzx", Gates::rzx}, // Pauli-ZX rotation gate
{"ecr", Gates::ecr}, // ECR Gate
/* Three-qubit gates */
{"ccx", Gates::ccx}, // Controlled-CX gate (Toffoli)
{"cswap", Gates::cswap},
Expand Down Expand Up @@ -673,6 +674,9 @@ void State::apply_gate(const Operations::Op &op) {
case Gates::pauli:
apply_pauli(op.qubits, op.string_params[0]);
break;
case Gates::ecr:
qreg_.apply_matrix(op.qubits, Linalg::Matrix::ECR);
break;
default:
// We shouldn't reach here unless there is a bug in gateset
throw std::invalid_argument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ enum Gates {
csx, // two qubit
ccx,
cswap, // three qubit
pauli
pauli,
ecr,
};

// enum class Direction {RIGHT, LEFT};
Expand Down
8 changes: 2 additions & 6 deletions test/terra/backends/aer_simulator/test_cliffords.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
"tensor_network",
]

SUPPORTED_ECR_METHODS = [
"stabilizer",
]


@ddt
class TestCliffords(SimulatorTestCase):
Expand Down Expand Up @@ -249,11 +245,11 @@ def test_pauli_gate_deterministic(self, method, device):
# ---------------------------------------------------------------------
# Test ecr gate
# ---------------------------------------------------------------------
@supported_methods(SUPPORTED_ECR_METHODS)
@supported_methods(SUPPORTED_METHODS)
def test_ecr_gate_nondeterministic(self, method, device):
"""Test ecr gate circuits"""
backend = self.backend(method=method, device=device, seed_simulator=self.SEED)
shots = 100
shots = 1000
circuits = ref_2q_clifford.ecr_gate_circuits_nondeterministic(final_measure=True)
targets = ref_2q_clifford.ecr_gate_counts_nondeterministic(shots)
result = backend.run(circuits, shots=shots).result()
Expand Down
Loading