Skip to content

Commit

Permalink
Use more gates for AerState to call state_controller (#1643)
Browse files Browse the repository at this point in the history
`AerStatevector` wrongly assumed that methods of `AerState` support `mc*` instructions.
With this commit, `AerStatevector` use basic gates supported in specified method.

* use more gates for AerState to call state_controller
* take assertion for parameter checking to use IndexError and take debug codes
* add more tests for AerStatevector
* fix lint
* update aer_runtime to use cu that has four parameters
  • Loading branch information
hhorii authored Nov 30, 2022
1 parent eab7d89 commit bce3150
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 37 deletions.
15 changes: 7 additions & 8 deletions contrib/runtime/aer_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,31 @@ void aer_apply_z(void* handler, uint_t qubit) {
// Clifford gate: Hadamard
void aer_apply_h(void* handler, uint_t qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({qubit}, M_PI / 2.0, 0, M_PI);
state->apply_h(qubit);
};

// Clifford gate: sqrt(Z) or S gate
void aer_apply_s(void* handler, uint_t qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({qubit}, 0, 0, M_PI / 2.0);
state->apply_u(qubit, 0, 0, M_PI / 2.0);
};

// Clifford gate: inverse of sqrt(Z)
void aer_apply_sdg(void* handler, uint_t qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({qubit}, 0, 0, - M_PI / 2.0);
state->apply_u(qubit, 0, 0, - M_PI / 2.0);
};

// // sqrt(S) or T gate
void aer_apply_t(void* handler, uint_t qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({qubit}, 0, 0, M_PI / 4.0);
state->apply_u(qubit, 0, 0, M_PI / 4.0);
};

// inverse of sqrt(S)
void aer_apply_tdg(void* handler, uint_t qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({qubit}, 0, 0, - M_PI / 4.0);
state->apply_u({qubit}, 0, 0, - M_PI / 4.0);
};

// sqrt(NOT) gate
Expand Down Expand Up @@ -199,7 +199,7 @@ void aer_apply_crz(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double th
// controlled-H
void aer_apply_ch(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcu({ctrl_qubit, tgt_qubit}, M_PI / 2.0, 0, M_PI);
state->apply_mcu({ctrl_qubit, tgt_qubit}, M_PI / 2.0, 0, M_PI, 0);
};

// swap
Expand All @@ -223,7 +223,6 @@ void aer_apply_cswap(void* handler, uint_t ctrl_qubit, uint_t qubit0, uint_t qub
// four parameter controlled-U gate with relative phase γ
void aer_apply_cu(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double theta, double phi, double lambda, double gamma) {
AER::AerState* state = reinterpret_cast<AER::AerState*>(handler);
state->apply_mcphase({ctrl_qubit}, gamma);
state->apply_mcu({ctrl_qubit, tgt_qubit}, theta, phi, lambda);
state->apply_mcu({ctrl_qubit, tgt_qubit}, theta, phi, lambda, gamma);
};
}
9 changes: 9 additions & 0 deletions qiskit_aer/backends/wrappers/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,19 @@ PYBIND11_MODULE(controller_wrappers, m) {
});

aer_state.def("apply_diagonal", &AER::AerState::apply_diagonal_matrix);
aer_state.def("apply_x", &AER::AerState::apply_x);
aer_state.def("apply_cx", &AER::AerState::apply_cx);
aer_state.def("apply_mcx", &AER::AerState::apply_mcx);
aer_state.def("apply_y", &AER::AerState::apply_y);
aer_state.def("apply_cy", &AER::AerState::apply_cy);
aer_state.def("apply_mcy", &AER::AerState::apply_mcy);
aer_state.def("apply_z", &AER::AerState::apply_z);
aer_state.def("apply_cz", &AER::AerState::apply_cz);
aer_state.def("apply_mcz", &AER::AerState::apply_mcz);
aer_state.def("apply_mcphase", &AER::AerState::apply_mcphase);
aer_state.def("apply_h", &AER::AerState::apply_h);
aer_state.def("apply_u", &AER::AerState::apply_u);
aer_state.def("apply_cu", &AER::AerState::apply_cu);
aer_state.def("apply_mcu", &AER::AerState::apply_mcu);
aer_state.def("apply_mcswap", &AER::AerState::apply_mcswap);
aer_state.def("apply_measure", &AER::AerState::apply_measure);
Expand Down
71 changes: 69 additions & 2 deletions qiskit_aer/quantum_info/states/aer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,21 @@ def apply_diagonal(self, qubits, diag):
# update state
self._native_state.apply_diagonal(qubits, diag)

def apply_x(self, target_qubit):
"""apply a x operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_x(target_qubit)

def apply_cx(self, control_qubit, target_qubit):
"""apply a cx operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(control_qubit)
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_cx([control_qubit, target_qubit])

def apply_mcx(self, control_qubits, target_qubit):
"""apply a mcx operation."""
self._assert_allocated_or_mapped()
Expand All @@ -288,6 +303,21 @@ def apply_mcx(self, control_qubits, target_qubit):
# update state
self._native_state.apply_mcx(control_qubits + [target_qubit])

def apply_y(self, target_qubit):
"""apply a y operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_y(target_qubit)

def apply_cy(self, control_qubit, target_qubit):
"""apply a cy operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(control_qubit)
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_cy([control_qubit, target_qubit])

def apply_mcy(self, control_qubits, target_qubit):
"""apply a mcy operation."""
self._assert_allocated_or_mapped()
Expand All @@ -296,6 +326,21 @@ def apply_mcy(self, control_qubits, target_qubit):
# update state
self._native_state.apply_mcy(control_qubits + [target_qubit])

def apply_z(self, target_qubit):
"""apply a z operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_z(target_qubit)

def apply_cz(self, control_qubit, target_qubit):
"""apply a cz operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(control_qubit)
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_cz([control_qubit, target_qubit])

def apply_mcz(self, control_qubits, target_qubit):
"""apply a mcz operation."""
self._assert_allocated_or_mapped()
Expand All @@ -312,13 +357,35 @@ def apply_mcphase(self, control_qubits, target_qubit, phase):
# update state
self._native_state.apply_mcphase(control_qubits + [target_qubit], phase)

def apply_mcu(self, control_qubits, target_qubit, theta, phi, lamb):
def apply_h(self, target_qubit):
"""apply a h operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_h(target_qubit)

def apply_u(self, target_qubit, theta, phi, lamb):
"""apply a u operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_u(target_qubit, theta, phi, lamb)

def apply_cu(self, control_qubit, target_qubit, theta, phi, lamb, gamma):
"""apply a cu operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(control_qubit)
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_cu([control_qubit, target_qubit], theta, phi, lamb, gamma)

def apply_mcu(self, control_qubits, target_qubit, theta, phi, lamb, gamma):
"""apply a mcu operation."""
self._assert_allocated_or_mapped()
self._assert_in_allocated_qubits(control_qubits)
self._assert_in_allocated_qubits(target_qubit)
# update state
self._native_state.apply_mcu(control_qubits + [target_qubit], theta, phi, lamb)
self._native_state.apply_mcu(control_qubits + [target_qubit], theta, phi, lamb, gamma)

def apply_mcswap(self, control_qubits, qubit0, qubit1):
"""apply a mcswap operation."""
Expand Down
82 changes: 60 additions & 22 deletions qiskit_aer/quantum_info/states/aer_statevector.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit_aer import AerSimulator
from .aer_state import AerState
from ...backends.aerbackend import AerError
from ...backends.backend_utils import BASIS_GATES


class AerStatevector(Statevector):
Expand Down Expand Up @@ -152,6 +153,14 @@ def _from_instruction(inst, init_data, configs):
for config_key, config_value in configs.items():
aer_state.configure(config_key, config_value)

if 'method' in configs:
method = configs['method']
else:
method = 'statevector'
aer_state.configure('method', method)

basis_gates = BASIS_GATES[method]

aer_state.allocate_qubits(inst.num_qubits)
num_qubits = inst.num_qubits

Expand All @@ -164,14 +173,14 @@ def _from_instruction(inst, init_data, configs):
aer_state.apply_global_phase(inst.global_phase)

if isinstance(inst, QuantumCircuit):
AerStatevector._aer_evolve_circuit(aer_state, inst, range(num_qubits))
AerStatevector._aer_evolve_circuit(aer_state, inst, range(num_qubits), basis_gates)
else:
AerStatevector._aer_evolve_instruction(aer_state, inst, range(num_qubits))
AerStatevector._aer_evolve_instruction(aer_state, inst, range(num_qubits), basis_gates)

return aer_state.move_to_ndarray(), aer_state

@staticmethod
def _aer_evolve_circuit(aer_state, circuit, qubits):
def _aer_evolve_circuit(aer_state, circuit, qubits, basis_gates=None):
"""Apply circuit into aer_state"""
for instruction in circuit.data:
if instruction.clbits:
Expand All @@ -182,34 +191,63 @@ def _aer_evolve_circuit(aer_state, circuit, qubits):
qargs = instruction.qubits
AerStatevector._aer_evolve_instruction(aer_state, inst,
[qubits[circuit.find_bit(qarg).index]
for qarg in qargs])
for qarg in qargs], basis_gates)

@staticmethod
def _aer_evolve_instruction(aer_state, inst, qubits):
def _aer_evolve_instruction(aer_state, inst, qubits, basis_gates=None):
"""Apply instruction into aer_state"""

params = inst.params
if inst.name in ['u', 'u3']:
aer_state.apply_mcu(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1],
params[0], params[1], params[2])
elif inst.name in ['x', 'cx', 'ccx']:
aer_state.apply_mcx(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in ['y', 'cy']:
aer_state.apply_mcy(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in ['z', 'cz']:
aer_state.apply_mcz(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name == 'unitary':
aer_state.apply_unitary(qubits, inst.params[0])
elif inst.name == 'diagonal':
aer_state.apply_diagonal(qubits, inst.params[0])
applied = True

if basis_gates and inst.name in basis_gates:
if inst.name in ['u3', 'u']:
aer_state.apply_u(qubits[0], params[0], params[1], params[2])
elif inst.name == 'h':
aer_state.apply_h(qubits[0])
elif inst.name == 'x':
aer_state.apply_x(qubits[0])
elif inst.name == 'cx':
aer_state.apply_cx(qubits[0], qubits[1])
elif inst.name == 'y':
aer_state.apply_y(qubits[0])
elif inst.name == 'cy':
aer_state.apply_cy(qubits[0], qubits[1])
elif inst.name == 'z':
aer_state.apply_z(qubits[0])
elif inst.name == 'cz':
aer_state.apply_cz(qubits[0], qubits[1])
elif inst.name == 'unitary':
aer_state.apply_unitary(qubits, inst.params[0])
elif inst.name == 'diagonal':
aer_state.apply_diagonal(qubits, inst.params)
elif inst.name == 'cu':
aer_state.apply_cu(qubits[0], qubits[1], params[0], params[1], params[2], params[3])
elif inst.name == 'mcu':
aer_state.apply_mcu(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1],
params[0], params[1], params[2], params[3])
elif inst.name in 'mcx':
aer_state.apply_mcx(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in 'mcy':
aer_state.apply_mcy(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in 'mcz':
aer_state.apply_mcz(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name == 'id':
pass
else:
applied = False
elif inst.name == 'reset':
aer_state.apply_reset(qubits)
elif inst.name == 'barrier':
pass
else:
applied = False

if not applied:
definition = inst.definition
if definition is inst:
raise AerError('cannot decompose ' + inst.name)
if not definition:
if definition is inst or definition is None:
raise AerError('cannot decompose ' + inst.name)
AerStatevector._aer_evolve_circuit(aer_state, definition, qubits)
AerStatevector._aer_evolve_circuit(aer_state, definition, qubits, basis_gates)

@classmethod
def from_label(cls, label):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
issues:
- |
Fix two bugs in AerStatevector. AerStatevector uses mc* instructions, which are
not enabled in matrix_product_state method. This commit changes AerStatevector
not to use MC* and use H, X, Y, Z, U and CX. AerStatevector also failed if an
instruction is decomposed to empty QuantumCircuit. This commit allows such
instruction.
Loading

0 comments on commit bce3150

Please sign in to comment.