From 0abacdffaaaa8635d0dde1182a590ef36db00270 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Tue, 23 Apr 2024 14:36:24 +0900 Subject: [PATCH] Fixes for dependency issues caused by 0.14 release (#2094) * Fixes for dependency issues * lint * lint * lint * fix release note * fix sampler * fix sampler * fix sampler * fix sampler * remove skip cp38 * hide primitives V2 for qiskit < 1.0 * lint * add test case for sampling measure for large stabilizer circuit * reduce warning * replace test case for large stabilizer with GHZ circuit * format * format * convert basis_gates from list to set * fix assemble_circuits --- pyproject.toml | 2 +- qiskit_aer/backends/aer_compiler.py | 20 +++++++++++++++---- qiskit_aer/backends/aer_simulator.py | 4 +++- qiskit_aer/backends/aerbackend.py | 10 +++++++++- qiskit_aer/primitives/__init__.py | 8 ++++++-- qiskit_aer/primitives/estimator.py | 1 + qiskit_aer/primitives/sampler.py | 15 +++++++++----- ...ndency_issues_by0.14-da7f11cb29710f86.yaml | 11 ++++++++++ .../matrix_product_state.hpp | 4 ++-- src/simulators/sample_vector.hpp | 5 +---- .../stabilizer/stabilizer_state.hpp | 2 +- .../backends/aer_simulator/test_measure.py | 19 ++++++++++++++++++ 12 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 releasenotes/notes/fixes_dependency_issues_by0.14-da7f11cb29710f86.yaml diff --git a/pyproject.toml b/pyproject.toml index 4d08ae9241..f6b8371875 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta" [tool.cibuildwheel] manylinux-x86_64-image = "manylinux2014" manylinux-i686-image = "manylinux2014" -skip = "pp* cp36* cp37* cp38* *musllinux*" +skip = "pp* cp36* cp37* *musllinux*" test-skip = "cp3*-win32 cp3*-manylinux_i686" test-command = "python {project}/tools/verify_wheels.py" # We need to use pre-built versions of Numpy and Scipy in the tests; they have a diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index 3d1965e25f..12758b4573 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -605,7 +605,7 @@ def generate_aer_config( return config -def assemble_circuit(circuit: QuantumCircuit): +def assemble_circuit(circuit: QuantumCircuit, basis_gates=None): """assemble circuit object mapped to AER::Circuit""" num_qubits = circuit.num_qubits @@ -687,6 +687,7 @@ def assemble_circuit(circuit: QuantumCircuit): is_conditional, conditional_reg, conditional_expr, + basis_gates, ) index_map.append(num_of_aer_ops - 1) @@ -792,6 +793,7 @@ def _assemble_op( is_conditional, conditional_reg, conditional_expr, + basis_gates, ): operation = inst.operation qubits = [qubit_indices[qubit] for qubit in inst.qubits] @@ -812,7 +814,7 @@ def _assemble_op( num_of_aer_ops = 1 # fmt: off - if name in { + if basis_gates is None and name in { "ccx", "ccz", "cp", "cswap", "csx", "cx", "cy", "cz", "delay", "ecr", "h", "id", "mcp", "mcphase", "mcr", "mcrx", "mcry", "mcrz", "mcswap", "mcsx", "mcu", "mcu1", "mcu2", "mcu3", "mcx", "mcx_gray", "mcy", "mcz", "p", "r", @@ -912,6 +914,9 @@ def _assemble_op( aer_circ.mark(qubits, params) elif name == "qerror_loc": aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg, aer_cond_expr) + elif basis_gates is not None and name in basis_gates: + aer_circ.gate(name, qubits, params, [], conditional_reg, aer_cond_expr, + label if label else name) elif name in ("for_loop", "while_loop", "if_else"): raise AerError( "control-flow instructions must be converted " f"to jump and mark instructions: {name}" @@ -923,11 +928,12 @@ def _assemble_op( return num_of_aer_ops -def assemble_circuits(circuits: List[QuantumCircuit]) -> List[AerCircuit]: +def assemble_circuits(circuits: List[QuantumCircuit], basis_gates: list = None) -> List[AerCircuit]: """converts a list of Qiskit circuits into circuits mapped AER::Circuit Args: circuits: circuit(s) to be converted + basis_gates (list): supported gates to be converted Returns: a list of circuits to be run on the Aer backends and @@ -947,5 +953,11 @@ def assemble_circuits(circuits: List[QuantumCircuit]) -> List[AerCircuit]: # Generate AerCircuit from the input circuit aer_qc_list, idx_maps = assemble_circuits(circuits=[qc]) """ - aer_circuits, idx_maps = zip(*[assemble_circuit(circuit) for circuit in circuits]) + if basis_gates is not None: + basis_gates_set = set(basis_gates) + aer_circuits, idx_maps = zip( + *[assemble_circuit(circuit, basis_gates_set) for circuit in circuits] + ) + else: + aer_circuits, idx_maps = zip(*[assemble_circuit(circuit) for circuit in circuits]) return list(aer_circuits), list(idx_maps) diff --git a/qiskit_aer/backends/aer_simulator.py b/qiskit_aer/backends/aer_simulator.py index 083fc5f9d2..958a10ba89 100644 --- a/qiskit_aer/backends/aer_simulator.py +++ b/qiskit_aer/backends/aer_simulator.py @@ -15,6 +15,7 @@ import copy import logging +from qiskit.providers import convert_to_target from qiskit.providers.options import Options from qiskit.providers.models import QasmBackendConfiguration from qiskit.providers.backend import BackendV2, BackendV1 @@ -33,6 +34,7 @@ # pylint: disable=import-error, no-name-in-module, abstract-method from .controller_wrappers import aer_controller_execute +from .name_mapping import NAME_MAPPING logger = logging.getLogger(__name__) @@ -856,7 +858,7 @@ def from_backend(cls, backend, **options): name = configuration.backend_name configuration.backend_name = f"aer_simulator_from({name})" - target = None + target = convert_to_target(configuration, properties, None, NAME_MAPPING) else: raise TypeError( "The backend argument requires a BackendV2 or BackendV1 object, " diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 58ca3d3d7a..ed8059a57b 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -350,6 +350,11 @@ def target(self): tgt._coupling_graph = self._coupling_map.graph.copy() return tgt + def set_max_qubits(self, max_qubits): + """Set maximun number of qubits to be used for this backend.""" + if self._target is not None: + self._configuration.n_qubits = max_qubits + def clear_options(self): """Reset the simulator options to default values.""" self._options = self._default_options() @@ -445,7 +450,10 @@ def _execute_circuits_job( # Compile circuits circuits, noise_model = self._compile(circuits, **run_options) - aer_circuits, idx_maps = assemble_circuits(circuits) + if self._target is not None: + aer_circuits, idx_maps = assemble_circuits(circuits, self.configuration().basis_gates) + else: + aer_circuits, idx_maps = assemble_circuits(circuits) if parameter_binds: run_options["parameterizations"] = self._convert_binds( circuits, parameter_binds, idx_maps diff --git a/qiskit_aer/primitives/__init__.py b/qiskit_aer/primitives/__init__.py index 7ed7414f0c..84a2d064d7 100644 --- a/qiskit_aer/primitives/__init__.py +++ b/qiskit_aer/primitives/__init__.py @@ -31,7 +31,11 @@ Estimator """ +import qiskit + from .estimator import Estimator -from .estimator_v2 import EstimatorV2 from .sampler import Sampler -from .sampler_v2 import SamplerV2 + +if not qiskit.__version__.startswith("0."): + from .estimator_v2 import EstimatorV2 + from .sampler_v2 import SamplerV2 diff --git a/qiskit_aer/primitives/estimator.py b/qiskit_aer/primitives/estimator.py index d4f9f65790..3c0ba42473 100644 --- a/qiskit_aer/primitives/estimator.py +++ b/qiskit_aer/primitives/estimator.py @@ -532,6 +532,7 @@ def _transpile_circuits(self, circuits): circuit = self._circuits[i].copy() circuit.measure_all() num_qubits = circuit.num_qubits + self._backend.set_max_qubits(num_qubits) circuit = self._transpile(circuit) bit_map = {bit: index for index, bit in enumerate(circuit.qubits)} layout = [bit_map[qr[0]] for _, qr, _ in circuit[-num_qubits:]] diff --git a/qiskit_aer/primitives/sampler.py b/qiskit_aer/primitives/sampler.py index 7f06124289..17350f0707 100644 --- a/qiskit_aer/primitives/sampler.py +++ b/qiskit_aer/primitives/sampler.py @@ -182,6 +182,15 @@ def _preprocess_circuit(circuit: QuantumCircuit): circuit.save_probabilities_dict(qargs) return circuit + def _transpile_circuit(self, circuit): + self._backend.set_max_qubits(circuit.num_qubits) + transpiled = transpile( + circuit, + self._backend, + **self._transpile_options, + ) + return transpiled + def _transpile(self, circuit_indices: Sequence[int], is_shots_none: bool): to_handle = [ i for i in set(circuit_indices) if (i, is_shots_none) not in self._transpiled_circuits @@ -191,11 +200,7 @@ def _transpile(self, circuit_indices: Sequence[int], is_shots_none: bool): if is_shots_none: circuits = (self._preprocess_circuit(circ) for circ in circuits) if not self._skip_transpilation: - circuits = transpile( - list(circuits), - self._backend, - **self._transpile_options, - ) + circuits = (self._transpile_circuit(circ) for circ in circuits) for i, circuit in zip(to_handle, circuits): self._transpiled_circuits[(i, is_shots_none)] = circuit diff --git a/releasenotes/notes/fixes_dependency_issues_by0.14-da7f11cb29710f86.yaml b/releasenotes/notes/fixes_dependency_issues_by0.14-da7f11cb29710f86.yaml new file mode 100644 index 0000000000..6fdb0e8595 --- /dev/null +++ b/releasenotes/notes/fixes_dependency_issues_by0.14-da7f11cb29710f86.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixes for dependency issues caused by release 0.14. + + Fix for issue in samplingVector.allocate() when > 63 qubits + + Use basis_gates in AerCompiler when AerBackend is made by from_backend + + Setting number of qubits before transpile for Primitives V1 (issue #2084) + diff --git a/src/simulators/matrix_product_state/matrix_product_state.hpp b/src/simulators/matrix_product_state/matrix_product_state.hpp index 60cba8195f..51df5dd27e 100644 --- a/src/simulators/matrix_product_state/matrix_product_state.hpp +++ b/src/simulators/matrix_product_state/matrix_product_state.hpp @@ -797,7 +797,7 @@ State::sample_measure_using_apply_measure(const reg_t &qubits, uint_t shots, for (int_t i = 0; i < static_cast(shots); i++) { temp.initialize(qreg_); auto single_result = temp.apply_measure_internal(qubits, rnds_list[i]); - all_samples[i] = single_result; + all_samples[i].from_vector(single_result); } } return all_samples; @@ -811,7 +811,7 @@ std::vector State::sample_measure_all(uint_t shots, #pragma omp parallel for if (getenv("PRL_PROB_MEAS")) for (int_t i = 0; i < static_cast(shots); i++) { auto single_result = qreg_.sample_measure(shots, rng); - all_samples[i] = single_result; + all_samples[i].from_vector(single_result); } return all_samples; } diff --git a/src/simulators/sample_vector.hpp b/src/simulators/sample_vector.hpp index 36717bfc4f..afc4658302 100644 --- a/src/simulators/sample_vector.hpp +++ b/src/simulators/sample_vector.hpp @@ -123,9 +123,7 @@ void SampleVector::allocate(uint_t n, uint_t base) { elem_mask_ = (1ull << (elem_shift_bits_ + 1)) - 1; vec_mask_ = (1ull << vec_shift_bits_) - 1; - uint_t size = n >> vec_shift_bits_; - if (size == 0) - size = 1; + uint_t size = (n + (REG_SIZE >> elem_shift_bits_) - 1) >> vec_shift_bits_; bits_.resize(size, 0ull); size_ = n; } @@ -185,7 +183,6 @@ void SampleVector::from_vector_with_map(const reg_t &src, const reg_t &map, uint_t pos = 0; uint_t n = REG_SIZE >> elem_shift_bits_; for (uint_t i = 0; i < bits_.size(); i++) { - uint_t n = REG_SIZE; uint_t val = 0; if (n > size_ - pos) n = size_ - pos; diff --git a/src/simulators/stabilizer/stabilizer_state.hpp b/src/simulators/stabilizer/stabilizer_state.hpp index e695411b61..cd0e18f678 100644 --- a/src/simulators/stabilizer/stabilizer_state.hpp +++ b/src/simulators/stabilizer/stabilizer_state.hpp @@ -519,7 +519,7 @@ std::vector State::sample_measure(const reg_t &qubits, auto qreg_cache = BaseState::qreg_; std::vector samples(shots); for (int_t ishot = 0; ishot < shots; ishot++) { - samples[ishot] = apply_measure_and_update(qubits, rng); + samples[ishot].from_vector(apply_measure_and_update(qubits, rng)); BaseState::qreg_ = qreg_cache; // restore pre-measurement data from cache } return samples; diff --git a/test/terra/backends/aer_simulator/test_measure.py b/test/terra/backends/aer_simulator/test_measure.py index eda06dbf7c..72356e5f89 100644 --- a/test/terra/backends/aer_simulator/test_measure.py +++ b/test/terra/backends/aer_simulator/test_measure.py @@ -17,6 +17,7 @@ from test.terra.reference import ref_measure from qiskit import QuantumCircuit from qiskit import transpile +import qiskit.quantum_info as qi from qiskit_aer import AerSimulator from qiskit_aer.noise import NoiseModel from qiskit_aer.noise.errors import ReadoutError, depolarizing_error @@ -265,6 +266,24 @@ def test_measure_stablizer_64bit(self, method, device): self.assertDictAlmostEqual(output, targets, delta=delta * shots) + @supported_methods(["stabilizer"], [65, 127, 433]) + def test_measure_sampling_large_ghz_stabilizer(self, method, device, num_qubits): + """Test sampling measure for large stabilizer circuit""" + shots = 1000 + delta = 0.05 + qc = QuantumCircuit(num_qubits) + qc.h(0) + for q in range(1, num_qubits): + qc.cx(q - 1, q) + qc.measure_all() + backend = self.backend(method=method) + result = backend.run(qc, shots=shots).result() + counts = result.get_counts() + targets = {} + targets["0" * num_qubits] = shots / 2 + targets["1" * num_qubits] = shots / 2 + self.assertDictAlmostEqual(counts, targets, delta=delta * shots) + # --------------------------------------------------------------------- # Test MPS algorithms for measure # ---------------------------------------------------------------------