From f9fdcb86c1fa093b35a27621238676faa462324d Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 29 Mar 2024 17:25:39 +0900 Subject: [PATCH 1/9] Implementing SamplerV2 --- qiskit_aer/primitives/__init__.py | 1 + qiskit_aer/primitives/sampler_v2.py | 264 ++++++++ .../notes/sampler_v2-be0fc5c29a8e2c14.yaml | 7 + test/terra/primitives/test_sampler_v2.py | 640 ++++++++++++++++++ 4 files changed, 912 insertions(+) create mode 100644 qiskit_aer/primitives/sampler_v2.py create mode 100644 releasenotes/notes/sampler_v2-be0fc5c29a8e2c14.yaml create mode 100644 test/terra/primitives/test_sampler_v2.py diff --git a/qiskit_aer/primitives/__init__.py b/qiskit_aer/primitives/__init__.py index 503cdadd88..21237cf3da 100644 --- a/qiskit_aer/primitives/__init__.py +++ b/qiskit_aer/primitives/__init__.py @@ -33,3 +33,4 @@ from .estimator import Estimator from .sampler import Sampler +from .sampler_v2 import SamplerV2 diff --git a/qiskit_aer/primitives/sampler_v2.py b/qiskit_aer/primitives/sampler_v2.py new file mode 100644 index 0000000000..99aeb1f9bc --- /dev/null +++ b/qiskit_aer/primitives/sampler_v2.py @@ -0,0 +1,264 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# 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. + +""" +Sampler V2 class. +""" + +from __future__ import annotations + +from collections.abc import Sequence + +from dataclasses import dataclass +from typing import Iterable +import warnings + +import numpy as np +from numpy.typing import NDArray + +from qiskit import ClassicalRegister, QiskitError, QuantumCircuit +from qiskit.circuit import ControlFlowOp + +from qiskit.primitives.base import BaseSamplerV2 +from qiskit.primitives.base.validation import _has_measure +from qiskit.primitives.containers import ( + BitArray, + PrimitiveResult, + PubResult, + SamplerPubLike, + make_data_bin, +) +from qiskit.primitives.containers.sampler_pub import SamplerPub +from qiskit.primitives.containers.bit_array import _min_num_bytes +from qiskit.primitives.primitive_job import PrimitiveJob +from qiskit.primitives.utils import bound_circuit_to_instruction + +from qiskit_aer import AerSimulator + + +@dataclass +class _MeasureInfo: + creg_name: str + num_bits: int + num_bytes: int + qreg_indices: list[int] + + +class SamplerV2(BaseSamplerV2): + """ + Aer implementation of SamplerV2 class. + + Each tuple of ``(circuit, parameter values, shots)``, called a sampler + primitive unified bloc (PUB), produces its own array-valued result. The :meth:`~run` method can + be given many pubs at once. + """ + + def __init__( + self, + *, + backend: AerSimulator = None, + default_shots: int = 1024, + seed: np.random.Generator | int | None = None, + ): + """ + Args: + backend: AerSimulator object to be used for sampler + default_shots: The default shots for the sampler if not specified during run. + seed: The seed or Generator object for random number generation. + If None, a random seeded default RNG will be used. + """ + self._default_shots = default_shots + self._seed = seed + + if backend is None: + self._backend = AerSimulator() + else: + self._backend = backend + + @property + def default_shots(self) -> int: + """Return the default shots""" + return self._default_shots + + @property + def seed(self) -> np.random.Generator | int | None: + """Return the seed or Generator object for random number generation.""" + return self._seed + + def run( + self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None + ) -> PrimitiveJob[PrimitiveResult[PubResult]]: + if shots is None: + shots = self._default_shots + coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs] + if any(len(pub.circuit.cregs) == 0 for pub in coerced_pubs): + warnings.warn( + "One of your circuits has no output classical registers and so the result " + "will be empty. Did you mean to add measurement instructions?", + UserWarning, + ) + + job = PrimitiveJob(self._run, coerced_pubs) + job._submit() + return job + + def _run(self, pubs: Iterable[SamplerPub]) -> PrimitiveResult[PubResult]: + results = [self._run_pub(pub) for pub in pubs] + return PrimitiveResult(results) + + def _run_pub(self, pub: SamplerPub) -> PubResult: + circuit, qargs, meas_info = _preprocess_circuit(pub.circuit) + + # convert to parameter bindings + parameter_values = pub.parameter_values + parameter_binds = {} + param_array = parameter_values.as_array(circuit.parameters) + parameter_binds = {p: param_array[..., i].ravel() for i, p in enumerate(circuit.parameters)} + + arrays = { + item.creg_name: np.zeros( + parameter_values.shape + (pub.shots, item.num_bytes), dtype=np.uint8 + ) + for item in meas_info + } + + if qargs: + circuit.measure_all() + result = self._backend.run( + circuit, + shots=pub.shots, + seed_simulator=self._seed, + parameter_binds=[parameter_binds], + ).result() + all_counts = result.get_counts() + + for index, counts in np.ndenumerate(all_counts): + samples = [] + for k, v in counts.items(): + k = k.replace(" ", "") + kk = "" + for q in qargs: + kk = k[circuit.num_qubits - 1 - q] + kk + for iv in range(0, v): + samples.append(kk) + + samples_array = np.array( + [np.fromiter(sample, dtype=np.uint8) for sample in samples] + ) + for item in meas_info: + ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) + arrays[item.creg_name][index] = ary + else: + for index in np.ndenumerate(parameter_values.shape): + samples = [""] * pub.shots + samples_array = np.array( + [np.fromiter(sample, dtype=np.uint8) for sample in samples] + ) + for item in meas_info: + ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) + arrays[item.creg_name][index] = ary + + data_bin_cls = make_data_bin( + [(item.creg_name, BitArray) for item in meas_info], + shape=parameter_values.shape, + ) + meas = { + item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info + } + data_bin = data_bin_cls(**meas) + return PubResult(data_bin, metadata={"shots": pub.shots}) + + +def _preprocess_circuit(circuit: QuantumCircuit): + num_bits_dict = {creg.name: creg.size for creg in circuit.cregs} + mapping = _final_measurement_mapping(circuit) + qargs = sorted(set(mapping.values())) + qargs_index = {v: k for k, v in enumerate(qargs)} + circuit = circuit.remove_final_measurements(inplace=False) + if _has_control_flow(circuit): + raise QiskitError("StatevectorSampler cannot handle ControlFlowOp") + if _has_measure(circuit): + raise QiskitError("StatevectorSampler cannot handle mid-circuit measurements") + # num_qubits is used as sentinel to fill 0 in _samples_to_packed_array + sentinel = len(qargs) + indices = {key: [sentinel] * val for key, val in num_bits_dict.items()} + for key, qreg in mapping.items(): + creg, ind = key + indices[creg.name][ind] = qargs_index[qreg] + meas_info = [ + _MeasureInfo( + creg_name=name, + num_bits=num_bits, + num_bytes=_min_num_bytes(num_bits), + qreg_indices=indices[name], + ) + for name, num_bits in num_bits_dict.items() + ] + return circuit, qargs, meas_info + + +def _samples_to_packed_array( + samples: NDArray[np.uint8], num_bits: int, indices: list[int] +) -> NDArray[np.uint8]: + # samples of `Statevector.sample_memory` will be in the order of + # qubit_last, ..., qubit_1, qubit_0. + # reverse the sample order into qubit_0, qubit_1, ..., qubit_last and + # pad 0 in the rightmost to be used for the sentinel introduced by _preprocess_circuit. + ary = np.pad(samples[:, ::-1], ((0, 0), (0, 1)), constant_values=0) + # place samples in the order of clbit_last, ..., clbit_1, clbit_0 + ary = ary[:, indices[::-1]] + # pad 0 in the left to align the number to be mod 8 + # since np.packbits(bitorder='big') pads 0 to the right. + pad_size = -num_bits % 8 + ary = np.pad(ary, ((0, 0), (pad_size, 0)), constant_values=0) + # pack bits in big endian order + ary = np.packbits(ary, axis=-1) + return ary + + +def _final_measurement_mapping(circuit: QuantumCircuit) -> dict[tuple[ClassicalRegister, int], int]: + """Return the final measurement mapping for the circuit. + + Parameters: + circuit: Input quantum circuit. + + Returns: + Mapping of classical bits to qubits for final measurements. + """ + active_qubits = set(range(circuit.num_qubits)) + active_cbits = set(range(circuit.num_clbits)) + + # Find final measurements starting in back + mapping = {} + for item in circuit[::-1]: + if item.operation.name == "measure": + loc = circuit.find_bit(item.clbits[0]) + cbit = loc.index + qbit = circuit.find_bit(item.qubits[0]).index + if cbit in active_cbits and qbit in active_qubits: + for creg in loc.registers: + mapping[creg] = qbit + active_cbits.remove(cbit) + elif item.operation.name not in ["barrier", "delay"]: + for qq in item.qubits: + _temp_qubit = circuit.find_bit(qq).index + if _temp_qubit in active_qubits: + active_qubits.remove(_temp_qubit) + + if not active_cbits or not active_qubits: + break + + return mapping + + +def _has_control_flow(circuit: QuantumCircuit) -> bool: + return any(isinstance(instruction.operation, ControlFlowOp) for instruction in circuit) diff --git a/releasenotes/notes/sampler_v2-be0fc5c29a8e2c14.yaml b/releasenotes/notes/sampler_v2-be0fc5c29a8e2c14.yaml new file mode 100644 index 0000000000..d3a2924f2f --- /dev/null +++ b/releasenotes/notes/sampler_v2-be0fc5c29a8e2c14.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adding SamplerV2 implementation based on BaseSamplerV2 using AerSimulator. + This is implemented based on qiskit.primitives.StatevectorSampler, but + this can simulate using any simulation methods other than statevector + by passing AerSimulator object with any methods. diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py new file mode 100644 index 0000000000..286855d3c0 --- /dev/null +++ b/test/terra/primitives/test_sampler_v2.py @@ -0,0 +1,640 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# 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. + +"""Tests for Sampler V2.""" + +from __future__ import annotations + +import unittest +from dataclasses import astuple + +import numpy as np +from numpy.typing import NDArray + +from qiskit import ClassicalRegister, QiskitError, QuantumCircuit, QuantumRegister +from qiskit.circuit import Parameter +from qiskit.circuit.library import RealAmplitudes, UnitaryGate +from qiskit.primitives import PrimitiveResult, PubResult +from qiskit.primitives.containers import BitArray +from qiskit.primitives.containers.data_bin import DataBin +from qiskit.primitives.containers.sampler_pub import SamplerPub +from qiskit.providers import JobStatus +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager + +from test.terra.common import QiskitAerTestCase + +from qiskit_aer import AerSimulator +from qiskit_aer.primitives import SamplerV2 + + +class TestSamplerV2(QiskitAerTestCase): + """Test for SamplerV2""" + + def setUp(self): + super().setUp() + self._shots = 10000 + self._seed = 123 + + self._cases = [] + hadamard = QuantumCircuit(1, 1, name="Hadamard") + hadamard.h(0) + hadamard.measure(0, 0) + self._cases.append((hadamard, None, {0: 5000, 1: 5000})) # case 0 + + bell = QuantumCircuit(2, name="Bell") + bell.h(0) + bell.cx(0, 1) + bell.measure_all() + self._cases.append((bell, None, {0: 5000, 3: 5000})) # case 1 + + self._backend = AerSimulator() + pm = generate_preset_pass_manager(optimization_level=0, backend=self._backend) + + pqc = RealAmplitudes(num_qubits=2, reps=2) + pqc.measure_all() + pqc = pm.run(pqc) + self._cases.append((pqc, [0] * 6, {0: 10000})) # case 2 + self._cases.append((pqc, [1] * 6, {0: 168, 1: 3389, 2: 470, 3: 5973})) # case 3 + self._cases.append((pqc, [0, 1, 1, 2, 3, 5], {0: 1339, 1: 3534, 2: 912, 3: 4215})) # case 4 + self._cases.append((pqc, [1, 2, 3, 4, 5, 6], {0: 634, 1: 291, 2: 6039, 3: 3036})) # case 5 + + pqc2 = RealAmplitudes(num_qubits=2, reps=3) + pqc2.measure_all() + pqc2 = pm.run(pqc2) + self._cases.append( + (pqc2, [0, 1, 2, 3, 4, 5, 6, 7], {0: 1898, 1: 6864, 2: 928, 3: 311}) + ) # case 6 + + def _assert_allclose(self, bitarray: BitArray, target: NDArray | BitArray, rtol=1e-1): + self.assertEqual(bitarray.shape, target.shape) + for idx in np.ndindex(bitarray.shape): + int_counts = bitarray.get_int_counts(idx) + target_counts = ( + target.get_int_counts(idx) if isinstance(target, BitArray) else target[idx] + ) + max_key = max(max(int_counts.keys()), max(target_counts.keys())) + ary = np.array([int_counts.get(i, 0) for i in range(max_key + 1)]) + tgt = np.array([target_counts.get(i, 0) for i in range(max_key + 1)]) + np.testing.assert_allclose(ary, tgt, rtol=rtol, err_msg=f"index: {idx}") + + def test_sampler_run(self): + """Test run().""" + + with self.subTest("single"): + bell, _, target = self._cases[1] + sampler = SamplerV2(seed=self._seed) + job = sampler.run([bell], shots=self._shots) + result = job.result() + self.assertIsInstance(result, PrimitiveResult) + self.assertIsInstance(result.metadata, dict) + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0], PubResult) + self.assertIsInstance(result[0].data, DataBin) + self.assertIsInstance(result[0].data.meas, BitArray) + self._assert_allclose(result[0].data.meas, np.array(target)) + + with self.subTest("single with param"): + pqc, param_vals, target = self._cases[2] + sampler = SamplerV2(seed=self._seed) + params = (param.name for param in pqc.parameters) + job = sampler.run([(pqc, {params: param_vals})], shots=self._shots) + result = job.result() + self.assertIsInstance(result, PrimitiveResult) + self.assertIsInstance(result.metadata, dict) + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0], PubResult) + self.assertIsInstance(result[0].data, DataBin) + self.assertIsInstance(result[0].data.meas, BitArray) + self._assert_allclose(result[0].data.meas, np.array(target)) + + with self.subTest("multiple"): + pqc, param_vals, target = self._cases[2] + sampler = SamplerV2(seed=self._seed) + params = (param.name for param in pqc.parameters) + job = sampler.run( + [(pqc, {params: [param_vals, param_vals, param_vals]})], shots=self._shots + ) + result = job.result() + self.assertIsInstance(result, PrimitiveResult) + self.assertIsInstance(result.metadata, dict) + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0], PubResult) + self.assertIsInstance(result[0].data, DataBin) + self.assertIsInstance(result[0].data.meas, BitArray) + self._assert_allclose(result[0].data.meas, np.array([target, target, target])) + + def test_sampler_run_multiple_times(self): + """Test run() returns the same results if the same input is given.""" + bell, _, _ = self._cases[1] + + sampler = SamplerV2(seed=self._seed) + result1 = sampler.run([bell], shots=self._shots).result() + meas1 = result1[0].data.meas + result2 = sampler.run([bell], shots=self._shots).result() + meas2 = result2[0].data.meas + self._assert_allclose(meas1, meas2, rtol=0) + + def test_sample_run_multiple_circuits(self): + """Test run() with multiple circuits.""" + bell, _, target = self._cases[1] + sampler = SamplerV2(seed=self._seed) + result = sampler.run([bell, bell, bell], shots=self._shots).result() + self.assertEqual(len(result), 3) + self._assert_allclose(result[0].data.meas, np.array(target)) + self._assert_allclose(result[1].data.meas, np.array(target)) + self._assert_allclose(result[2].data.meas, np.array(target)) + + def test_sampler_run_with_parameterized_circuits(self): + """Test run() with parameterized circuits.""" + + pqc1, param1, target1 = self._cases[4] + pqc2, param2, target2 = self._cases[5] + pqc3, param3, target3 = self._cases[6] + + sampler = SamplerV2(seed=self._seed) + result = sampler.run( + [(pqc1, param1), (pqc2, param2), (pqc3, param3)], shots=self._shots + ).result() + self.assertEqual(len(result), 3) + self._assert_allclose(result[0].data.meas, np.array(target1)) + self._assert_allclose(result[1].data.meas, np.array(target2)) + self._assert_allclose(result[2].data.meas, np.array(target3)) + + def test_run_1qubit(self): + """test for 1-qubit cases""" + qc = QuantumCircuit(1) + qc.measure_all() + qc2 = QuantumCircuit(1) + qc2.x(0) + qc2.measure_all() + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([qc, qc2], shots=self._shots).result() + self.assertEqual(len(result), 2) + for i in range(2): + self._assert_allclose(result[i].data.meas, np.array({i: self._shots})) + + def test_run_2qubit(self): + """test for 2-qubit cases""" + qc0 = QuantumCircuit(2) + qc0.measure_all() + qc1 = QuantumCircuit(2) + qc1.x(0) + qc1.measure_all() + qc2 = QuantumCircuit(2) + qc2.x(1) + qc2.measure_all() + qc3 = QuantumCircuit(2) + qc3.x([0, 1]) + qc3.measure_all() + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([qc0, qc1, qc2, qc3], shots=self._shots).result() + self.assertEqual(len(result), 4) + for i in range(4): + self._assert_allclose(result[i].data.meas, np.array({i: self._shots})) + + def test_run_single_circuit(self): + """Test for single circuit case.""" + + with self.subTest("No parameter"): + circuit, _, target = self._cases[1] + param_target = [ + (None, np.array(target)), + ({}, np.array(target)), + ] + for param, target in param_target: + with self.subTest(f"{circuit.name} w/ {param}"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(circuit, param)], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, target) + + with self.subTest("One parameter"): + circuit = QuantumCircuit(1, 1, name="X gate") + param = Parameter("x") + circuit.ry(param, 0) + circuit.measure(0, 0) + param_target = [ + ({"x": np.pi}, np.array({1: self._shots})), + ({param: np.pi}, np.array({1: self._shots})), + ({"x": np.array(np.pi)}, np.array({1: self._shots})), + ({param: np.array(np.pi)}, np.array({1: self._shots})), + ({"x": [np.pi]}, np.array({1: self._shots})), + ({param: [np.pi]}, np.array({1: self._shots})), + ({"x": np.array([np.pi])}, np.array({1: self._shots})), + ({param: np.array([np.pi])}, np.array({1: self._shots})), + ] + for param, target in param_target: + with self.subTest(f"{circuit.name} w/ {param}"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(circuit, param)], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.c, target) + + with self.subTest("More than one parameter"): + circuit, param, target = self._cases[3] + param_target = [ + (param, np.array(target)), + (tuple(param), np.array(target)), + (np.array(param), np.array(target)), + ((param,), np.array([target])), + ([param], np.array([target])), + (np.array([param]), np.array([target])), + ] + for param, target in param_target: + with self.subTest(f"{circuit.name} w/ {param}"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(circuit, param)], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, target) + + def test_run_reverse_meas_order(self): + """test for sampler with reverse measurement order""" + x = Parameter("x") + y = Parameter("y") + + qc = QuantumCircuit(3, 3) + qc.rx(x, 0) + qc.rx(y, 1) + qc.x(2) + qc.measure(0, 2) + qc.measure(1, 1) + qc.measure(2, 0) + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(qc, [0, 0]), (qc, [np.pi / 2, 0])], shots=self._shots).result() + self.assertEqual(len(result), 2) + + # qc({x: 0, y: 0}) + self._assert_allclose(result[0].data.c, np.array({1: self._shots})) + + # qc({x: pi/2, y: 0}) + self._assert_allclose(result[1].data.c, np.array({1: self._shots / 2, 5: self._shots / 2})) + + def test_run_errors(self): + """Test for errors with run method""" + qc1 = QuantumCircuit(1) + qc1.measure_all() + qc2 = RealAmplitudes(num_qubits=1, reps=1) + qc2.measure_all() + qc3 = QuantumCircuit(1, 1) + with qc3.for_loop(range(5)): + qc3.h(0) + + pm = generate_preset_pass_manager(optimization_level=0, backend=self._backend) + qc2 = pm.run(qc2) + + sampler = SamplerV2() + with self.subTest("set parameter values to a non-parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([(qc1, [1e2])]).result() + with self.subTest("missing all parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([qc2]).result() + with self.assertRaises(ValueError): + _ = sampler.run([(qc2, [])]).result() + with self.assertRaises(ValueError): + _ = sampler.run([(qc2, None)]).result() + with self.subTest("missing some parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([(qc2, [1e2])]).result() + with self.subTest("too many parameter values for a parameterized circuit"): + with self.assertRaises(ValueError): + _ = sampler.run([(qc2, [1e2] * 100)]).result() + with self.subTest("with control flow"): + with self.assertRaises(QiskitError): + _ = sampler.run([qc3]).result() + with self.subTest("negative shots, run arg"): + with self.assertRaises(ValueError): + _ = sampler.run([qc1], shots=-1).result() + with self.subTest("negative shots, pub-like"): + with self.assertRaises(ValueError): + _ = sampler.run([(qc1, None, -1)]).result() + with self.subTest("negative shots, pub"): + with self.assertRaises(ValueError): + _ = sampler.run([SamplerPub(qc1, shots=-1)]).result() + with self.subTest("zero shots, run arg"): + with self.assertRaises(ValueError): + _ = sampler.run([qc1], shots=0).result() + with self.subTest("zero shots, pub-like"): + with self.assertRaises(ValueError): + _ = sampler.run([(qc1, None, 0)]).result() + with self.subTest("zero shots, pub"): + with self.assertRaises(ValueError): + _ = sampler.run([SamplerPub(qc1, shots=0)]).result() + with self.subTest("missing []"): + with self.assertRaisesRegex(ValueError, "An invalid Sampler pub-like was given"): + _ = sampler.run(qc1).result() + with self.subTest("missing [] for pqc"): + with self.assertRaisesRegex(ValueError, "Note that if you want to run a single pub,"): + _ = sampler.run((qc2, [0, 1])).result() + + def test_run_empty_parameter(self): + """Test for empty parameter""" + n = 5 + qc = QuantumCircuit(n, n - 1) + qc.measure(range(n - 1), range(n - 1)) + sampler = SamplerV2(seed=self._seed) + with self.subTest("one circuit"): + result = sampler.run([qc], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.c, np.array({0: self._shots})) + + with self.subTest("two circuits"): + result = sampler.run([qc, qc], shots=self._shots).result() + self.assertEqual(len(result), 2) + for i in range(2): + self._assert_allclose(result[i].data.c, np.array({0: self._shots})) + + def test_run_numpy_params(self): + """Test for numpy array as parameter values""" + qc = RealAmplitudes(num_qubits=2, reps=2) + qc.measure_all() + pm = generate_preset_pass_manager(optimization_level=0, backend=self._backend) + qc = pm.run(qc) + k = 5 + params_array = np.linspace(0, 1, k * qc.num_parameters).reshape((k, qc.num_parameters)) + params_list = params_array.tolist() + sampler = SamplerV2(seed=self._seed) + target = sampler.run([(qc, params_list)], shots=self._shots).result() + + with self.subTest("ndarray"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(qc, params_array)], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, target[0].data.meas) + + with self.subTest("split a list"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run( + [(qc, params) for params in params_list], shots=self._shots + ).result() + self.assertEqual(len(result), k) + for i in range(k): + self._assert_allclose( + result[i].data.meas, np.array(target[0].data.meas.get_int_counts(i)) + ) + + def test_run_with_shots_option(self): + """test with shots option.""" + bell, _, _ = self._cases[1] + shots = 100 + + with self.subTest("run arg"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([bell], shots=shots).result() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].data.meas.num_shots, shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), shots) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], shots) + + with self.subTest("default shots"): + sampler = SamplerV2(seed=self._seed) + default_shots = sampler.default_shots + result = sampler.run([bell]).result() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].data.meas.num_shots, default_shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), default_shots) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], default_shots) + + with self.subTest("setting default shots"): + default_shots = 100 + sampler = SamplerV2(default_shots=default_shots, seed=self._seed) + self.assertEqual(sampler.default_shots, default_shots) + result = sampler.run([bell]).result() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].data.meas.num_shots, default_shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), default_shots) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], default_shots) + + with self.subTest("pub-like"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([(bell, None, shots)]).result() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].data.meas.num_shots, shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), shots) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], shots) + + with self.subTest("pub"): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([SamplerPub(bell, shots=shots)]).result() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].data.meas.num_shots, shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), shots) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], shots) + + with self.subTest("multiple pubs"): + sampler = SamplerV2(seed=self._seed) + shots1 = 100 + shots2 = 200 + result = sampler.run( + [ + SamplerPub(bell, shots=shots1), + SamplerPub(bell, shots=shots2), + ], + shots=self._shots, + ).result() + self.assertEqual(len(result), 2) + self.assertEqual(result[0].data.meas.num_shots, shots1) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), shots1) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], shots1) + + self.assertEqual(result[1].data.meas.num_shots, shots2) + self.assertEqual(sum(result[1].data.meas.get_counts().values()), shots2) + self.assertIn("shots", result[1].metadata) + self.assertEqual(result[1].metadata["shots"], shots2) + + def test_run_shots_result_size(self): + """test with shots option to validate the result size""" + n = 10 + qc = QuantumCircuit(n) + qc.h(range(n)) + qc.measure_all() + sampler = SamplerV2(seed=self._seed) + result = sampler.run([qc], shots=self._shots).result() + self.assertEqual(len(result), 1) + self.assertLessEqual(result[0].data.meas.num_shots, self._shots) + self.assertEqual(sum(result[0].data.meas.get_counts().values()), self._shots) + + def test_primitive_job_status_done(self): + """test primitive job's status""" + bell, _, _ = self._cases[1] + sampler = SamplerV2(seed=self._seed) + job = sampler.run([bell], shots=self._shots) + _ = job.result() + self.assertEqual(job.status(), JobStatus.DONE) + + def test_seed(self): + """Test for seed options""" + with self.subTest("empty"): + sampler = SamplerV2() + self.assertIsNone(sampler.seed) + with self.subTest("set int"): + sampler = SamplerV2(seed=self._seed) + self.assertEqual(sampler.seed, self._seed) + with self.subTest("set generator"): + sampler = SamplerV2(seed=np.random.default_rng(self._seed)) + self.assertIsInstance(sampler.seed, np.random.Generator) + + def test_circuit_with_unitary(self): + """Test for circuit with unitary gate.""" + + with self.subTest("identity"): + gate = UnitaryGate(np.eye(2)) + + circuit = QuantumCircuit(1) + circuit.append(gate, [0]) + circuit.measure_all() + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([circuit], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, np.array({0: self._shots})) + + with self.subTest("X"): + gate = UnitaryGate([[0, 1], [1, 0]]) + + circuit = QuantumCircuit(1) + circuit.append(gate, [0]) + circuit.measure_all() + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([circuit], shots=self._shots).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, np.array({1: self._shots})) + + def test_circuit_with_multiple_cregs(self): + """Test for circuit with multiple classical registers.""" + cases = [] + + # case 1 + a = ClassicalRegister(1, "a") + b = ClassicalRegister(2, "b") + c = ClassicalRegister(3, "c") + + qc = QuantumCircuit(QuantumRegister(3), a, b, c) + qc.h(range(3)) + qc.measure([0, 1, 2, 2], [0, 2, 4, 5]) + target = {"a": {0: 5000, 1: 5000}, "b": {0: 5000, 2: 5000}, "c": {0: 5000, 6: 5000}} + cases.append(("use all cregs", qc, target)) + + # case 2 + a = ClassicalRegister(1, "a") + b = ClassicalRegister(5, "b") + c = ClassicalRegister(3, "c") + + qc = QuantumCircuit(QuantumRegister(3), a, b, c) + qc.h(range(3)) + qc.measure([0, 1, 2, 2], [0, 2, 4, 5]) + target = { + "a": {0: 5000, 1: 5000}, + "b": {0: 2500, 2: 2500, 24: 2500, 26: 2500}, + "c": {0: 10000}, + } + cases.append(("use only a and b", qc, target)) + + # case 3 + a = ClassicalRegister(1, "a") + b = ClassicalRegister(2, "b") + c = ClassicalRegister(3, "c") + + qc = QuantumCircuit(QuantumRegister(3), a, b, c) + qc.h(range(3)) + qc.measure(1, 5) + target = {"a": {0: 10000}, "b": {0: 10000}, "c": {0: 5000, 4: 5000}} + cases.append(("use only c", qc, target)) + + # case 4 + a = ClassicalRegister(1, "a") + b = ClassicalRegister(2, "b") + c = ClassicalRegister(3, "c") + + qc = QuantumCircuit(QuantumRegister(3), a, b, c) + qc.h(range(3)) + qc.measure([0, 1, 2], [5, 5, 5]) + target = {"a": {0: 10000}, "b": {0: 10000}, "c": {0: 5000, 4: 5000}} + cases.append(("use only c multiple qubits", qc, target)) + + # case 5 + a = ClassicalRegister(1, "a") + b = ClassicalRegister(2, "b") + c = ClassicalRegister(3, "c") + + qc = QuantumCircuit(QuantumRegister(3), a, b, c) + qc.h(range(3)) + target = {"a": {0: 10000}, "b": {0: 10000}, "c": {0: 10000}} + cases.append(("no measure", qc, target)) + + for title, qc, target in cases: + with self.subTest(title): + sampler = SamplerV2(seed=self._seed) + result = sampler.run([qc], shots=self._shots).result() + self.assertEqual(len(result), 1) + data = result[0].data + self.assertEqual(len(astuple(data)), 3) + for creg in qc.cregs: + self.assertTrue(hasattr(data, creg.name)) + self._assert_allclose(getattr(data, creg.name), np.array(target[creg.name])) + + def test_circuit_with_aliased_cregs(self): + """Test for circuit with aliased classical registers.""" + q = QuantumRegister(3, "q") + c1 = ClassicalRegister(1, "c1") + c2 = ClassicalRegister(1, "c2") + + qc = QuantumCircuit(q, c1, c2) + qc.ry(np.pi / 4, 2) + qc.cx(2, 1) + qc.cx(0, 1) + qc.h(0) + qc.measure(0, c1) + qc.measure(1, c2) + qc.z(2).c_if(c1, 1) + qc.x(2).c_if(c2, 1) + qc2 = QuantumCircuit(5, 5) + qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True) + cregs = [creg.name for creg in qc2.cregs] + target = { + cregs[0]: {0: 4255, 4: 4297, 16: 720, 20: 726}, + cregs[1]: {0: 5000, 1: 5000}, + cregs[2]: {0: 8500, 1: 1500}, + } + + sampler = SamplerV2(seed=self._seed) + result = sampler.run([qc2], shots=self._shots).result() + self.assertEqual(len(result), 1) + data = result[0].data + self.assertEqual(len(astuple(data)), 3) + for creg_name in target: + self.assertTrue(hasattr(data, creg_name)) + self._assert_allclose(getattr(data, creg_name), np.array(target[creg_name])) + + def test_no_cregs(self): + """Test that the sampler works when there are no classical register in the circuit.""" + qc = QuantumCircuit(2) + sampler = SamplerV2() + with self.assertWarns(UserWarning): + result = sampler.run([qc]).result() + + self.assertEqual(len(result), 1) + self.assertEqual(len(result[0].data), 0) + + +if __name__ == "__main__": + unittest.main() From b7eda900f099c12f0dbbf397220bb88f29a22304 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 29 Mar 2024 17:59:05 +0900 Subject: [PATCH 2/9] fix lint error --- qiskit_aer/primitives/sampler_v2.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qiskit_aer/primitives/sampler_v2.py b/qiskit_aer/primitives/sampler_v2.py index 99aeb1f9bc..b68add5dd3 100644 --- a/qiskit_aer/primitives/sampler_v2.py +++ b/qiskit_aer/primitives/sampler_v2.py @@ -16,8 +16,6 @@ from __future__ import annotations -from collections.abc import Sequence - from dataclasses import dataclass from typing import Iterable import warnings @@ -40,7 +38,6 @@ from qiskit.primitives.containers.sampler_pub import SamplerPub from qiskit.primitives.containers.bit_array import _min_num_bytes from qiskit.primitives.primitive_job import PrimitiveJob -from qiskit.primitives.utils import bound_circuit_to_instruction from qiskit_aer import AerSimulator @@ -148,7 +145,7 @@ def _run_pub(self, pub: SamplerPub) -> PubResult: kk = "" for q in qargs: kk = k[circuit.num_qubits - 1 - q] + kk - for iv in range(0, v): + for _ in range(0, v): samples.append(kk) samples_array = np.array( From 0ab7a85edd5e96f42cddfdf8b7502467a7935a25 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 29 Mar 2024 18:15:24 +0900 Subject: [PATCH 3/9] fix test --- test/terra/primitives/test_sampler_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py index 286855d3c0..dddd1d621d 100644 --- a/test/terra/primitives/test_sampler_v2.py +++ b/test/terra/primitives/test_sampler_v2.py @@ -292,7 +292,7 @@ def test_run_errors(self): qc3.h(0) pm = generate_preset_pass_manager(optimization_level=0, backend=self._backend) - qc2 = pm.run(qc2) + qc1, qc2, qc3 = pm.run([qc1, qc2, qc3]) sampler = SamplerV2() with self.subTest("set parameter values to a non-parameterized circuit"): From df621a8baf87a2e5b1eab487a44c61a541115fe1 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 29 Mar 2024 18:46:14 +0900 Subject: [PATCH 4/9] fix test --- test/terra/primitives/test_sampler_v2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py index dddd1d621d..ac0d7daa7e 100644 --- a/test/terra/primitives/test_sampler_v2.py +++ b/test/terra/primitives/test_sampler_v2.py @@ -332,9 +332,6 @@ def test_run_errors(self): with self.subTest("zero shots, pub"): with self.assertRaises(ValueError): _ = sampler.run([SamplerPub(qc1, shots=0)]).result() - with self.subTest("missing []"): - with self.assertRaisesRegex(ValueError, "An invalid Sampler pub-like was given"): - _ = sampler.run(qc1).result() with self.subTest("missing [] for pqc"): with self.assertRaisesRegex(ValueError, "Note that if you want to run a single pub,"): _ = sampler.run((qc2, [0, 1])).result() From 7c4c2b4e4ba8ff291df827cd07fbcebc91bfa59b Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 29 Mar 2024 19:16:38 +0900 Subject: [PATCH 5/9] fix test --- test/terra/primitives/test_sampler_v2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py index ac0d7daa7e..0fe645e263 100644 --- a/test/terra/primitives/test_sampler_v2.py +++ b/test/terra/primitives/test_sampler_v2.py @@ -332,9 +332,6 @@ def test_run_errors(self): with self.subTest("zero shots, pub"): with self.assertRaises(ValueError): _ = sampler.run([SamplerPub(qc1, shots=0)]).result() - with self.subTest("missing [] for pqc"): - with self.assertRaisesRegex(ValueError, "Note that if you want to run a single pub,"): - _ = sampler.run((qc2, [0, 1])).result() def test_run_empty_parameter(self): """Test for empty parameter""" From af09282bdbc318385add1fd7a3ef4953754389e0 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 1 Apr 2024 14:25:51 +0900 Subject: [PATCH 6/9] add options and function from_backend --- qiskit_aer/primitives/estimator_v2.py | 3 ++ qiskit_aer/primitives/sampler_v2.py | 49 +++++++++++++++++++++------ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/qiskit_aer/primitives/estimator_v2.py b/qiskit_aer/primitives/estimator_v2.py index f3a1167270..d438554f3a 100644 --- a/qiskit_aer/primitives/estimator_v2.py +++ b/qiskit_aer/primitives/estimator_v2.py @@ -73,6 +73,9 @@ def __init__( method = "density_matrix" if "noise_model" in self.options.backend_options else "automatic" self._backend = AerSimulator(method=method, **self.options.backend_options) + def from_backend(self, backend, **options): + self._backend.from_backend(backend, **options) + @property def options(self) -> Options: """Return the options""" diff --git a/qiskit_aer/primitives/sampler_v2.py b/qiskit_aer/primitives/sampler_v2.py index b68add5dd3..d0add31aa0 100644 --- a/qiskit_aer/primitives/sampler_v2.py +++ b/qiskit_aer/primitives/sampler_v2.py @@ -22,6 +22,7 @@ import numpy as np from numpy.typing import NDArray +from dataclasses import dataclass, field from qiskit import ClassicalRegister, QiskitError, QuantumCircuit from qiskit.circuit import ControlFlowOp @@ -50,6 +51,17 @@ class _MeasureInfo: qreg_indices: list[int] +@dataclass +class Options: + """Options for :class:`~.SamplerV2`.""" + + backend_options: dict = field(default_factory=dict) + """backend_options: Options passed to AerSimulator.""" + + run_options: dict = field(default_factory=dict) + """run_options: Options passed to run.""" + + class SamplerV2(BaseSamplerV2): """ Aer implementation of SamplerV2 class. @@ -57,29 +69,40 @@ class SamplerV2(BaseSamplerV2): Each tuple of ``(circuit, parameter values, shots)``, called a sampler primitive unified bloc (PUB), produces its own array-valued result. The :meth:`~run` method can be given many pubs at once. + + * ``backend_options``: Options passed to AerSimulator. + Default: {}. + + * ``run_options``: Options passed to :meth:`AerSimulator.run`. + Default: {}. + """ def __init__( self, *, - backend: AerSimulator = None, default_shots: int = 1024, seed: np.random.Generator | int | None = None, + options: dict | None = None, ): """ Args: - backend: AerSimulator object to be used for sampler default_shots: The default shots for the sampler if not specified during run. seed: The seed or Generator object for random number generation. If None, a random seeded default RNG will be used. + options: + the backend options (``backend_options``), and + the runtime options (``run_options``). + """ self._default_shots = default_shots self._seed = seed - if backend is None: - self._backend = AerSimulator() - else: - self._backend = backend + self._options = Options(**options) if options else Options() + self._backend = AerSimulator(**self.options.backend_options) + + def from_backend(self, backend, **options): + self._backend.from_backend(backend, **options) @property def default_shots(self) -> int: @@ -91,6 +114,11 @@ def seed(self) -> np.random.Generator | int | None: """Return the seed or Generator object for random number generation.""" return self._seed + @property + def options(self) -> Options: + """Return the options""" + return self._options + def run( self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None ) -> PrimitiveJob[PrimitiveResult[PubResult]]: @@ -145,12 +173,11 @@ def _run_pub(self, pub: SamplerPub) -> PubResult: kk = "" for q in qargs: kk = k[circuit.num_qubits - 1 - q] + kk - for _ in range(0, v): - samples.append(kk) - samples_array = np.array( - [np.fromiter(sample, dtype=np.uint8) for sample in samples] - ) + samples.append((np.fromiter(kk, dtype=np.uint8), v)) + + samples_array = np.array([sample for sample, v in samples for _ in range(0, v)]) + for item in meas_info: ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) arrays[item.creg_name][index] = ary From 7f0317130202233ffb45074c28b8e67c8d0c9476 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 1 Apr 2024 14:30:09 +0900 Subject: [PATCH 7/9] lint --- qiskit_aer/primitives/estimator_v2.py | 1 + qiskit_aer/primitives/sampler_v2.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit_aer/primitives/estimator_v2.py b/qiskit_aer/primitives/estimator_v2.py index d438554f3a..a4bdb4da68 100644 --- a/qiskit_aer/primitives/estimator_v2.py +++ b/qiskit_aer/primitives/estimator_v2.py @@ -74,6 +74,7 @@ def __init__( self._backend = AerSimulator(method=method, **self.options.backend_options) def from_backend(self, backend, **options): + """use external backend""" self._backend.from_backend(backend, **options) @property diff --git a/qiskit_aer/primitives/sampler_v2.py b/qiskit_aer/primitives/sampler_v2.py index d0add31aa0..4f8c8f52a2 100644 --- a/qiskit_aer/primitives/sampler_v2.py +++ b/qiskit_aer/primitives/sampler_v2.py @@ -16,13 +16,12 @@ from __future__ import annotations -from dataclasses import dataclass from typing import Iterable +from dataclasses import dataclass, field import warnings import numpy as np from numpy.typing import NDArray -from dataclasses import dataclass, field from qiskit import ClassicalRegister, QiskitError, QuantumCircuit from qiskit.circuit import ControlFlowOp @@ -102,6 +101,7 @@ def __init__( self._backend = AerSimulator(**self.options.backend_options) def from_backend(self, backend, **options): + """use external backend""" self._backend.from_backend(backend, **options) @property From 1a8bc417e5f2b114f39c6a383880e5fd4c9fb584 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 1 Apr 2024 14:55:30 +0900 Subject: [PATCH 8/9] build test --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b79723976..62c168315f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -183,7 +183,7 @@ jobs: - name: Build wheels 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_BEFORE_BUILD : "pip install nvidia-cuda-runtime-cu11 nvidia-cublas-cu11 nvidia-cusolver-cu11 nvidia-cusparse-cu11 cutensor-cu11 cuquantum-cu11" CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* cp38* cp39* cp310* cp311* *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}' From 926627a0b50d9c792ff949f82f43522bf9561a75 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 1 Apr 2024 15:22:51 +0900 Subject: [PATCH 9/9] build test --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62c168315f..3def5c4b0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -183,7 +183,7 @@ jobs: - name: Build wheels 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 cutensor-cu11 cuquantum-cu11" + CIBW_BEFORE_BUILD : "pip cache purge && pip install nvidia-cuda-runtime-cu11 nvidia-cublas-cu11 nvidia-cusolver-cu11 nvidia-cusparse-cu11 cuquantum-cu11" CIBW_SKIP: "*-manylinux_i686 pp* cp36* cp37* cp38* cp39* cp310* cp311* *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}'