From 85831ee1bfb2480d335a7fdb43633a4b9244cc9d Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Thu, 26 Oct 2023 22:14:13 +0900 Subject: [PATCH 01/17] add skip Python 3.12 for GPU build (#1965) --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6c92ad004b..72e0be04de 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -148,7 +148,7 @@ jobs: env: CIBW_BEFORE_ALL: "yum install -y yum-utils wget && wget -q https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-rhel7-11-8-local-11.8.0_520.61.05-1.x86_64.rpm && rpm -i cuda-repo-rhel7-11-8-local-11.8.0_520.61.05-1.x86_64.rpm && yum clean all && yum -y install cuda && yum -y install openblas-devel && yum-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-rhel7.repo && yum clean all" CIBW_BEFORE_BUILD : "pip install nvidia-cuda-runtime-cu11 nvidia-cublas-cu11 nvidia-cusolver-cu11 nvidia-cusparse-cu11 cuquantum-cu11" - CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* *musllinux*" + CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* cp312* *musllinux*" CIBW_ENVIRONMENT: QISKIT_AER_PACKAGE_NAME=qiskit-aer-gpu-cu11 QISKIT_AER_CUDA_MAJOR=11 CMAKE_VERBOSE_MAKEFILE=true AER_THRUST_BACKEND=CUDA CUDACXX=/usr/local/cuda/bin/nvcc AER_CUDA_ARCH="7.0 7.2 7.5 8.0 8.6 8.7" AER_PYTHON_CUDA_ROOT=/opt/_internal AER_CIBUILD=true CIBW_REPAIR_WHEEL_COMMAND: 'auditwheel repair --exclude libcudart.so.11.0 --exclude libcustatevec.so.1 --exclude libcutensornet.so.2 --exclude libcutensor.so.1 --exclude libcutensorMg.so.1 --exclude libcusolver.so.11 --exclude libcusolverMg.so.11 --exclude libcusparse.so.11 --exclude libcublas.so.11 --exclude libcublasLt.so.11 -w {dest_dir} {wheel}' run: | @@ -192,7 +192,7 @@ jobs: env: CIBW_BEFORE_ALL: "yum install -y yum-utils wget && wget -q https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda-repo-rhel7-12-2-local-12.2.2_535.104.05-1.x86_64.rpm && rpm -i cuda-repo-rhel7-12-2-local-12.2.2_535.104.05-1.x86_64.rpm && yum clean all && yum -y install nvidia-driver-latest-dkms && yum -y install cuda && yum -y install openblas-devel && yum-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-rhel7.repo && yum clean all" CIBW_BEFORE_BUILD : "pip install nvidia-cuda-runtime-cu12 nvidia-nvjitlink-cu12 nvidia-cublas-cu12 nvidia-cusolver-cu12 nvidia-cusparse-cu12 cuquantum-cu12" - CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* *musllinux*" + CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* cp312* *musllinux*" CIBW_ENVIRONMENT: QISKIT_AER_PACKAGE_NAME=qiskit-aer-gpu QISKIT_AER_CUDA_MAJOR=12 CMAKE_VERBOSE_MAKEFILE=true AER_THRUST_BACKEND=CUDA CUDACXX=/usr/local/cuda/bin/nvcc AER_CUDA_ARCH="7.0 7.2 7.5 8.0 8.6 8.7 9.0" AER_PYTHON_CUDA_ROOT=/opt/_internal AER_CIBUILD=true CIBW_REPAIR_WHEEL_COMMAND: 'auditwheel repair --exclude libcudart.so.12 --exclude libcustatevec.so.1 --exclude libcutensornet.so.2 --exclude libcutensor.so.1 --exclude libcutensorMg.so.1 --exclude libcusolver.so.11 --exclude libcusolverMg.so.11 --exclude libcusolver.so.12 --exclude libcusolverMg.so.12 --exclude libcusparse.so.12 --exclude libcublas.so.12 --exclude libcublasLt.so.12 --exclude libnvJitLink.so.12 -w {dest_dir} {wheel}' run: | From 314c1ff251001dd1d65349e4b2ea27cb26fcaadc Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Tue, 31 Oct 2023 22:04:15 +0900 Subject: [PATCH 02/17] Fix basis gates of Aer backends (#1976) * move reset and switch_case ops to custom istr * Fix reset in AerStatevector * add test case * format * fix installing built Aer in some test cases --- .github/workflows/tests.yml | 4 ++-- qiskit_aer/backends/aer_simulator.py | 13 +++++++++++++ qiskit_aer/backends/backend_utils.py | 12 ------------ qiskit_aer/backends/qasm_simulator.py | 2 +- qiskit_aer/backends/statevector_simulator.py | 2 +- qiskit_aer/backends/unitary_simulator.py | 3 +-- qiskit_aer/quantum_info/states/aer_statevector.py | 4 ++-- .../notes/fix_basis_gates-5edf9708e3eec097.yaml | 5 +++++ test/terra/backends/aer_simulator/test_noise.py | 15 +++++++++++++++ 9 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/fix_basis_gates-5edf9708e3eec097.yaml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f25e8ea0c..aa7b3903d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -176,7 +176,7 @@ jobs: run: | set -e python -I -m build --wheel --config-setting=--build-option=-- --config-setting=--build-option=-DTEST_JSON=1 - pip install --find-links=dist qiskit-aer + pip install -U dist/*.whl - name: Run Tests run: | set -e @@ -267,7 +267,7 @@ jobs: run: | set -e python -I -m build --wheel - pip install --find-links=dist qiskit-aer + pip install -U dist/*.whl shell: bash - name: Run Tests env: diff --git a/qiskit_aer/backends/aer_simulator.py b/qiskit_aer/backends/aer_simulator.py index 0f6e3758b0..2ede8e3bbb 100644 --- a/qiskit_aer/backends/aer_simulator.py +++ b/qiskit_aer/backends/aer_simulator.py @@ -516,6 +516,8 @@ class AerSimulator(AerBackend): "while_loop", "break_loop", "continue_loop", + "reset", + "switch_case", ] ), "density_matrix": sorted( @@ -538,6 +540,8 @@ class AerSimulator(AerBackend): "while_loop", "break_loop", "continue_loop", + "reset", + "switch_case", ] ), "matrix_product_state": sorted( @@ -562,6 +566,8 @@ class AerSimulator(AerBackend): "while_loop", "break_loop", "continue_loop", + "reset", + "switch_case", ] ), "stabilizer": sorted( @@ -583,6 +589,8 @@ class AerSimulator(AerBackend): "while_loop", "break_loop", "continue_loop", + "reset", + "switch_case", ] ), "extended_stabilizer": sorted( @@ -591,6 +599,7 @@ class AerSimulator(AerBackend): "qerror_loc", "roerror", "save_statevector", + "reset", ] ), "unitary": sorted( @@ -598,6 +607,7 @@ class AerSimulator(AerBackend): "save_state", "save_unitary", "set_unitary", + "reset", ] ), "superop": sorted( @@ -609,6 +619,7 @@ class AerSimulator(AerBackend): "save_state", "save_superop", "set_superop", + "reset", ] ), "tensor_network": sorted( @@ -630,6 +641,8 @@ class AerSimulator(AerBackend): "save_statevector_dict", "set_statevector", "set_density_matrix", + "reset", + "switch_case", ] ), } diff --git a/qiskit_aer/backends/backend_utils.py b/qiskit_aer/backends/backend_utils.py index d7b3e92c0b..d472b924f4 100644 --- a/qiskit_aer/backends/backend_utils.py +++ b/qiskit_aer/backends/backend_utils.py @@ -109,8 +109,6 @@ "pauli", "mcx_gray", "ecr", - "reset", - "switch_case", ] ), "density_matrix": sorted( @@ -151,8 +149,6 @@ "delay", "pauli", "ecr", - "reset", - "switch_case", ] ), "matrix_product_state": sorted( @@ -195,8 +191,6 @@ "cswap", "diagonal", "initialize", - "reset", - "switch_case", ] ), "stabilizer": sorted( @@ -216,12 +210,10 @@ "swap", "delay", "pauli", - "reset", "ecr", "rx", "ry", "rz", - "switch_case", ] ), "extended_stabilizer": sorted( @@ -247,7 +239,6 @@ "ccz", "delay", "pauli", - "reset", ] ), "unitary": sorted( @@ -309,7 +300,6 @@ "delay", "pauli", "ecr", - "reset", ] ), "superop": sorted( @@ -349,7 +339,6 @@ "diagonal", "delay", "pauli", - "reset", ] ), "tensor_network": sorted( @@ -412,7 +401,6 @@ "delay", "pauli", "mcx_gray", - "reset", ] ), } diff --git a/qiskit_aer/backends/qasm_simulator.py b/qiskit_aer/backends/qasm_simulator.py index 9e0bc8a7c9..d73938a6b9 100644 --- a/qiskit_aer/backends/qasm_simulator.py +++ b/qiskit_aer/backends/qasm_simulator.py @@ -365,7 +365,6 @@ class QasmSimulator(AerBackend): "pauli", "mcx_gray", "ecr", - "reset", ] ) @@ -389,6 +388,7 @@ class QasmSimulator(AerBackend): "set_statevector", "set_density_matrix", "set_stabilizer", + "reset", ] ) diff --git a/qiskit_aer/backends/statevector_simulator.py b/qiskit_aer/backends/statevector_simulator.py index bd288084ce..342997c49f 100644 --- a/qiskit_aer/backends/statevector_simulator.py +++ b/qiskit_aer/backends/statevector_simulator.py @@ -213,7 +213,6 @@ class StatevectorSimulator(AerBackend): "initialize", "delay", "pauli", - "reset", ] ), "custom_instructions": sorted( @@ -231,6 +230,7 @@ class StatevectorSimulator(AerBackend): "save_amplitudes_sq", "save_state", "set_statevector", + "reset", ] ), "gates": [], diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index 069704cfd2..ca28204a9a 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -216,10 +216,9 @@ class UnitarySimulator(AerBackend): "multiplexer", "delay", "pauli", - "reset", ] ), - "custom_instructions": sorted(["save_unitary", "save_state", "set_unitary"]), + "custom_instructions": sorted(["save_unitary", "save_state", "set_unitary", "reset"]), "gates": [], } diff --git a/qiskit_aer/quantum_info/states/aer_statevector.py b/qiskit_aer/quantum_info/states/aer_statevector.py index 350d95bb5e..eac02af221 100644 --- a/qiskit_aer/quantum_info/states/aer_statevector.py +++ b/qiskit_aer/quantum_info/states/aer_statevector.py @@ -258,10 +258,10 @@ def _aer_evolve_instruction(aer_state, inst, qubits, basis_gates=None): aer_state.apply_mcz(qubits[0 : len(qubits) - 1], qubits[len(qubits) - 1]) elif inst.name == "id": pass - elif inst.name == "reset": - aer_state.apply_reset(qubits) else: applied = False + elif inst.name == "reset": + aer_state.apply_reset(qubits) elif inst.name == "kraus": aer_state.apply_kraus(qubits, inst.params) elif inst.name == "barrier": diff --git a/releasenotes/notes/fix_basis_gates-5edf9708e3eec097.yaml b/releasenotes/notes/fix_basis_gates-5edf9708e3eec097.yaml new file mode 100644 index 0000000000..6fa974b2e8 --- /dev/null +++ b/releasenotes/notes/fix_basis_gates-5edf9708e3eec097.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed basis gates sets of Aer backend, moved `reset` and `switch_case` + to custom instructions. diff --git a/test/terra/backends/aer_simulator/test_noise.py b/test/terra/backends/aer_simulator/test_noise.py index 9255bc8868..5e4a4c579c 100644 --- a/test/terra/backends/aer_simulator/test_noise.py +++ b/test/terra/backends/aer_simulator/test_noise.py @@ -15,6 +15,7 @@ from ddt import ddt from qiskit_aer import noise +import numpy as np import qiskit.quantum_info as qi from qiskit import transpile @@ -69,6 +70,20 @@ def test_readout_noise(self, method, device): self.assertSuccess(result) self.compare_counts(result, [circuit], [target], delta=0.05 * shots) + @supported_methods(ALL_METHODS) + def test_readout_noise_without_basis_gates(self, method, device): + """Test simulation with classical readout error noise model w/o basis gates.""" + backend = self.backend(method=method, device=device) + noise_model = noise.NoiseModel() + noise_model.add_readout_error(np.array([[0.9, 0.1], [0.1, 0.9]]), [0]) + backend.set_options(noise_model=noise_model) + circ = QuantumCircuit(1, 1) + circ.reset(0) + circ.measure(0, 0) + circ = transpile(circ, backend) + result = backend.run(circ, shots=1).result() + self.assertSuccess(result) + @supported_methods(ALL_METHODS) def test_pauli_gate_noise(self, method, device): """Test simulation with Pauli gate error noise model.""" From e31ff65946c6214a89799acd2c38259f45dbeb9c Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Wed, 1 Nov 2023 18:39:44 +0900 Subject: [PATCH 03/17] Applying global phase multiplication to initialize operation (#1980) * Applying global phase to initialize operation * fix format * remove recursive, add omp --- ...ze_with_global_phase-56d529cd9c09c2fa.yaml | 7 +++++ src/simulators/circuit_executor.hpp | 6 ++++ .../matrix_product_state.hpp | 17 +++++++++- src/simulators/state.hpp | 2 ++ .../statevector/statevector_executor.hpp | 31 +++++++++++++++++-- .../statevector/statevector_state.hpp | 14 ++++++++- .../tensor_network/tensor_net_executor.hpp | 16 +++++++++- .../tensor_network/tensor_net_state.hpp | 13 +++++++- .../backends/aer_simulator/test_initialize.py | 12 +++++++ 9 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/fix_initialize_with_global_phase-56d529cd9c09c2fa.yaml diff --git a/releasenotes/notes/fix_initialize_with_global_phase-56d529cd9c09c2fa.yaml b/releasenotes/notes/fix_initialize_with_global_phase-56d529cd9c09c2fa.yaml new file mode 100644 index 0000000000..565a5a0174 --- /dev/null +++ b/releasenotes/notes/fix_initialize_with_global_phase-56d529cd9c09c2fa.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + When applying `initialize`, global phase was not multiplied to state. + This fix multiplies global phase to `initialize` operation. + Also this fix applies global phase to `matrix_product_state` method, + which did not use global phase. diff --git a/src/simulators/circuit_executor.hpp b/src/simulators/circuit_executor.hpp index b5ea6193a0..02f00224a6 100644 --- a/src/simulators/circuit_executor.hpp +++ b/src/simulators/circuit_executor.hpp @@ -101,6 +101,9 @@ class Executor : public Base { int parallel_shots_; int parallel_state_update_; + // OpenMP qubit threshold + int omp_qubit_threshold_ = 14; + // results are stored independently in each process if true bool accept_distributed_results_ = true; @@ -262,6 +265,9 @@ void Executor::set_config(const Config &config) { max_parallel_threads_ = (max_parallel_threads_ > 0) ? std::min(max_parallel_threads_, omp_threads) : std::max(1, omp_threads); + + // Set OMP threshold for state update functions + omp_qubit_threshold_ = config.statevector_parallel_threshold; #else // No OpenMP so we disable parallelization max_parallel_threads_ = 1; diff --git a/src/simulators/matrix_product_state/matrix_product_state.hpp b/src/simulators/matrix_product_state/matrix_product_state.hpp index 1c29c9bd02..68f79f1f99 100644 --- a/src/simulators/matrix_product_state/matrix_product_state.hpp +++ b/src/simulators/matrix_product_state/matrix_product_state.hpp @@ -312,6 +312,10 @@ const stringmap_t void State::initialize_qreg(uint_t num_qubits = 0) { qreg_.initialize(num_qubits); + if (BaseState::has_global_phase_) { + BaseState::qreg_.apply_diagonal_matrix( + {0}, {BaseState::global_phase_, BaseState::global_phase_}); + } } void State::initialize_omp() { @@ -721,7 +725,18 @@ void State::apply_kraus(const reg_t &qubits, void State::apply_initialize(const reg_t &qubits, const cvector_t ¶ms, RngEngine &rng) { - qreg_.apply_initialize(qubits, params, rng); + // apply global phase here + if (BaseState::has_global_phase_) { + cvector_t tmp(params.size()); + auto apply_global_phase = [&tmp, params, this](int_t i) { + tmp[i] = params[i] * BaseState::global_phase_; + }; + Utils::apply_omp_parallel_for((qubits.size() > 14), 0, params.size(), + apply_global_phase, BaseState::threads_); + qreg_.apply_initialize(qubits, tmp, rng); + } else { + qreg_.apply_initialize(qubits, params, rng); + } } void State::apply_measure(const reg_t &qubits, const reg_t &cmemory, diff --git a/src/simulators/state.hpp b/src/simulators/state.hpp index d0cd4baac0..ee5613328a 100644 --- a/src/simulators/state.hpp +++ b/src/simulators/state.hpp @@ -207,6 +207,8 @@ class Base { // Set a complex global phase value exp(1j * theta) for the state void set_global_phase(double theta); + bool has_global_phase() { return has_global_phase_; } + complex_t global_phase() { return global_phase_; } // Set a complex global phase value exp(1j * theta) for the state void add_global_phase(double theta); diff --git a/src/simulators/statevector/statevector_executor.hpp b/src/simulators/statevector/statevector_executor.hpp index 6cd6877211..27cdf4a3ae 100644 --- a/src/simulators/statevector/statevector_executor.hpp +++ b/src/simulators/statevector/statevector_executor.hpp @@ -1259,10 +1259,23 @@ std::vector Executor::sample_measure(const reg_t &qubits, template void Executor::apply_initialize(const reg_t &qubits, - const cvector_t ¶ms, + const cvector_t ¶ms_in, RngEngine &rng) { auto sorted_qubits = qubits; std::sort(sorted_qubits.begin(), sorted_qubits.end()); + // apply global phase here + cvector_t tmp; + if (Base::states_[0].has_global_phase()) { + tmp.resize(params_in.size()); + std::complex global_phase = Base::states_[0].global_phase(); + auto apply_global_phase = [&tmp, ¶ms_in, global_phase](int_t i) { + tmp[i] = params_in[i] * global_phase; + }; + Utils::apply_omp_parallel_for((qubits.size() > Base::omp_qubit_threshold_), + 0, params_in.size(), apply_global_phase, + Base::parallel_state_update_); + } + const cvector_t ¶ms = tmp.empty() ? params_in : tmp; if (qubits.size() == Base::num_qubits_) { // If qubits is all ordered qubits in the statevector // we can just initialize the whole state directly @@ -1601,7 +1614,21 @@ void Executor::apply_reset(CircuitExecutor::Branch &root, template void Executor::apply_initialize(CircuitExecutor::Branch &root, const reg_t &qubits, - const cvector_t ¶ms) { + const cvector_t ¶ms_in) { + // apply global phase here + cvector_t tmp; + if (Base::states_[root.state_index()].has_global_phase()) { + tmp.resize(params_in.size()); + std::complex global_phase = + Base::states_[root.state_index()].global_phase(); + auto apply_global_phase = [&tmp, params_in, global_phase](int_t i) { + tmp[i] = params_in[i] * global_phase; + }; + Utils::apply_omp_parallel_for((qubits.size() > Base::omp_qubit_threshold_), + 0, params_in.size(), apply_global_phase, + Base::parallel_state_update_); + } + const cvector_t ¶ms = tmp.empty() ? params_in : tmp; if (qubits.size() == Base::num_qubits_) { auto sorted_qubits = qubits; std::sort(sorted_qubits.begin(), sorted_qubits.end()); diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 0922705ade..0cfbd2963c 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -1055,10 +1055,22 @@ std::vector State::sample_measure(const reg_t &qubits, template void State::apply_initialize(const reg_t &qubits, - const cvector_t ¶ms, + const cvector_t ¶ms_in, RngEngine &rng) { auto sorted_qubits = qubits; std::sort(sorted_qubits.begin(), sorted_qubits.end()); + // apply global phase here + cvector_t tmp; + if (BaseState::has_global_phase_) { + tmp.resize(params_in.size()); + auto apply_global_phase = [&tmp, ¶ms_in, this](int_t i) { + tmp[i] = params_in[i] * BaseState::global_phase_; + }; + Utils::apply_omp_parallel_for((qubits.size() > omp_qubit_threshold_), 0, + params_in.size(), apply_global_phase, + BaseState::threads_); + } + const cvector_t ¶ms = tmp.empty() ? params_in : tmp; if (qubits.size() == BaseState::qreg_.num_qubits()) { // If qubits is all ordered qubits in the statevector // we can just initialize the whole state directly diff --git a/src/simulators/tensor_network/tensor_net_executor.hpp b/src/simulators/tensor_network/tensor_net_executor.hpp index 102fb22c2f..971dcd02c9 100644 --- a/src/simulators/tensor_network/tensor_net_executor.hpp +++ b/src/simulators/tensor_network/tensor_net_executor.hpp @@ -249,7 +249,21 @@ void Executor::apply_reset(CircuitExecutor::Branch &root, template void Executor::apply_initialize(CircuitExecutor::Branch &root, const reg_t &qubits, - const cvector_t ¶ms) { + const cvector_t ¶ms_in) { + // apply global phase here + cvector_t tmp; + if (Base::states_[root.state_index()].has_global_phase()) { + tmp.resize(params_in.size()); + std::complex global_phase = + Base::states_[root.state_index()].global_phase(); + auto apply_global_phase = [&tmp, params_in, global_phase](int_t i) { + tmp[i] = params_in[i] * global_phase; + }; + Utils::apply_omp_parallel_for((qubits.size() > Base::omp_qubit_threshold_), + 0, params_in.size(), apply_global_phase, + Base::parallel_state_update_); + } + const cvector_t ¶ms = tmp.empty() ? params_in : tmp; if (qubits.size() == Base::num_qubits_) { auto sorted_qubits = qubits; std::sort(sorted_qubits.begin(), sorted_qubits.end()); diff --git a/src/simulators/tensor_network/tensor_net_state.hpp b/src/simulators/tensor_network/tensor_net_state.hpp index f302e8a470..7ac73ad22e 100644 --- a/src/simulators/tensor_network/tensor_net_state.hpp +++ b/src/simulators/tensor_network/tensor_net_state.hpp @@ -928,10 +928,21 @@ std::vector State::sample_measure(const reg_t &qubits, template void State::apply_initialize(const reg_t &qubits, - const cvector_t ¶ms, + const cvector_t ¶ms_in, RngEngine &rng) { auto sorted_qubits = qubits; std::sort(sorted_qubits.begin(), sorted_qubits.end()); + // apply global phase here + cvector_t tmp; + if (BaseState::has_global_phase_) { + tmp.resize(params_in.size()); + auto apply_global_phase = [&tmp, params_in, this](int_t i) { + tmp[i] = params_in[i] * BaseState::global_phase_; + }; + Utils::apply_omp_parallel_for((qubits.size() > 14), 0, params_in.size(), + apply_global_phase, BaseState::threads_); + } + const cvector_t ¶ms = tmp.empty() ? params_in : tmp; if (qubits.size() == BaseState::qreg_.num_qubits()) { // If qubits is all ordered qubits in the statevector // we can just initialize the whole state directly diff --git a/test/terra/backends/aer_simulator/test_initialize.py b/test/terra/backends/aer_simulator/test_initialize.py index 739a77f709..015c971c8d 100644 --- a/test/terra/backends/aer_simulator/test_initialize.py +++ b/test/terra/backends/aer_simulator/test_initialize.py @@ -224,3 +224,15 @@ def test_initialize_with_int_twice(self, method, device): actual = backend.run(circ).result().get_statevector(circ) self.assertAlmostEqual(actual[5], 1) + + @supported_methods(SUPPORTED_METHODS) + def test_initialize_with_global_phase(self, method, device): + """Test AerSimulator initialize with global phase""" + backend = self.backend(method=method, device=device) + circ = QuantumCircuit(2) + circ.global_phase = np.pi + circ.initialize([1, 0, 0, 0]) + circ.x(0) + circ.save_statevector() + actual = backend.run(circ).result().get_statevector(circ) + self.assertAlmostEqual(actual[1], -1) From 5c38827dc14f57f56132fd45db9e24f1c329ca0d Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Thu, 2 Nov 2023 17:30:34 +0900 Subject: [PATCH 04/17] Release 0.13.1 --- docs/conf.py | 4 ++-- qiskit_aer/VERSION.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e1c21f0b5a..da1adfa30f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -45,9 +45,9 @@ author = 'Qiskit Development Team' # The short X.Y version -version = '0.13.0' +version = '0.13.1' # The full version, including alpha/beta/rc tags -release = '0.13.0' +release = '0.13.1' templates_path = ['_templates'] diff --git a/qiskit_aer/VERSION.txt b/qiskit_aer/VERSION.txt index 54d1a4f2a4..c317a91891 100644 --- a/qiskit_aer/VERSION.txt +++ b/qiskit_aer/VERSION.txt @@ -1 +1 @@ -0.13.0 +0.13.1 From 5db25fdb2e8a439358463e948d677c43e82fcd73 Mon Sep 17 00:00:00 2001 From: Ikko Hamamura Date: Wed, 8 Nov 2023 16:16:12 +0900 Subject: [PATCH 05/17] Revert too many deprecations in Estimator (#1990) * Revert too much deprecation * fix typo * fix tests --- qiskit_aer/primitives/estimator.py | 12 +++--------- test/terra/primitives/test_estimator.py | 6 ++---- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/qiskit_aer/primitives/estimator.py b/qiskit_aer/primitives/estimator.py index 600a7203d7..df8f10d71c 100644 --- a/qiskit_aer/primitives/estimator.py +++ b/qiskit_aer/primitives/estimator.py @@ -39,7 +39,7 @@ Optimize1qGatesDecomposition, SetLayout, ) -from qiskit.utils import deprecate_arg, deprecate_func +from qiskit.utils import deprecate_func from .. import AerError, AerSimulator @@ -76,12 +76,6 @@ class Estimator(BaseEstimator): normal distribution approximation. """ - @deprecate_arg( - "approximation", - since=0.13, - package_name="qiskit-aer", - additional_msg="approximation=True will be default in the future.", - ) def __init__( self, *, @@ -118,7 +112,7 @@ def __init__( warn( "Option approximation=False is deprecated as of qiskit-aer 0.13. " "It will be removed no earlier than 3 months after the release date. " - "Instead, use BackendEstmator from qiskit.primitives.", + "Instead, use BackendEstimator from qiskit.primitives.", DeprecationWarning, stacklevel=3, ) @@ -153,7 +147,7 @@ def approximation(self, approximation): warn( "Option approximation=False is deprecated as of qiskit-aer 0.13. " "It will be removed no earlier than 3 months after the release date. " - "Instead, use BackendEstmator from qiskit.primitives.", + "Instead, use BackendEstimator from qiskit.primitives.", DeprecationWarning, stacklevel=3, ) diff --git a/test/terra/primitives/test_estimator.py b/test/terra/primitives/test_estimator.py index deb73f7162..d7aa89f437 100644 --- a/test/terra/primitives/test_estimator.py +++ b/test/terra/primitives/test_estimator.py @@ -287,8 +287,7 @@ def test_with_shots_option_with_approximation(self, abelian_grouping): """test with shots option.""" # Note: abelian_gropuing is ignored when approximation is True as documented. # The purpose of this test is to make sure the results remain the same. - with self.assertWarns(DeprecationWarning): - est = Estimator(approximation=True, abelian_grouping=abelian_grouping) + est = Estimator(approximation=True, abelian_grouping=abelian_grouping) result = est.run( self.ansatz, self.observable, parameter_values=[[0, 1, 1, 2, 3, 5]], shots=1024, seed=15 ).result() @@ -332,8 +331,7 @@ def test_result_order(self): qc2.ry(np.pi / 2 * param, 0) qc2.measure_all() - with self.assertWarns(DeprecationWarning): - estimator = Estimator(approximation=True) + estimator = Estimator(approximation=True) job = estimator.run([qc1, qc2, qc1, qc1, qc2], ["Z"] * 5, [[], [1], [], [], [1]]) result = job.result() np.testing.assert_allclose(result.values, [1, 0, 1, 1, 0], atol=1e-10) From b6defadb0e4b044be8a79a247491e4185fb934d8 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Wed, 8 Nov 2023 22:58:56 +0900 Subject: [PATCH 06/17] Change priority of method selection of noise simulation (#1989) * Avoid selecting stabilizer method when noise model contains rotational gates * remove checking noise opsets, change priority selecting density_matrix * format * modify test cases use auto method result may change by this PR * modify one more test case --- ...utomethod_stabilizer-90963b34bd5b4439.yaml | 9 ++++++ src/controllers/aer_controller.hpp | 10 +++---- .../aer_simulator/test_auto_method.py | 30 ++++++++++++++----- .../backends/aer_simulator/test_measure.py | 3 +- .../aer_simulator/test_thread_management.py | 13 ++++++-- 5 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 releasenotes/notes/fix_automethod_stabilizer-90963b34bd5b4439.yaml diff --git a/releasenotes/notes/fix_automethod_stabilizer-90963b34bd5b4439.yaml b/releasenotes/notes/fix_automethod_stabilizer-90963b34bd5b4439.yaml new file mode 100644 index 0000000000..92c8c46c03 --- /dev/null +++ b/releasenotes/notes/fix_automethod_stabilizer-90963b34bd5b4439.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed `stabilizer` was selected with `method="automatic" ` when simulating + circuits with rotational gates with noise models for small number of qubits + even it is faster to calculate with `density_matrix` method. + This fix checks if `density_matrix` method with noise model is faster or not + at first and then check using `stabilizer` method. + This is side effect of implementing rotational gates in stabilizer PR #1938 diff --git a/src/controllers/aer_controller.hpp b/src/controllers/aer_controller.hpp index d216b4ff9e..baa9d5d85d 100755 --- a/src/controllers/aer_controller.hpp +++ b/src/controllers/aer_controller.hpp @@ -833,22 +833,22 @@ Controller::simulation_methods(const Config &config, Method Controller::automatic_simulation_method( const Config &config, const Circuit &circ, const Noise::NoiseModel &noise_model) const { - // If circuit and noise model are Clifford run on Stabilizer simulator - if (validate_method(Method::stabilizer, config, circ, noise_model, false)) { - return Method::stabilizer; - } // For noisy simulations we enable the density matrix method if // shots > 2 ** num_qubits. This is based on a rough estimate that // a single shot of the density matrix simulator is approx 2 ** nq // times slower than a single shot of statevector due the increased // dimension - if (noise_model.has_quantum_errors() && circ.num_qubits < 64 && + if (noise_model.has_quantum_errors() && circ.num_qubits < 30 && circ.shots > (1ULL << circ.num_qubits) && validate_method(Method::density_matrix, config, circ, noise_model, false) && circ.can_sample) { return Method::density_matrix; } + // If circuit and noise model are Clifford run on Stabilizer simulator + if (validate_method(Method::stabilizer, config, circ, noise_model, false)) { + return Method::stabilizer; + } // If the special conditions for stabilizer or density matrix are // not satisfied we choose simulation method based on supported diff --git a/test/terra/backends/aer_simulator/test_auto_method.py b/test/terra/backends/aer_simulator/test_auto_method.py index 70fcb0d175..53adc9fb66 100644 --- a/test/terra/backends/aer_simulator/test_auto_method.py +++ b/test/terra/backends/aer_simulator/test_auto_method.py @@ -48,7 +48,7 @@ class TestSimulationMethod(SimulatorTestCase): # --------------------------------------------------------------------- def test_auto_method_clifford_circuits(self): - """Test statevector method is used for Clifford circuit""" + """Test stabilizer method is used for Clifford circuit""" # Test circuits backend = self.backend() shots = 100 @@ -59,7 +59,7 @@ def test_auto_method_clifford_circuits(self): self.compare_result_metadata(result, circuits, "method", "stabilizer") def test_auto_method_clifford_circuits_and_reset_noise(self): - """Test statevector method is used for Clifford circuit""" + """Test stabilizer method is used for Clifford circuit""" # Test noise model noise_circs = [Reset(), IGate()] noise_probs = [0.5, 0.5] @@ -69,7 +69,7 @@ def test_auto_method_clifford_circuits_and_reset_noise(self): backend = self.backend(noise_model=noise_model) # Test circuits - shots = 100 + shots = 4 circuits = ref_2q_clifford.cz_gate_circuits_deterministic(final_measure=True) result = backend.run(circuits, shots=shots).result() success = getattr(result, "success", False) @@ -77,7 +77,7 @@ def test_auto_method_clifford_circuits_and_reset_noise(self): self.compare_result_metadata(result, circuits, "method", "stabilizer") def test_auto_method_clifford_circuits_and_pauli_noise(self): - """Test statevector method is used for Clifford circuit""" + """Test stabilizer method is used for Clifford circuit""" # Noise Model error = pauli_error([["XX", 0.5], ["II", 0.5]]) noise_model = NoiseModel() @@ -85,15 +85,31 @@ def test_auto_method_clifford_circuits_and_pauli_noise(self): backend = self.backend(noise_model=noise_model) # Test circuits - shots = 100 + shots = 4 circuits = ref_2q_clifford.cz_gate_circuits_deterministic(final_measure=True) result = backend.run(circuits, shots=shots).result() success = getattr(result, "success", False) self.assertTrue(success) self.compare_result_metadata(result, circuits, "method", "stabilizer") + def test_auto_method_clifford_circuits_and_pauli_noise_with_many_shots(self): + """Test density_matrix method is used for Clifford circuit""" + # Noise Model + error = pauli_error([["XX", 0.5], ["II", 0.5]]) + noise_model = NoiseModel() + noise_model.add_all_qubit_quantum_error(error, ["cz", "cx"]) + backend = self.backend(noise_model=noise_model) + + # Test circuits + shots = 1000 + circuits = ref_2q_clifford.cz_gate_circuits_deterministic(final_measure=True) + result = backend.run(circuits, shots=shots).result() + success = getattr(result, "success", False) + self.assertTrue(success) + self.compare_result_metadata(result, circuits, "method", "density_matrix") + def test_auto_method_clifford_circuits_and_unitary_noise(self): - """Test statevector method is used for Clifford circuit""" + """Test density_matrix method is used for Clifford circuit""" # Noise Model error = mixed_unitary_error( [(Pauli("XX").to_matrix(), 0.5), (Pauli("II").to_matrix(), 0.5)] @@ -110,7 +126,7 @@ def test_auto_method_clifford_circuits_and_unitary_noise(self): self.compare_result_metadata(result, circuits, "method", "density_matrix") def test_auto_method_clifford_circuits_and_kraus_noise(self): - """Test statevector method is used for Clifford circuit""" + """Test density_matrix method is used for Clifford circuit""" # Noise Model error = amplitude_damping_error(0.5) noise_model = NoiseModel() diff --git a/test/terra/backends/aer_simulator/test_measure.py b/test/terra/backends/aer_simulator/test_measure.py index fd39d68042..aaf40b598e 100644 --- a/test/terra/backends/aer_simulator/test_measure.py +++ b/test/terra/backends/aer_simulator/test_measure.py @@ -142,7 +142,8 @@ def test_measure_sampling_with_quantum_noise(self, method, device): targets = ref_measure.measure_counts_deterministic(shots) result = backend.run(circuits, shots=shots).result() self.assertSuccess(result) - sampling = method == "density_matrix" or method == "tensor_network" + method_used = result.results[0].metadata.get("method") + sampling = method_used == "density_matrix" or method_used == "tensor_network" self.compare_result_metadata(result, circuits, "measure_sampling", sampling) # --------------------------------------------------------------------- diff --git a/test/terra/backends/aer_simulator/test_thread_management.py b/test/terra/backends/aer_simulator/test_thread_management.py index 003cb92372..60154e8c25 100644 --- a/test/terra/backends/aer_simulator/test_thread_management.py +++ b/test/terra/backends/aer_simulator/test_thread_management.py @@ -147,7 +147,9 @@ def test_parallel_defaults_single_ideal(self): def test_parallel_defaults_single_noise(self): """Test parallel thread assignment defaults""" backend = self.backend( - noise_model=self.dummy_noise_model(), **self.backend_options_parallel() + method="statevector", + noise_model=self.dummy_noise_model(), + **self.backend_options_parallel(), ) max_threads = self.available_threads() @@ -209,7 +211,9 @@ def test_parallel_defaults_multi_ideal(self): def test_parallel_defaults_multi_noise(self): """Test parallel thread assignment defaults""" backend = self.backend( - noise_model=self.dummy_noise_model(), **self.backend_options_parallel() + method="statevector", + noise_model=self.dummy_noise_model(), + **self.backend_options_parallel(), ) max_threads = self.available_threads() @@ -295,7 +299,9 @@ def test_parallel_thread_assignment(self, custom_max_threads): # Test single circuit, with noise # Parallel experiments should always be 1 # parallel shots should be greater than 1 - backend = self.backend(noise_model=self.dummy_noise_model(), **parallel_opts) + backend = self.backend( + method="statevector", noise_model=self.dummy_noise_model(), **parallel_opts + ) circuits = self.dummy_circuit(1) result = backend.run(circuits, shots=shots).result() for threads in self.threads_used(result): @@ -531,6 +537,7 @@ def test_parallel_shot_thread_multi_noise(self): max_threads = self.available_threads() backend = self.backend( + method="statevector", noise_model=self.dummy_noise_model(), **self.backend_options_parallel(shot_threads=max_threads), ) From 8ac3c20230e2b7a29d9cf0efea86c3514e6001ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:07:36 +0100 Subject: [PATCH 07/17] Remove use of opflow in Estimator (#1996) --- qiskit_aer/primitives/estimator.py | 5 ++--- ...remove-opflow-estimator-a3b64cfe8a4fd6b3.yaml | 8 ++++++++ test/terra/primitives/test_estimator.py | 16 +--------------- 3 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 releasenotes/notes/remove-opflow-estimator-a3b64cfe8a4fd6b3.yaml diff --git a/qiskit_aer/primitives/estimator.py b/qiskit_aer/primitives/estimator.py index df8f10d71c..14f5749469 100644 --- a/qiskit_aer/primitives/estimator.py +++ b/qiskit_aer/primitives/estimator.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -23,7 +23,6 @@ import numpy as np from qiskit.circuit import ParameterExpression, QuantumCircuit from qiskit.compiler import transpile -from qiskit.opflow import PauliSumOp from qiskit.primitives import BaseEstimator, EstimatorResult from qiskit.primitives.primitive_job import PrimitiveJob from qiskit.primitives.utils import _circuit_key, _observable_key, init_observable @@ -174,7 +173,7 @@ def _call( def _run( self, circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], + observables: Sequence[BaseOperator], parameter_values: Sequence[Sequence[float]], **run_options, ) -> PrimitiveJob: diff --git a/releasenotes/notes/remove-opflow-estimator-a3b64cfe8a4fd6b3.yaml b/releasenotes/notes/remove-opflow-estimator-a3b64cfe8a4fd6b3.yaml new file mode 100644 index 0000000000..f79a41724c --- /dev/null +++ b/releasenotes/notes/remove-opflow-estimator-a3b64cfe8a4fd6b3.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The :meth:`qiskit_aer.primitives.Estimator.run` method no longer supports + ``observables`` input arguments of type ``PauliSumOp``. The ``PauliSumOp`` + class was deprecated in Qiskit 0.44 and will be removed in Qiskit 1.0. + Alternative types that you can use instead of ``PauliSumOp`` are + :class:`qiskit.quantum_info.SparsePauliOp` or :class:`qiskit.quantum_info.Pauli`. diff --git a/test/terra/primitives/test_estimator.py b/test/terra/primitives/test_estimator.py index d7aa89f437..dfd658e05c 100644 --- a/test/terra/primitives/test_estimator.py +++ b/test/terra/primitives/test_estimator.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -23,7 +23,6 @@ from qiskit.circuit import Parameter, QuantumCircuit from qiskit.circuit.library import RealAmplitudes from qiskit.exceptions import QiskitError -from qiskit.opflow import PauliSumOp from qiskit.primitives import EstimatorResult from qiskit.quantum_info import Operator, SparsePauliOp @@ -53,19 +52,6 @@ def setUp(self): def test_estimator(self, abelian_grouping): """test for a simple use case""" lst = [("XX", 1), ("YY", 2), ("ZZ", 3)] - with self.assertWarns(DeprecationWarning): - with self.subTest("PauliSumOp"): - observable = PauliSumOp.from_list(lst) - ansatz = RealAmplitudes(num_qubits=2, reps=2) - est = Estimator( - backend_options={"method": "statevector"}, abelian_grouping=abelian_grouping - ) - result = est.run( - ansatz, observable, parameter_values=[[0, 1, 1, 2, 3, 5]], seed=15 - ).result() - self.assertIsInstance(result, EstimatorResult) - np.testing.assert_allclose(result.values, [1.728515625]) - with self.subTest("SparsePauliOp"): observable = SparsePauliOp.from_list(lst) ansatz = RealAmplitudes(num_qubits=2, reps=2) From a07f76f41b99918d05631967832b9911e7cc1fc4 Mon Sep 17 00:00:00 2001 From: jon <70080228+notcruz@users.noreply.github.com> Date: Wed, 15 Nov 2023 20:55:14 -0500 Subject: [PATCH 08/17] Update misspelling apply_gate method doc (#1998) Co-authored-by: Jun Doi --- src/simulators/statevector/statevector_state.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 0cfbd2963c..00b1e1711e 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -173,7 +173,7 @@ class State : public QuantumState::State { // Apply instructions //----------------------------------------------------------------------- - // Applies a sypported Gate operation to the state class. + // Applies a supported Gate operation to the state class. // If the input is not in allowed_gates an exeption will be raised. void apply_gate(const Operations::Op &op); From 1c7d241e5eba1215c6652fd48e4196c0afe9b784 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 20 Nov 2023 18:52:57 +0900 Subject: [PATCH 09/17] Add optimization_level=0 to transpiler for compiling dynamic circuits (#2000) ``id`` gate was removed by transpiler called from aer_compiler without optimization_level for dynamic circuits. This commits adds ``optimization_level=0`` to avoid removing id gates --- qiskit_aer/backends/aer_compiler.py | 4 +++- ...ranspiler_lvl_for_dynamic_circuit-4ead3b497bbcc632.yaml | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix_transpiler_lvl_for_dynamic_circuit-4ead3b497bbcc632.yaml diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index d89eca1e87..1ab2bc4315 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -99,7 +99,9 @@ def compile(self, circuits, basis_gates=None, optypes=None): circuit = self._inline_initialize(circuit, compiled_optypes[idx]) if self._is_dynamic(circuit, compiled_optypes[idx]): compiled_circ = transpile( - self._inline_circuit(circuit, None, None), basis_gates=basis_gates + self._inline_circuit(circuit, None, None), + basis_gates=basis_gates, + optimization_level=0, ) compiled_circuits.append(compiled_circ) # Recompute optype for compiled circuit diff --git a/releasenotes/notes/fix_transpiler_lvl_for_dynamic_circuit-4ead3b497bbcc632.yaml b/releasenotes/notes/fix_transpiler_lvl_for_dynamic_circuit-4ead3b497bbcc632.yaml new file mode 100644 index 0000000000..e42a2b33c1 --- /dev/null +++ b/releasenotes/notes/fix_transpiler_lvl_for_dynamic_circuit-4ead3b497bbcc632.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + For dynamic circuits, aer_compiler calls transpiler without optimizaiton + level. Because id gates are removed by transpiler, noise applied id gates + did not work correctly. This fix adds optimization_level=0 not to remove + id gates of dynamic circuits From 68b89e72df61db44fe148ac0d869a9ad4a8b47c2 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 20 Nov 2023 19:34:25 +0900 Subject: [PATCH 10/17] fix ry gate for stabilizer (#2001) Co-authored-by: Hiroshi Horii --- .../notes/fix_stabilizer_ry_gate-07538d8a2462c09d.yaml | 4 ++++ src/simulators/stabilizer/stabilizer_state.hpp | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/fix_stabilizer_ry_gate-07538d8a2462c09d.yaml diff --git a/releasenotes/notes/fix_stabilizer_ry_gate-07538d8a2462c09d.yaml b/releasenotes/notes/fix_stabilizer_ry_gate-07538d8a2462c09d.yaml new file mode 100644 index 0000000000..66c10e1191 --- /dev/null +++ b/releasenotes/notes/fix_stabilizer_ry_gate-07538d8a2462c09d.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + fixed ry gate for stabilizer method, PI/2 and PI3/2 was inverted diff --git a/src/simulators/stabilizer/stabilizer_state.hpp b/src/simulators/stabilizer/stabilizer_state.hpp index 26ab0f418e..9078237d62 100644 --- a/src/simulators/stabilizer/stabilizer_state.hpp +++ b/src/simulators/stabilizer/stabilizer_state.hpp @@ -417,16 +417,14 @@ void State::apply_gate(const Operations::Op &op) { case Gates::ry: pi2 = (int_t)std::round(std::real(op.params[0]) * 2.0 / M_PI) & 3; if (pi2 == 1) { - // HX - BaseState::qreg_.append_x(op.qubits[0]); BaseState::qreg_.append_h(op.qubits[0]); + BaseState::qreg_.append_x(op.qubits[0]); } else if (pi2 == 2) { // Y BaseState::qreg_.append_y(op.qubits[0]); } else if (pi2 == 3) { - // Hdg - BaseState::qreg_.append_h(op.qubits[0]); BaseState::qreg_.append_x(op.qubits[0]); + BaseState::qreg_.append_h(op.qubits[0]); } break; case Gates::rz: From ab06827e512603452fd814d0612f4182ffe5b833 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 20 Nov 2023 23:00:10 -0500 Subject: [PATCH 11/17] Directly use psutil to get total system memory (#2002) Currently Aer is using Qiskit's local_hardware_info() function which to determine the total amount of system memory which is used to compute the largest statevector the system can build. However, this function wasn't really intended to be used outside of Qiskit and also Qiskit is looking to remove the memory reporting (see: Qiskit/qiskit#11254). This commit just pivots to using psutil directly which is what qiskit is doing internally. --- qiskit_aer/backends/backend_utils.py | 6 ++++-- qiskit_aer/backends/statevector_simulator.py | 5 +++-- qiskit_aer/backends/unitary_simulator.py | 5 +++-- releasenotes/notes/psutil-added-ffb2a4b5956fa03d.yaml | 9 +++++++++ setup.py | 1 + 5 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/psutil-added-ffb2a4b5956fa03d.yaml diff --git a/qiskit_aer/backends/backend_utils.py b/qiskit_aer/backends/backend_utils.py index d472b924f4..eb99050486 100644 --- a/qiskit_aer/backends/backend_utils.py +++ b/qiskit_aer/backends/backend_utils.py @@ -16,16 +16,18 @@ """ import os from math import log2 -from qiskit.utils import local_hardware_info + +import psutil from qiskit.circuit import QuantumCircuit from qiskit.compiler import assemble from qiskit.qobj import QasmQobjInstruction from qiskit.result import ProbDistribution from qiskit.quantum_info import Clifford + from .compatibility import Statevector, DensityMatrix, StabilizerState, Operator, SuperOp # Available system memory -SYSTEM_MEMORY_GB = local_hardware_info()["memory"] +SYSTEM_MEMORY_GB = psutil.virtual_memory().total / (1024**3) # Max number of qubits for complex double statevector # given available system memory diff --git a/qiskit_aer/backends/statevector_simulator.py b/qiskit_aer/backends/statevector_simulator.py index 342997c49f..da653bac6c 100644 --- a/qiskit_aer/backends/statevector_simulator.py +++ b/qiskit_aer/backends/statevector_simulator.py @@ -16,7 +16,8 @@ import copy import logging from warnings import warn -from qiskit.utils import local_hardware_info + +import psutil from qiskit.providers.options import Options from qiskit.providers.models import QasmBackendConfiguration @@ -364,7 +365,7 @@ def _validate(self, qobj): if n_qubits > max_qubits: raise AerError( f"Number of qubits ({n_qubits}) is greater than max ({max_qubits}) " - f'for "{name}" with {int(local_hardware_info()["memory"])} GB system memory.' + f'for "{name}" with {int(psutil.virtual_memory().total / (1024**3))} GB system memory.' ) if qobj.config.shots != 1: diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index ca28204a9a..1cb477e67b 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -17,7 +17,8 @@ import copy import logging from warnings import warn -from qiskit.utils import local_hardware_info + +import psutil from qiskit.providers.options import Options from qiskit.providers.models import QasmBackendConfiguration @@ -351,7 +352,7 @@ def _validate(self, qobj): raise AerError( f"Number of qubits ({n_qubits}) is greater than " f'max ({max_qubits}) for "{name}" with ' - f"{int(local_hardware_info()['memory'])} GB system memory." + f"{int(psutil.virtual_memory().total / (1024**3))} GB system memory." ) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) diff --git a/releasenotes/notes/psutil-added-ffb2a4b5956fa03d.yaml b/releasenotes/notes/psutil-added-ffb2a4b5956fa03d.yaml new file mode 100644 index 0000000000..34939eafd2 --- /dev/null +++ b/releasenotes/notes/psutil-added-ffb2a4b5956fa03d.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + Added `psutil `__ as a dependency for + Qiskit Aer. This is used to determine the amount of physical resources + available. ``psutil`` is currently a dependency of Qiskit, which is a + requirement for Qiskit Aer, so ``psutil`` was effectively already required + for any ``qiskit-aer`` installation. But, as qiskit-aer is now using it + directly is now a direct dependency for ``qiskit-aer``. diff --git a/setup.py b/setup.py index d2a67e87f3..fc35929a17 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ "qiskit>=0.44.0", "numpy>=1.16.3", "scipy>=1.0", + "psutil>=5", ] classifiers = [ From a261af2a0bf6eaacc8218ec090bd7b382b9b9610 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Wed, 22 Nov 2023 12:40:20 +0900 Subject: [PATCH 12/17] test build fix (#2004) --- .github/workflows/tests.yml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aa7b3903d5..88a9b13feb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -175,7 +175,7 @@ jobs: - name: Install Aer run: | set -e - python -I -m build --wheel --config-setting=--build-option=-- --config-setting=--build-option=-DTEST_JSON=1 + python -I -m build --wheel --config-setting=--build-option=-DTEST_JSON=1 pip install -U dist/*.whl - name: Run Tests run: | diff --git a/tox.ini b/tox.ini index 5b5a17c4ef..7a8d31afd3 100644 --- a/tox.ini +++ b/tox.ini @@ -53,7 +53,7 @@ deps = build qiskit-ibmq-provider commands = - python -I -m build --wheel -C=--build-option=-- -C=--build-option=-- -C=--build-option=-j4 + python -I -m build --wheel -C=--build-option=-j4 pip install --find-links={toxinidir}/dist qiskit_aer sphinx-build -W -b html docs/ docs/_build/html -j auto {posargs} From e30bedb7b941f8143b165dec62e8657eea87708a Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Wed, 22 Nov 2023 13:43:00 +0900 Subject: [PATCH 13/17] Reverse ordering to read out error in sampling measure (#2003) * reverse ordering of read out error in sampling measure * fix batch check --------- Co-authored-by: Hiroshi Horii --- .../notes/fix_sample_measure_roerr-747b955aa2bf778c.yaml | 7 +++++++ src/simulators/batch_shots_executor.hpp | 2 +- src/simulators/circuit_executor.hpp | 2 +- src/simulators/multi_state_executor.hpp | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/fix_sample_measure_roerr-747b955aa2bf778c.yaml diff --git a/releasenotes/notes/fix_sample_measure_roerr-747b955aa2bf778c.yaml b/releasenotes/notes/fix_sample_measure_roerr-747b955aa2bf778c.yaml new file mode 100644 index 0000000000..a36e891c3d --- /dev/null +++ b/releasenotes/notes/fix_sample_measure_roerr-747b955aa2bf778c.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes order of applying read out error in sampling measure + in circuit executors. Ordering is reversed to fit to the older verison + of Aer. + Also fixed check of bacth execution. diff --git a/src/simulators/batch_shots_executor.hpp b/src/simulators/batch_shots_executor.hpp index ec98965846..6776bb26de 100644 --- a/src/simulators/batch_shots_executor.hpp +++ b/src/simulators/batch_shots_executor.hpp @@ -150,7 +150,7 @@ template void BatchShotsExecutor::run_circuit_with_sampling( Circuit &circ, const Config &config, RngEngine &init_rng, ResultItr result_it) { - if (circ.num_bind_params == 1 || !enable_batch_multi_shots_) { + if (!enable_batch_multi_shots_) { return Executor::run_circuit_with_sampling(circ, config, init_rng, result_it); } diff --git a/src/simulators/circuit_executor.hpp b/src/simulators/circuit_executor.hpp index 02f00224a6..aa8d70d761 100644 --- a/src/simulators/circuit_executor.hpp +++ b/src/simulators/circuit_executor.hpp @@ -1073,7 +1073,7 @@ void Executor::measure_sampler(InputIterator first_meas, uint_t num_registers = (register_map.empty()) ? 0ULL : 1 + register_map.rbegin()->first; ClassicalRegister creg; - for (int_t i = 0; i < all_samples.size(); i++) { + for (int_t i = all_samples.size() - 1; i >= 0; i--) { creg.initialize(num_memory, num_registers); // process memory bit measurements diff --git a/src/simulators/multi_state_executor.hpp b/src/simulators/multi_state_executor.hpp index 27e533c702..be578c0da3 100644 --- a/src/simulators/multi_state_executor.hpp +++ b/src/simulators/multi_state_executor.hpp @@ -863,7 +863,7 @@ void MultiStateExecutor::measure_sampler(InputIterator first_meas, (memory_map.empty()) ? 0ULL : 1 + memory_map.rbegin()->first; uint_t num_registers = (register_map.empty()) ? 0ULL : 1 + register_map.rbegin()->first; - for (int_t i = 0; i < all_samples.size(); i++) { + for (int_t i = all_samples.size() - 1; i >= 0; i--) { ClassicalRegister creg = state.creg(); // process memory bit measurements From f8e1a3968eb520cf0f61abae3b83323d376380ef Mon Sep 17 00:00:00 2001 From: eliotheinrich <38039898+eliotheinrich@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:48:35 -0500 Subject: [PATCH 14/17] Fix extended stabilizer thread safety in apply_ops_parallel (#1993) * Extended stabilizer simulator no longer shares RngEngine amongst states when ops are applied in parallel * Added release note * Fixed ugly cast --------- Co-authored-by: Jun Doi --- ...fix_extstabilizer_thread_safety-c85e926c7ecb8dfb.yaml | 6 ++++++ .../extended_stabilizer/extended_stabilizer_state.hpp | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix_extstabilizer_thread_safety-c85e926c7ecb8dfb.yaml diff --git a/releasenotes/notes/fix_extstabilizer_thread_safety-c85e926c7ecb8dfb.yaml b/releasenotes/notes/fix_extstabilizer_thread_safety-c85e926c7ecb8dfb.yaml new file mode 100644 index 0000000000..2551b8bc47 --- /dev/null +++ b/releasenotes/notes/fix_extstabilizer_thread_safety-c85e926c7ecb8dfb.yaml @@ -0,0 +1,6 @@ +fixes: + - | + Extended stabilizer simulation was sharing a single copy of RngEngine amongst + parallelized states in ``ExtendedStabilizer::State::apply_ops_parallel``, + leading to thread safety issue. Now, a new RngEngine is seeded for each parallel + state. \ No newline at end of file diff --git a/src/simulators/extended_stabilizer/extended_stabilizer_state.hpp b/src/simulators/extended_stabilizer/extended_stabilizer_state.hpp index 86947f9b5a..be6a8af609 100644 --- a/src/simulators/extended_stabilizer/extended_stabilizer_state.hpp +++ b/src/simulators/extended_stabilizer/extended_stabilizer_state.hpp @@ -463,6 +463,12 @@ template void State::apply_ops_parallel(InputIterator first, InputIterator last, ExperimentResult &result, RngEngine &rng) { const int_t NUM_STATES = BaseState::qreg_.get_num_states(); + + std::vector rng_seeds(NUM_STATES); + for (int_t i = 0; i < NUM_STATES; i++) { + rng_seeds[i] = rng.rand_int(0, SIZE_MAX); + } + #pragma omp parallel for if (BaseState::qreg_.check_omp_threshold() && \ BaseState::threads_ > 1) \ num_threads(BaseState::threads_) @@ -470,10 +476,11 @@ void State::apply_ops_parallel(InputIterator first, InputIterator last, if (!BaseState::qreg_.check_eps(i)) { continue; } + RngEngine local_rng(rng_seeds[i]); for (auto it = first; it != last; it++) { switch (it->type) { case Operations::OpType::gate: - apply_gate(*it, rng, i); + apply_gate(*it, local_rng, i); break; case Operations::OpType::barrier: case Operations::OpType::qerror_loc: From c784592e307e795740d9fd5d64c3b7737a2bb58d Mon Sep 17 00:00:00 2001 From: Ikko Hamamura Date: Fri, 24 Nov 2023 11:39:49 +0900 Subject: [PATCH 15/17] add note (#1992) Co-authored-by: Jun Doi --- qiskit_aer/primitives/estimator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_aer/primitives/estimator.py b/qiskit_aer/primitives/estimator.py index 14f5749469..17bdfa7ba1 100644 --- a/qiskit_aer/primitives/estimator.py +++ b/qiskit_aer/primitives/estimator.py @@ -91,7 +91,7 @@ def __init__( transpile_options: Options passed to transpile. run_options: Options passed to run. approximation: If True, it calculates expectation values with normal distribution - approximation. + approximation. Note that this appproximation ignores readout errors. skip_transpilation: If True, transpilation is skipped. abelian_grouping: Whether the observable should be grouped into commuting. If approximation is True, this parameter is ignored and assumed to be False. From ea70d1cfe55806a80ccd8ba287c8bc3189178b94 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 24 Nov 2023 15:03:29 +0900 Subject: [PATCH 16/17] Fix AerBackend issues caused by upgrading BackendV2 (#1995) * add description if no description is provided, build coupling map if it is provided * move import line * fix target for simulator backend * format * remove unused import * use translation plugin to rebuild gate sets for simulator * rename plugin * rebuild of gate sets is eanbled only for opt level 0 and 1 * fix custom pass manager * fix pass_manager function * added ccx in NAME_MAPPING * added missed gates in NAME_MAPPING * added release note * add check if opnodes is None * add check config * decrease return * check opt level * fix searching ops in control flow blocks * Update qiskit_aer/backends/plugin/aer_backend_plugin.py Co-authored-by: Matthew Treinish * Update qiskit_aer/backends/plugin/aer_backend_plugin.py Co-authored-by: Matthew Treinish * refer review comments * remove unused import --------- Co-authored-by: Matthew Treinish --- qiskit_aer/backends/aer_simulator.py | 22 ++- qiskit_aer/backends/aerbackend.py | 54 +++----- qiskit_aer/backends/name_mapping.py | 103 +++++++------- qiskit_aer/backends/plugin/__init__.py | 0 .../backends/plugin/aer_backend_plugin.py | 126 ++++++++++++++++++ .../fix_aerbackend-7e9a74f8219315dc.yaml | 13 ++ setup.py | 6 + 7 files changed, 229 insertions(+), 95 deletions(-) create mode 100644 qiskit_aer/backends/plugin/__init__.py create mode 100644 qiskit_aer/backends/plugin/aer_backend_plugin.py create mode 100644 releasenotes/notes/fix_aerbackend-7e9a74f8219315dc.yaml diff --git a/qiskit_aer/backends/aer_simulator.py b/qiskit_aer/backends/aer_simulator.py index 2ede8e3bbb..04fc439592 100644 --- a/qiskit_aer/backends/aer_simulator.py +++ b/qiskit_aer/backends/aer_simulator.py @@ -695,7 +695,9 @@ class AerSimulator(AerBackend): _AVAILABLE_DEVICES = None - def __init__(self, configuration=None, properties=None, provider=None, **backend_options): + def __init__( + self, configuration=None, properties=None, provider=None, target=None, **backend_options + ): self._controller = aer_controller_execute() # Update available methods and devices for class @@ -717,7 +719,11 @@ def __init__(self, configuration=None, properties=None, provider=None, **backend self._cached_basis_gates = self._BASIS_GATES["automatic"] super().__init__( - configuration, properties=properties, provider=provider, backend_options=backend_options + configuration, + properties=properties, + provider=provider, + target=target, + backend_options=backend_options, ) @classmethod @@ -812,6 +818,11 @@ def _name(self): def from_backend(cls, backend, **options): """Initialize simulator from backend.""" if isinstance(backend, BackendV2): + if backend.description is None: + description = "created by AerSimulator.from_backend" + else: + description = backend.description + configuration = QasmBackendConfiguration( backend_name=f"'aer_simulator({backend.name})", backend_version=backend.backend_version, @@ -826,9 +837,10 @@ def from_backend(cls, backend, **options): max_shots=int(1e6), coupling_map=list(backend.coupling_map.get_edges()), max_experiments=backend.max_circuits, - description=backend.description, + description=description, ) properties = target_to_backend_properties(backend.target) + target = backend.target elif isinstance(backend, BackendV1): # Get configuration and properties from backend configuration = copy.copy(backend.configuration()) @@ -837,6 +849,8 @@ def from_backend(cls, backend, **options): # Customize configuration name name = configuration.backend_name configuration.backend_name = f"aer_simulator({name})" + + target = None else: raise TypeError( "The backend argument requires a BackendV2 or BackendV1 object, " @@ -853,7 +867,7 @@ def from_backend(cls, backend, **options): options["noise_model"] = noise_model # Initialize simulator - sim = cls(configuration=configuration, properties=properties, **options) + sim = cls(configuration=configuration, properties=properties, target=target, **options) return sim def available_methods(self): diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 22f620ba77..36545ffe8c 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -29,6 +29,7 @@ from qiskit.pulse import Schedule, ScheduleBlock from qiskit.qobj import QasmQobj, PulseQobj from qiskit.result import Result +from qiskit.transpiler import CouplingMap from ..aererror import AerError from ..jobs import AerJob, AerJobSet, split_qobj from ..noise.noise_model import NoiseModel, QuantumErrorLocation @@ -48,7 +49,7 @@ class AerBackend(Backend, ABC): """Aer Backend class.""" def __init__( - self, configuration, properties=None, defaults=None, backend_options=None, provider=None + self, configuration, properties=None, provider=None, target=None, backend_options=None ): """Aer class for backends. @@ -59,8 +60,8 @@ def __init__( Args: configuration (BackendConfiguration): backend configuration. properties (BackendProperties or None): Optional, backend properties. - defaults (PulseDefaults or None): Optional, backend pulse defaults. provider (Provider): Optional, provider responsible for this backend. + target (Target): initial target for backend backend_options (dict or None): Optional set custom backend options. Raises: @@ -76,22 +77,24 @@ def __init__( backend_version=configuration.backend_version, ) - # Initialize backend properties and pulse defaults. + # Initialize backend properties self._properties = properties - self._defaults = defaults self._configuration = configuration - # Custom option values for config, properties, and defaults + # Custom option values for config, properties self._options_configuration = {} - self._options_defaults = {} self._options_properties = {} - self._target = None + self._target = target self._mapping = NAME_MAPPING # Set options from backend_options dictionary if backend_options is not None: self.set_options(**backend_options) + # build coupling map + if self.configuration().coupling_map is not None: + self._coupling_map = CouplingMap(self.configuration().coupling_map) + def _convert_circuit_binds(self, circuit, binds, idx_map): parameterizations = [] @@ -330,18 +333,6 @@ def properties(self): setattr(properties, key, val) return properties - def defaults(self): - """Return the simulator backend pulse defaults. - - Returns: - PulseDefaults: The backend pulse defaults or ``None`` if the - backend does not support pulse. - """ - defaults = copy.copy(self._defaults) - for key, val in self._options_defaults.items(): - setattr(defaults, key, val) - return defaults - @property def max_circuits(self): if hasattr(self.configuration(), "max_experiments"): @@ -351,17 +342,16 @@ def max_circuits(self): @property def target(self): - self._target = convert_to_target( - self.configuration(), self.properties(), self.defaults(), self._mapping - ) - return self._target + if self._target is not None: + return self._target + + return convert_to_target(self.configuration(), self.properties(), None, NAME_MAPPING) def clear_options(self): """Reset the simulator options to default values.""" self._options = self._default_options() self._options_configuration = {} self._options_properties = {} - self._options_defaults = {} def status(self): """Return backend status. @@ -702,8 +692,6 @@ def set_option(self, key, value): self._set_configuration_option(key, value) elif hasattr(self._properties, key): self._set_properties_option(key, value) - elif hasattr(self._defaults, key): - self._set_defaults_option(key, value) else: if not hasattr(self._options, key): raise AerError(f"Invalid option {key}") @@ -735,15 +723,15 @@ def _set_properties_option(self, key, value): elif key in self._options_properties: self._options_properties.pop(key) - def _set_defaults_option(self, key, value): - """Special handling for setting backend defaults options.""" - if value is not None: - self._options_defaults[key] = value - elif key in self._options_defaults: - self._options_defaults.pop(key) - def __repr__(self): """String representation of an AerBackend.""" name = self.__class__.__name__ display = f"'{self.name}'" return f"{name}({display})" + + def get_translation_stage_plugin(self): + """use custom translation method to avoid gate exchange""" + if self._target is None: + return "aer_backend_plugin" + else: + return None diff --git a/qiskit_aer/backends/name_mapping.py b/qiskit_aer/backends/name_mapping.py index 0caadc1999..419e3cde37 100644 --- a/qiskit_aer/backends/name_mapping.py +++ b/qiskit_aer/backends/name_mapping.py @@ -17,21 +17,23 @@ from qiskit.circuit import ControlledGate, Parameter from qiskit.circuit.reset import Reset from qiskit.circuit.library import ( - SXGate, - MCPhaseGate, - MCXGate, - RZGate, - RXGate, U2Gate, - U1Gate, - U3Gate, - YGate, - ZGate, - PauliGate, - SwapGate, RGate, + CYGate, + CZGate, + CSXGate, + CU3Gate, + CSwapGate, + PauliGate, + DiagonalGate, + UnitaryGate, + MCPhaseGate, + MCXGate, + CRXGate, + CRYGate, + CRZGate, + MCU1Gate, MCXGrayCode, - RYGate, ) from qiskit.circuit.controlflow import ( IfElseOp, @@ -41,8 +43,8 @@ BreakLoopOp, SwitchCaseOp, ) -from qiskit.extensions import Initialize, UnitaryGate -from qiskit.extensions.quantum_initializer import DiagonalGate, UCGate +from qiskit.extensions import Initialize +from qiskit.extensions.quantum_initializer import UCGate from qiskit.quantum_info.operators.channel.kraus import Kraus from qiskit.quantum_info.operators.channel import SuperOp from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel @@ -85,7 +87,7 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=SXGate(), + base_gate=CSXGate(), ) @@ -100,7 +102,7 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=YGate(), + base_gate=CYGate(), ) @@ -115,7 +117,7 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=ZGate(), + base_gate=CZGate(), ) @@ -130,7 +132,7 @@ def __init__(self, theta, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=RXGate(theta), + base_gate=CRXGate(theta), ) @@ -145,7 +147,7 @@ def __init__(self, theta, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=RYGate(theta), + base_gate=CRYGate(theta), ) @@ -160,7 +162,7 @@ def __init__(self, theta, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=RZGate(theta), + base_gate=CRZGate(theta), ) @@ -179,21 +181,6 @@ def __init__(self, theta, phi, num_ctrl_qubits, ctrl_state=None): ) -class MCU1Gate(ControlledGate): - """mcu1 gate""" - - def __init__(self, theta, num_ctrl_qubits, ctrl_state=None): - super().__init__( - "mcu1", - 1 + num_ctrl_qubits, - [theta], - None, - num_ctrl_qubits, - ctrl_state=ctrl_state, - base_gate=U1Gate(theta), - ) - - class MCU2Gate(ControlledGate): """mcu2 gate""" @@ -220,7 +207,7 @@ def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=U3Gate(theta, phi, lam), + base_gate=CU3Gate(theta, phi, lam), ) @@ -235,7 +222,7 @@ def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=U3Gate(theta, phi, lam), + base_gate=CU3Gate(theta, phi, lam), ) @@ -250,33 +237,43 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): None, num_ctrl_qubits, ctrl_state=ctrl_state, - base_gate=SwapGate(), + base_gate=CSwapGate(), ) PHI = Parameter("phi") LAM = Parameter("lam") NAME_MAPPING = { + "cu2": U2Gate(PHI, LAM).control(), + "pauli": PauliGate, + "diagonal": DiagonalGate, + "unitary": UnitaryGate, "mcsx": MCSXGate, "mcp": MCPhaseGate, "mcphase": MCPhaseGate, + "mcu": MCUGate, + "mcu1": MCU1Gate, + "mcu2": MCU2Gate, + "mcu3": MCU3Gate, + "mcx": MCXGate, + "mcy": MCYGate, + "mcz": MCZGate, + "mcr": MCRGate, + "mcrx": MCRXGate, + "mcry": MCRYGate, + "mcrz": MCRZGate, + "mcx_gray": MCXGrayCode, + "mcswap": MCSwapGate, + "multiplexer": UCGate, + "kraus": Kraus, + "superop": SuperOp, "initialize": Initialize, "quantum_channel": QuantumChannel, "save_expval": SaveExpectationValue, - "diagonal": DiagonalGate, "save_amplitudes": SaveAmplitudes, "roerror": ReadoutError, - "mcrx": MCRXGate, - "kraus": Kraus, "save_statevector_dict": SaveStatevectorDict, - "mcx": MCXGate, - "mcu1": MCU1Gate, - "mcu2": MCU2Gate, - "mcu3": MCU3Gate, "save_superop": SaveSuperOp, - "multiplexer": UCGate, - "mcy": MCYGate, - "superop": SuperOp, "save_clifford": SaveClifford, "save_matrix_product_state": SaveMatrixProductState, "save_density_matrix": SaveDensityMatrix, @@ -288,30 +285,20 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): "break_loop": BreakLoopOp, "continue_loop": ContinueLoopOp, "save_statevector": SaveStatevector, - "mcu": MCUGate, "set_density_matrix": SetDensityMatrix, "qerror_loc": QuantumErrorLocation, - "unitary": UnitaryGate, - "mcz": MCZGate, - "pauli": PauliGate, "set_unitary": SetUnitary, "save_state": SaveState, - "mcswap": MCSwapGate, "set_matrix_product_state": SetMatrixProductState, "save_unitary": SaveUnitary, - "mcr": MCRGate, - "mcx_gray": MCXGrayCode, - "mcrz": MCRZGate, "set_superop": SetSuperOp, "save_expval_var": SaveExpectationValueVariance, "save_stabilizer": SaveStabilizer, "set_statevector": SetStatevector, - "mcry": MCRYGate, "set_stabilizer": SetStabilizer, "save_amplitudes_sq": SaveAmplitudesSquared, "save_probabilities_dict": SaveProbabilitiesDict, "save_probs_ket": SaveProbabilitiesDict, "save_probs": SaveProbabilities, - "cu2": U2Gate(PHI, LAM).control(), "reset": Reset(), } diff --git a/qiskit_aer/backends/plugin/__init__.py b/qiskit_aer/backends/plugin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qiskit_aer/backends/plugin/aer_backend_plugin.py b/qiskit_aer/backends/plugin/aer_backend_plugin.py new file mode 100644 index 0000000000..73be26fcef --- /dev/null +++ b/qiskit_aer/backends/plugin/aer_backend_plugin.py @@ -0,0 +1,126 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Aer simulator backend transpiler plug-in +""" +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin +from qiskit.transpiler import PassManager, TransformationPass +from qiskit.transpiler.passes import BasisTranslator +from qiskit.transpiler.passes import UnitarySynthesis +from qiskit.transpiler.passes import HighLevelSynthesis +from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel +from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping +from qiskit.circuit.measure import Measure +from qiskit.circuit.library import Barrier +from qiskit.circuit import ControlFlowOp +from qiskit.converters import circuit_to_dag +from qiskit_aer.backends.name_mapping import NAME_MAPPING + + +class AerBackendRebuildGateSetsFromCircuit(TransformationPass): + """custom translation class to rebuild basis gates with gates in circuit""" + + def __init__(self, config, opt_lvl): + super().__init__() + self.config = config + if opt_lvl is None: + self.optimization_level = 1 + else: + self.optimization_level = opt_lvl + self.qiskit_inst_name_map = get_standard_gate_name_mapping() + self.qiskit_inst_name_map["barrier"] = Barrier + + def _add_ops(self, dag, ops: set): + num_unsupported_ops = 0 + opnodes = dag.op_nodes() + if opnodes is None: + return num_unsupported_ops + + for node in opnodes: + if isinstance(node.op, ControlFlowOp): + for block in node.op.blocks: + num_unsupported_ops += self._add_ops(circuit_to_dag(block), ops) + if node.name in self.qiskit_inst_name_map: + ops.add(node.name) + elif node.name in self.config.target: + ops.add(node.name) + else: + num_unsupported_ops = num_unsupported_ops + 1 + return num_unsupported_ops + + def run(self, dag): + # do nothing for higher optimization level + if self.optimization_level > 1: + return dag + if self.config is None or self.config.target is None: + return dag + + # search ops in supported name mapping + ops = set() + num_unsupported_ops = self._add_ops(dag, ops) + + # if there are some unsupported node (i.e. RealAmplitudes) do nothing + if num_unsupported_ops > 0 or len(ops) < 1: + return dag + + # clear all instructions in target + self.config.target._gate_map.clear() + self.config.target._gate_name_map.clear() + self.config.target._qarg_gate_map.clear() + self.config.target._global_operations.clear() + + # rebuild gate sets from circuit + for name in ops: + if name in self.qiskit_inst_name_map: + self.config.target.add_instruction(self.qiskit_inst_name_map[name], name=name) + else: + self.config.target.add_instruction(NAME_MAPPING[name], name=name) + if "measure" not in ops: + self.config.target.add_instruction(Measure()) + self.config.basis_gates = list(self.config.target.operation_names) + + return dag + + +# This plugin should not be used outside of simulator +# TODO : this plugin should be moved to optimization stage plugin +# if Qiskit will have custom optimizaiton stage plugin interface +# in that case just return pass without Optimize1qGatesDecomposition +class AerBackendPlugin(PassManagerStagePlugin): + """custom passmanager to avoid unnecessary gate changes""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + return PassManager( + [ + UnitarySynthesis( + pass_manager_config.basis_gates, + approximation_degree=pass_manager_config.approximation_degree, + coupling_map=pass_manager_config.coupling_map, + backend_props=pass_manager_config.backend_properties, + plugin_config=pass_manager_config.unitary_synthesis_plugin_config, + method=pass_manager_config.unitary_synthesis_method, + target=pass_manager_config.target, + ), + HighLevelSynthesis( + hls_config=pass_manager_config.hls_config, + coupling_map=pass_manager_config.coupling_map, + target=pass_manager_config.target, + use_qubit_indices=True, + equivalence_library=sel, + basis_gates=pass_manager_config.basis_gates, + ), + BasisTranslator(sel, pass_manager_config.basis_gates, pass_manager_config.target), + AerBackendRebuildGateSetsFromCircuit( + config=pass_manager_config, opt_lvl=optimization_level + ), + ] + ) diff --git a/releasenotes/notes/fix_aerbackend-7e9a74f8219315dc.yaml b/releasenotes/notes/fix_aerbackend-7e9a74f8219315dc.yaml new file mode 100644 index 0000000000..f2cf556195 --- /dev/null +++ b/releasenotes/notes/fix_aerbackend-7e9a74f8219315dc.yaml @@ -0,0 +1,13 @@ +--- +fixes: + - | + Fixes AerBackend issues caused by upgading to BackendV2 in 0.13.0 release + and fix test failures for Qiskit 0.45 release. + + For issue #1987, added description if backend given by from_backend does not + have description. + + For issue #1988, added building coupling map from option. + + For issue #1982, added custome pass maneger to rebuild basis gates from + input circuits to prevent unnecessary gate changes diff --git a/setup.py b/setup.py index fc35929a17..d0b8ce4d8a 100644 --- a/setup.py +++ b/setup.py @@ -91,6 +91,7 @@ if is_win_32_bit: cmake_args.append("-DCMAKE_GENERATOR_PLATFORM=Win32") + setup( name=PACKAGE_NAME, version=VERSION, @@ -112,4 +113,9 @@ cmake_args=cmake_args, keywords="qiskit, simulator, quantum computing, backend", zip_safe=False, + entry_points={ + "qiskit.transpiler.translation": [ + "aer_backend_plugin = qiskit_aer.backends.plugin.aer_backend_plugin:AerBackendPlugin", + ] + }, ) From 76673bc6977f51cd855aee0fbf9c453c1a5942ca Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 24 Nov 2023 15:09:15 +0900 Subject: [PATCH 17/17] add prelude --- releasenotes/notes/release0.13.1-a07688e4eb04b9d6.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 releasenotes/notes/release0.13.1-a07688e4eb04b9d6.yaml diff --git a/releasenotes/notes/release0.13.1-a07688e4eb04b9d6.yaml b/releasenotes/notes/release0.13.1-a07688e4eb04b9d6.yaml new file mode 100644 index 0000000000..c4108551d6 --- /dev/null +++ b/releasenotes/notes/release0.13.1-a07688e4eb04b9d6.yaml @@ -0,0 +1,6 @@ +--- +prelude: > + Aer Release 0.13.1 is bug fix release for 0.13.0. + The most important fixes are for issues caused by upgrading AerBackend to + BackendV2 interface. + Aer 0.13.1 is now ready for Qiskit 0.45.