From 639e7385248fd5aaba0356a7df94fae41f2eaba6 Mon Sep 17 00:00:00 2001 From: gadial Date: Thu, 22 Feb 2024 09:50:09 +0200 Subject: [PATCH 1/9] Clean version of SamplerV2 update --- qiskit_ibm_runtime/sampler.py | 23 +- test/integration/test_sampler_v2.py | 596 ++++++++++++++++++++++++++++ 2 files changed, 605 insertions(+), 14 deletions(-) create mode 100644 test/integration/test_sampler_v2.py diff --git a/qiskit_ibm_runtime/sampler.py b/qiskit_ibm_runtime/sampler.py index c1129b406..ad9d78bf7 100644 --- a/qiskit_ibm_runtime/sampler.py +++ b/qiskit_ibm_runtime/sampler.py @@ -14,7 +14,7 @@ from __future__ import annotations import os -from typing import Dict, Optional, Sequence, Any, Union, Iterable +from typing import Dict, Optional, Sequence, Any, Union, Iterable, List import logging import warnings @@ -42,7 +42,7 @@ class Sampler: version = 0 -class SamplerV2(BasePrimitiveV2[SamplerOptions], Sampler, BaseSamplerV2): +class SamplerV2(BasePrimitiveV2, Sampler, BaseSamplerV2): """Class for interacting with Qiskit Runtime Sampler primitive service. This class supports version 2 of the Sampler interface, which uses different @@ -90,8 +90,6 @@ def __init__( Sampler.__init__(self) BasePrimitiveV2.__init__(self, backend=backend, session=session, options=options) - raise NotImplementedError("SamplerV2 is not currently supported.") - # if self._service._channel_strategy == "q-ctrl": # raise NotImplementedError("SamplerV2 is not supported with q-ctrl channel strategy.") @@ -107,7 +105,11 @@ def run(self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None) -> Ru Returns: Submitted job. + The result of the job is an instance of + :class:`qiskit.primitives.containers.PrimitiveResult`. + Raises: + ValueError: Invalid arguments are given. """ coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs] @@ -148,23 +150,16 @@ class SamplerV1(BasePrimitiveV1, Sampler, BaseSampler): Example:: - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister + from qiskit.test.reference_circuits import ReferenceCircuits from qiskit_ibm_runtime import QiskitRuntimeService, Session, Sampler service = QiskitRuntimeService(channel="ibm_cloud") - - # Bell Circuit - qr = QuantumRegister(2, name="qr") - cr = ClassicalRegister(2, name="cr") - qc = QuantumCircuit(qr, cr, name="bell") - qc.h(qr[0]) - qc.cx(qr[0], qr[1]) - qc.measure(qr, cr) + bell = ReferenceCircuits.bell() with Session(service, backend="ibmq_qasm_simulator") as session: sampler = Sampler(session=session) - job = sampler.run(qc, shots=1024) + job = sampler.run(bell, shots=1024) print(f"Job ID: {job.job_id()}") print(f"Job result: {job.result()}") diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py new file mode 100644 index 000000000..2b7fdbbe4 --- /dev/null +++ b/test/integration/test_sampler_v2.py @@ -0,0 +1,596 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023, 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, SamplerPub +from qiskit.primitives.containers import BitArray +from qiskit.primitives.containers.data_bin import DataBin +from qiskit.providers import JobStatus + +from qiskit_ibm_runtime import Session +from qiskit_ibm_runtime import SamplerV2 as Sampler +from ..decorators import run_integration_test +from ..ibm_test_case import IBMIntegrationTestCase +class TestSampler(IBMIntegrationTestCase): + """Test Sampler""" + + def setUp(self): + super().setUp() + self.backend = "ibmq_qasm_simulator" + self._shots = 10000 + self._options = {"shots": 10000, "seed_simulator": 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 + + pqc = RealAmplitudes(num_qubits=2, reps=2) + pqc.measure_all() + 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() + 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}") + + @run_integration_test + def test_sampler_run(self, service): + """Test Sampler.run().""" + with Session(service, self.backend) as session: + bell, _, target = self._cases[1] + + with self.subTest("single"): + sampler = Sampler(session=session, options=self._options) + job = sampler.run([bell]) + 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"): + sampler = Sampler(session=session, options=self._options) + job = sampler.run([(bell, ())]) + 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 array"): + sampler = Sampler(session=session, options=self._options) + job = sampler.run([(bell, [()])]) + 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"): + sampler = Sampler(session=session, options=self._options) + job = sampler.run([(bell, [(), (), ()])]) + 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])) + + @run_integration_test + def test_sample_run_multiple_circuits(self, service): + """Test Sampler.run() with multiple circuits.""" + with Session(service, self.backend) as session: + bell, _, target = self._cases[1] + sampler = Sampler(session=session, options=self._options) + result = sampler.run([bell, bell, bell]).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)) + + @run_integration_test + def test_sampler_run_with_parameterized_circuits(self, service): + """Test Sampler.run() with parameterized circuits.""" + with Session(service, self.backend) as session: + pqc1, param1, target1 = self._cases[4] + pqc2, param2, target2 = self._cases[5] + pqc3, param3, target3 = self._cases[6] + + sampler = Sampler(session=session, options=self._options) + result = sampler.run([(pqc1, param1), (pqc2, param2), (pqc3, param3)]).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)) + + @run_integration_test + def test_run_1qubit(self, service): + """test for 1-qubit cases""" + with Session(service, self.backend) as session: + qc = QuantumCircuit(1) + qc.measure_all() + qc2 = QuantumCircuit(1) + qc2.x(0) + qc2.measure_all() + + sampler = Sampler(session=session, options=self._options) + result = sampler.run([qc, qc2]).result() + self.assertEqual(len(result), 2) + for i in range(2): + self._assert_allclose(result[i].data.meas, np.array({i: self._shots})) + + @run_integration_test + def test_run_2qubit(self, service): + """test for 2-qubit cases""" + with Session(service, self.backend) as session: + 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 = Sampler(session=session, options=self._options) + result = sampler.run([qc0, qc1, qc2, qc3]).result() + self.assertEqual(len(result), 4) + for i in range(4): + self._assert_allclose(result[i].data.meas, np.array({i: self._shots})) + + @run_integration_test + def test_run_single_circuit(self, service): + """Test for single circuit case.""" + with Session(service, self.backend) as session: + sampler = Sampler(session=session, options=self._options) + + with self.subTest("No parameter"): + circuit, _, target = self._cases[1] + param_target = [ + (None, np.array(target)), + ((), np.array(target)), + ([], np.array(target)), + (np.array([]), np.array(target)), + (((),), np.array([target])), + (([],), np.array([target])), + ([[]], np.array([target])), + ([()], np.array([target])), + (np.array([[]]), np.array([target])), + ] + for param, target in param_target: + with self.subTest(f"{circuit.name} w/ {param}"): + result = sampler.run([(circuit, param)]).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 = [ + ([np.pi], np.array({1: self._shots})), + ((np.pi,), np.array({1: self._shots})), + (np.array([np.pi]), np.array({1: self._shots})), + ([[np.pi]], np.array([{1: self._shots}])), + (((np.pi,),), np.array([{1: self._shots}])), + (np.array([[np.pi]]), np.array([{1: self._shots}])), + ] + for param, target in param_target: + with self.subTest(f"{circuit.name} w/ {param}"): + result = sampler.run([(circuit, param)]).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}"): + result = sampler.run([(circuit, param)]).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, target) + + @run_integration_test + def test_run_reverse_meas_order(self, service): + """test for sampler with reverse measurement order""" + with Session(service, self.backend) as session: + 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 = Sampler(session=session, options=self._options) + result = sampler.run([(qc, [0, 0]), (qc, [np.pi / 2, 0])]).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})) + + @run_integration_test + def test_run_errors(self, service): + """Test for errors with run method""" + with Session(service, self.backend) as session: + qc1 = QuantumCircuit(1) + qc1.measure_all() + qc2 = RealAmplitudes(num_qubits=1, reps=1) + qc2.measure_all() + qc3 = QuantumCircuit(1) + qc4 = QuantumCircuit(1, 1) + with qc4.for_loop(range(5)): + qc4.h(0) + + sampler = Sampler(session=session, options=self._options) + 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("no classical bits"): + with self.assertRaises(ValueError): + _ = sampler.run([qc3]).result() + with self.subTest("with control flow"): + with self.assertRaises(QiskitError): + _ = sampler.run([qc4]).result() + + @run_integration_test + def test_run_empty_parameter(self, service): + """Test for empty parameter""" + with Session(service, self.backend) as session: + n = 5 + qc = QuantumCircuit(n, n - 1) + qc.measure(range(n - 1), range(n - 1)) + sampler = Sampler(session=session, options=self._options) + with self.subTest("one circuit"): + result = sampler.run([qc]).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]).result() + self.assertEqual(len(result), 2) + for i in range(2): + self._assert_allclose(result[i].data.c, np.array({0: self._shots})) + + @run_integration_test + def test_run_numpy_params(self, service): + """Test for numpy array as parameter values""" + with Session(service, self.backend) as session: + qc = RealAmplitudes(num_qubits=2, reps=2) + qc.measure_all() + k = 5 + params_array = np.random.rand(k, qc.num_parameters) + params_list = params_array.tolist() + sampler = Sampler(session=session, options=self._options) + target = sampler.run([(qc, params_list)]).result() + + with self.subTest("ndarray"): + result = sampler.run([(qc, params_array)]).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, target[0].data.meas) + + with self.subTest("split a list"): + result = sampler.run([(qc, params) for params in params_list]).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)) + ) + + @run_integration_test + def test_run_with_shots_option(self, service): + """test with shots option.""" + with Session(service, self.backend) as session: + bell, _, _ = self._cases[1] + shots = 100 + + with self.subTest("init option"): + sampler = Sampler(session=session, options={"shots": shots}) + result = sampler.run([bell]).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) + + with self.subTest("update option"): + sampler = Sampler(session=session) + sampler.options.shots = shots + result = sampler.run([bell]).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) + + with self.subTest("run arg"): + sampler = Sampler(session=session) + result = sampler.run([bell], 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) + + with self.subTest("run arg"): + sampler = Sampler(session=session) + result = sampler.run([bell], 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) + + with self.subTest("pub-like"): + sampler = Sampler(session=session) + 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) + + with self.subTest("pub"): + sampler = Sampler(session=session) + 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) + + with self.subTest("multiple pubs"): + sampler = Sampler() + shots1 = 100 + shots2 = 200 + result = sampler.run( + [ + SamplerPub(bell, shots=shots1), + SamplerPub(bell, shots=shots2), + ] + ).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.assertEqual(result[1].data.meas.num_shots, shots2) + self.assertEqual(sum(result[1].data.meas.get_counts().values()), shots2) + + @run_integration_test + def test_run_shots_result_size(self, service): + """test with shots option to validate the result size""" + with Session(service, self.backend) as session: + n = 10 + qc = QuantumCircuit(n) + qc.h(range(n)) + qc.measure_all() + sampler = Sampler(session=session, options=self._options) + result = sampler.run([qc]).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) + + @run_integration_test + def test_primitive_job_status_done(self, service): + """test primitive job's status""" + with Session(service, self.backend) as session: + bell, _, _ = self._cases[1] + sampler = Sampler(session=session, options=self._options) + job = sampler.run([bell]) + _ = job.result() + self.assertEqual(job.status(), JobStatus.DONE) + + @run_integration_test + def test_options(self, service): + """Test for options""" + with Session(service, self.backend) as session: + with self.subTest("init"): + sampler = Sampler(session=session, options={"shots": 3000}) + self.assertEqual(sampler.options.shots, 3000) + with self.subTest("set options"): + sampler.options.shots = 1024 + sampler.options.seed = 15 + self.assertEqual(sampler.options.shots, 1024) + self.assertEqual(sampler.options.seed, 15) + with self.subTest("update options"): + sampler.options.update({"shots": 100, "seed": 12}) + self.assertEqual(sampler.options.shots, 100) + self.assertEqual(sampler.options.seed, 12) + + @run_integration_test + def test_circuit_with_unitary(self, service): + """Test for circuit with unitary gate.""" + with Session(service, self.backend) as session: + with self.subTest("identity"): + gate = UnitaryGate(np.eye(2)) + + circuit = QuantumCircuit(1) + circuit.append(gate, [0]) + circuit.measure_all() + + sampler = Sampler(session=session, options=self._options) + result = sampler.run([circuit]).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 = Sampler(session=session, options=self._options) + result = sampler.run([circuit]).result() + self.assertEqual(len(result), 1) + self._assert_allclose(result[0].data.meas, np.array({1: self._shots})) + + @run_integration_test + def test_metadata(self, service): + """Test for metatdata.""" + qc, _, _ = self._cases[1] + with Session(service, self.backend) as session: + sampler = Sampler(session=session, options=self._options) + result = sampler.run([qc]).result() + self.assertEqual(len(result), 1) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], self._shots) + + shots = 100 + sampler.options.shots = 100 + result = sampler.run([qc]).result() + self.assertEqual(len(result), 1) + self.assertIn("shots", result[0].metadata) + self.assertEqual(result[0].metadata["shots"], shots) + + @run_integration_test + def test_circuit_with_multiple_cregs(self, service): + """Test for circuit with multiple classical registers.""" + with Session(service, self.backend) as session: + 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 = Sampler(session=session, options=self._options) + result = sampler.run([qc]).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])) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 85fb293bb8dd8c53684b6f72df9ffbf65777efbf Mon Sep 17 00:00:00 2001 From: gadial Date: Thu, 22 Feb 2024 09:57:34 +0200 Subject: [PATCH 2/9] Added SamplerV2 to primitives_v2 unit tests --- test/integration/test_sampler_v2.py | 3 +- test/unit/test_ibm_primitives_v2.py | 64 ++++++++++++++--------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 2b7fdbbe4..9ef8cb7a2 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -23,9 +23,10 @@ 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, SamplerPub +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_ibm_runtime import Session diff --git a/test/unit/test_ibm_primitives_v2.py b/test/unit/test_ibm_primitives_v2.py index 4f2030d92..4c67bca5c 100644 --- a/test/unit/test_ibm_primitives_v2.py +++ b/test/unit/test_ibm_primitives_v2.py @@ -33,7 +33,7 @@ ) from qiskit_ibm_runtime.ibm_backend import IBMBackend from qiskit_ibm_runtime.utils.default_session import _DEFAULT_SESSION -from qiskit_ibm_runtime import EstimatorV2 +from qiskit_ibm_runtime import EstimatorV2, SamplerV2 from qiskit_ibm_runtime.estimator import Estimator as IBMBaseEstimator from qiskit_ibm_runtime.fake_provider import FakeManila @@ -67,7 +67,7 @@ def tearDown(self) -> None: super().tearDown() _DEFAULT_SESSION.set(None) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_dict_options(self, primitive): """Test passing a dictionary as options.""" options_vars = [ @@ -84,7 +84,7 @@ def test_dict_options(self, primitive): self.assertTrue(dict_paritally_equal(asdict(inst.options), options)) @combine( - primitive=[EstimatorV2], + primitive=[EstimatorV2, SamplerV2], env_var=[ {"log_level": "DEBUG"}, {"job_tags": ["foo", "bar"]}, @@ -101,7 +101,7 @@ def test_runtime_options(self, primitive, env_var): self.assertEqual(run_options[key], val) @combine( - primitive=[EstimatorV2], + primitive=[EstimatorV2, SamplerV2], opts=[ {"experimental": {"image": "foo:bar"}}, {"experimental": {"image": "foo:bar"}, "environment": {"log_level": "INFO"}}, @@ -120,7 +120,7 @@ def test_image(self, primitive, opts): self.assertEqual(run_options[key], val) self.assertNotIn(key, input_params) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_options_copied(self, primitive): """Test modifying original options does not affect primitives.""" options = primitive._options_class() @@ -129,7 +129,7 @@ def test_options_copied(self, primitive): options.max_execution_time = 200 self.assertEqual(inst.options.max_execution_time, 100) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_backend_str(self, primitive): """Test initializing a primitive with a backend name.""" backend_name = "ibm_gotham" @@ -151,7 +151,7 @@ def test_init_with_backend_str(self, primitive): runtime_options = mock_service_inst.run.call_args.kwargs["options"] self.assertEqual(runtime_options["backend"], backend_name) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_session_backend_str(self, primitive): """Test initializing a primitive with a backend name using session.""" backend_name = "ibm_gotham" @@ -162,7 +162,7 @@ def test_init_with_session_backend_str(self, primitive): self.assertIsNone(inst.session) self.assertIn("session must be of type Session or None", str(exc.exception)) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_backend_instance(self, primitive): """Test initializing a primitive with a backend instance.""" service = MagicMock() @@ -185,7 +185,7 @@ def test_init_with_backend_instance(self, primitive): self.assertIsNone(inst.session) self.assertIn("session must be of type Session or None", str(exc.exception)) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_backend_session(self, primitive): """Test initializing a primitive with both backend and session.""" session = MagicMock(spec=MockSession) @@ -197,7 +197,7 @@ def test_init_with_backend_session(self, primitive): inst.run(**get_primitive_inputs(inst)) session.run.assert_called_once() - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_no_backend_session_cloud(self, primitive): """Test initializing a primitive without backend or session for cloud channel.""" with patch("qiskit_ibm_runtime.base_primitive.QiskitRuntimeService") as mock_service: @@ -210,7 +210,7 @@ def test_init_with_no_backend_session_cloud(self, primitive): mock_service.assert_called_once() self.assertIsNone(inst.session) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_init_with_no_backend_session_quantum(self, primitive): """Test initializing a primitive without backend or session for quantum channel.""" @@ -219,7 +219,7 @@ def test_init_with_no_backend_session_quantum(self, primitive): with self.assertRaises(ValueError): _ = primitive() - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_default_session_context_manager(self, primitive): """Test getting default session within context manager.""" service = MagicMock() @@ -230,7 +230,7 @@ def test_default_session_context_manager(self, primitive): self.assertEqual(inst.session, session) self.assertEqual(inst.session.backend(), backend) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_default_session_cm_new_backend(self, primitive): """Test using a different backend within context manager.""" cm_backend = "ibm_metropolis" @@ -251,7 +251,7 @@ def test_default_session_cm_new_backend(self, primitive): runtime_options = service.run.call_args.kwargs["options"] self.assertEqual(runtime_options["backend"], backend.name) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_no_session(self, primitive): """Test running without session.""" model_backend = FakeManila() @@ -269,7 +269,7 @@ def test_no_session(self, primitive): self.assertNotIn("session_id", kwargs_list) self.assertNotIn("start_session", kwargs_list) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_parameters_single_circuit(self, primitive): """Test parameters for a single cirucit.""" @@ -300,7 +300,7 @@ def test_parameters_single_circuit(self, primitive): pub = (circ, "ZZ", val) if isinstance(inst, EstimatorV2) else (circ, val) inst.run([pub]) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_parameters_vals_kwvals(self, primitive): """Test a mixture of vals and kwvals.""" circ = RealAmplitudes(num_qubits=2, reps=1) @@ -317,7 +317,7 @@ def test_parameters_vals_kwvals(self, primitive): pub = (circ, "ZZ", barray) if isinstance(inst, EstimatorV2) else (circ, barray) inst.run([pub]) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_parameters_multiple_circuits(self, primitive): """Test multiple parameters for multiple circuits.""" circuits = [ @@ -352,7 +352,7 @@ def test_parameters_multiple_circuits(self, primitive): pubs.append(publet) inst.run(pubs) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_run_updated_options(self, primitive): """Test run using overwritten options.""" session = MagicMock(spec=MockSession) @@ -377,7 +377,7 @@ def test_run_updated_options(self, primitive): inputs = session.run.call_args.kwargs["inputs"] self._assert_dict_partially_equal(inputs, expected) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_run_overwrite_runtime_options(self, primitive): """Test run using overwritten runtime options.""" session = MagicMock(spec=MockSession) @@ -396,7 +396,7 @@ def test_run_overwrite_runtime_options(self, primitive): self._assert_dict_partially_equal(rt_options, options) @combine( - primitive=[EstimatorV2], + primitive=[EstimatorV2, SamplerV2], exp_opt=[{"foo": "bar"}, {"transpilation": {"extra_key": "bar"}}], ) def test_run_experimental_options(self, primitive, exp_opt): @@ -410,7 +410,7 @@ def test_run_experimental_options(self, primitive, exp_opt): self.assertNotIn("extra_key", inputs) @combine( - primitive=[EstimatorV2], + primitive=[EstimatorV2, SamplerV2], exp_opt=[{"foo": "bar"}, {"execution": {"extra_key": "bar"}}], ) def test_run_experimental_options_init(self, primitive, exp_opt): @@ -422,7 +422,7 @@ def test_run_experimental_options_init(self, primitive, exp_opt): self._assert_dict_partially_equal(inputs, exp_opt) self.assertNotIn("extra_key", inputs) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_run_unset_options(self, primitive): """Test running with unset options.""" session = MagicMock(spec=MockSession) @@ -434,7 +434,7 @@ def test_run_unset_options(self, primitive): expected = {"skip_transpilation": False, "execution": {"init_qubits": True}, "version": 2} self.assertDictEqual(inputs, expected) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_run_multiple_different_options(self, primitive): """Test multiple runs with different options.""" session = MagicMock(spec=MockSession) @@ -449,7 +449,7 @@ def test_run_multiple_different_options(self, primitive): def test_run_same_session(self): """Test multiple runs within a session.""" num_runs = 5 - primitives = [EstimatorV2] + primitives = [EstimatorV2, SamplerV2] session = MagicMock(spec=MockSession) for idx in range(num_runs): cls = primitives[idx % len(primitives)] @@ -458,7 +458,7 @@ def test_run_same_session(self): self.assertEqual(session.run.call_count, num_runs) @combine( - primitive=[EstimatorV2], + primitive=[EstimatorV2, SamplerV2], new_opts=[ {"optimization_level": 0}, {"optimization_level": 1, "shots": 200}, @@ -484,7 +484,7 @@ def test_set_options(self, primitive, new_opts): f"inst_options={inst_options}, original={opt_cls()}", ) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_accept_level_1_options(self, primitive): """Test initializing options properly when given on level 1.""" @@ -575,7 +575,7 @@ def test_default_error_levels(self): ) self.assertEqual(inputs["resilience_level"], 0) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_raise_faulty_qubits(self, primitive): """Test faulty qubits is raised.""" fake_backend = FakeManila() @@ -604,7 +604,7 @@ def test_raise_faulty_qubits(self, primitive): inst.run(pubs=[pub]) self.assertIn(f"faulty qubit {faulty_qubit}", str(err.exception)) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_raise_faulty_qubits_many(self, primitive): """Test faulty qubits is raised if one circuit uses it.""" fake_backend = FakeManila() @@ -636,7 +636,7 @@ def test_raise_faulty_qubits_many(self, primitive): inst.run(pubs=pubs) self.assertIn(f"faulty qubit {faulty_qubit}", str(err.exception)) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_raise_faulty_edge(self, primitive): """Test faulty edge is raised.""" fake_backend = FakeManila() @@ -665,7 +665,7 @@ def test_raise_faulty_edge(self, primitive): self.assertIn("cx", str(err.exception)) self.assertIn(f"faulty edge {tuple(edge_qubits)}", str(err.exception)) - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_faulty_qubit_not_used(self, primitive): """Test faulty qubit is not raise if not used.""" fake_backend = FakeManila() @@ -693,7 +693,7 @@ def test_faulty_qubit_not_used(self, primitive): inst.run([pub]) mock_run.assert_called_once() - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_faulty_edge_not_used(self, primitive): """Test faulty edge is not raised if not used.""" fake_backend = FakeManila() @@ -723,7 +723,7 @@ def test_faulty_edge_not_used(self, primitive): inst.run([pub]) mock_run.assert_called_once() - @data(EstimatorV2) + @data(EstimatorV2, SamplerV2) def test_no_raise_skip_transpilation(self, primitive): """Test faulty qubits and edges are not raise if not skipping.""" fake_backend = FakeManila() From 02d216cc6bc7511935671f5b5bd39d85b11792cb Mon Sep 17 00:00:00 2001 From: gadial Date: Thu, 22 Feb 2024 14:11:48 +0200 Subject: [PATCH 3/9] linting --- test/integration/test_sampler_v2.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 9ef8cb7a2..4c50f7fda 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -33,6 +33,8 @@ from qiskit_ibm_runtime import SamplerV2 as Sampler from ..decorators import run_integration_test from ..ibm_test_case import IBMIntegrationTestCase + + class TestSampler(IBMIntegrationTestCase): """Test Sampler""" @@ -281,7 +283,9 @@ def test_run_reverse_meas_order(self, service): 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})) + self._assert_allclose( + result[1].data.c, np.array({1: self._shots / 2, 5: self._shots / 2}) + ) @run_integration_test def test_run_errors(self, service): @@ -594,4 +598,4 @@ def test_circuit_with_multiple_cregs(self, service): if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() From c0fb6a4265e8a301b9d6b9c347627545214c4af6 Mon Sep 17 00:00:00 2001 From: gadial Date: Thu, 22 Feb 2024 14:40:09 +0200 Subject: [PATCH 4/9] linting --- qiskit_ibm_runtime/sampler.py | 2 +- test/integration/test_sampler_v2.py | 46 ++++------------------------- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/qiskit_ibm_runtime/sampler.py b/qiskit_ibm_runtime/sampler.py index ad9d78bf7..cbec5f54f 100644 --- a/qiskit_ibm_runtime/sampler.py +++ b/qiskit_ibm_runtime/sampler.py @@ -14,7 +14,7 @@ from __future__ import annotations import os -from typing import Dict, Optional, Sequence, Any, Union, Iterable, List +from typing import Dict, Optional, Sequence, Any, Union, Iterable import logging import warnings diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 4c50f7fda..3851ee7e1 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -11,6 +11,7 @@ # that they have been altered from the originals. """Tests for Sampler V2.""" +# pylint: disable=invalid-name from __future__ import annotations @@ -20,7 +21,7 @@ import numpy as np from numpy.typing import NDArray -from qiskit import ClassicalRegister, QiskitError, QuantumCircuit, QuantumRegister +from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.circuit import Parameter from qiskit.circuit.library import RealAmplitudes, UnitaryGate from qiskit.primitives import PrimitiveResult, PubResult @@ -76,7 +77,7 @@ def _assert_allclose(self, bitarray: BitArray, target: NDArray | BitArray, rtol= 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())) + max_key = max(int_counts.keys(), 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}") @@ -287,43 +288,6 @@ def test_run_reverse_meas_order(self, service): result[1].data.c, np.array({1: self._shots / 2, 5: self._shots / 2}) ) - @run_integration_test - def test_run_errors(self, service): - """Test for errors with run method""" - with Session(service, self.backend) as session: - qc1 = QuantumCircuit(1) - qc1.measure_all() - qc2 = RealAmplitudes(num_qubits=1, reps=1) - qc2.measure_all() - qc3 = QuantumCircuit(1) - qc4 = QuantumCircuit(1, 1) - with qc4.for_loop(range(5)): - qc4.h(0) - - sampler = Sampler(session=session, options=self._options) - 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("no classical bits"): - with self.assertRaises(ValueError): - _ = sampler.run([qc3]).result() - with self.subTest("with control flow"): - with self.assertRaises(QiskitError): - _ = sampler.run([qc4]).result() - @run_integration_test def test_run_empty_parameter(self, service): """Test for empty parameter""" @@ -392,14 +356,14 @@ def test_run_with_shots_option(self, service): with self.subTest("run arg"): sampler = Sampler(session=session) - result = sampler.run([bell], shots).result() + result = sampler.run(pubs=[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) with self.subTest("run arg"): sampler = Sampler(session=session) - result = sampler.run([bell], shots).result() + result = sampler.run(pubs=[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) From b921438e142d65e870c49c065f4ee6624a0227e0 Mon Sep 17 00:00:00 2001 From: gadial Date: Thu, 22 Feb 2024 14:48:58 +0200 Subject: [PATCH 5/9] linting --- test/integration/test_sampler_v2.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 3851ee7e1..59e30b3b2 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -70,7 +70,9 @@ def setUp(self): (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): + def _assert_allclose( + self, bitarray: BitArray, target: NDArray | BitArray, rtol: float = 1e-1 + ) -> None: self.assertEqual(bitarray.shape, target.shape) for idx in np.ndindex(bitarray.shape): int_counts = bitarray.get_int_counts(idx) From e2ce0a81bb9c867c937b61fc47f80c7f51140395 Mon Sep 17 00:00:00 2001 From: gadial Date: Fri, 23 Feb 2024 17:31:04 +0200 Subject: [PATCH 6/9] Minor fixes in integration tests --- test/integration/test_sampler_v2.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 59e30b3b2..3f6f6ed61 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -21,7 +21,7 @@ import numpy as np from numpy.typing import NDArray -from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister +from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister, transpile from qiskit.circuit import Parameter from qiskit.circuit.library import RealAmplitudes, UnitaryGate from qiskit.primitives import PrimitiveResult, PubResult @@ -32,6 +32,7 @@ from qiskit_ibm_runtime import Session from qiskit_ibm_runtime import SamplerV2 as Sampler +from qiskit_ibm_runtime.fake_provider import FakeManila from ..decorators import run_integration_test from ..ibm_test_case import IBMIntegrationTestCase @@ -42,6 +43,7 @@ class TestSampler(IBMIntegrationTestCase): def setUp(self): super().setUp() self.backend = "ibmq_qasm_simulator" + self.fake_backend = FakeManila() self._shots = 10000 self._options = {"shots": 10000, "seed_simulator": 123} @@ -59,6 +61,7 @@ def setUp(self): pqc = RealAmplitudes(num_qubits=2, reps=2) pqc.measure_all() + pqc = transpile(circuits=pqc, backend=self.fake_backend) 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 @@ -66,6 +69,7 @@ def setUp(self): pqc2 = RealAmplitudes(num_qubits=2, reps=3) pqc2.measure_all() + pqc2 = transpile(circuits=pqc2, backend=self.fake_backend) self._cases.append( (pqc2, [0, 1, 2, 3, 4, 5, 6, 7], {0: 1898, 1: 6864, 2: 928, 3: 311}) ) # case 6 @@ -79,7 +83,7 @@ def _assert_allclose( target_counts = ( target.get_int_counts(idx) if isinstance(target, BitArray) else target[idx] ) - max_key = max(int_counts.keys(), target_counts.keys()) + max_key = max(max(int_counts.keys()), max(target_counts.keys())) # pylint: disable=nested-min-max 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}") @@ -315,6 +319,7 @@ def test_run_numpy_params(self, service): with Session(service, self.backend) as session: qc = RealAmplitudes(num_qubits=2, reps=2) qc.measure_all() + qc = transpile(circuits=qc, backend=self.fake_backend) k = 5 params_array = np.random.rand(k, qc.num_parameters) params_list = params_array.tolist() @@ -429,15 +434,13 @@ def test_options(self, service): """Test for options""" with Session(service, self.backend) as session: with self.subTest("init"): - sampler = Sampler(session=session, options={"shots": 3000}) - self.assertEqual(sampler.options.shots, 3000) + sampler = Sampler(session=session, options={"seed": 42}) + self.assertEqual(sampler.options.seed, 42) with self.subTest("set options"): - sampler.options.shots = 1024 sampler.options.seed = 15 - self.assertEqual(sampler.options.shots, 1024) self.assertEqual(sampler.options.seed, 15) with self.subTest("update options"): - sampler.options.update({"shots": 100, "seed": 12}) + sampler.options.update(seed=12) self.assertEqual(sampler.options.shots, 100) self.assertEqual(sampler.options.seed, 12) @@ -477,15 +480,7 @@ def test_metadata(self, service): sampler = Sampler(session=session, options=self._options) result = sampler.run([qc]).result() self.assertEqual(len(result), 1) - self.assertIn("shots", result[0].metadata) - self.assertEqual(result[0].metadata["shots"], self._shots) - - shots = 100 - sampler.options.shots = 100 - result = sampler.run([qc]).result() - self.assertEqual(len(result), 1) - self.assertIn("shots", result[0].metadata) - self.assertEqual(result[0].metadata["shots"], shots) + self.assertEqual(result[0].data.meas.num_shots, self._shots) @run_integration_test def test_circuit_with_multiple_cregs(self, service): From 99a6985ed03e2cef62449fcbcccf172847023696 Mon Sep 17 00:00:00 2001 From: gadial Date: Fri, 23 Feb 2024 17:35:43 +0200 Subject: [PATCH 7/9] linting --- test/integration/test_sampler_v2.py | 4 +++- test/unit/test_ibm_primitives_v2.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index 3f6f6ed61..bee399730 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -83,7 +83,9 @@ def _assert_allclose( 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())) # pylint: disable=nested-min-max + max_key = max( + max(int_counts.keys()), max(target_counts.keys()) + ) # pylint: disable=nested-min-max 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}") diff --git a/test/unit/test_ibm_primitives_v2.py b/test/unit/test_ibm_primitives_v2.py index bc5e09e2d..8e816ea73 100644 --- a/test/unit/test_ibm_primitives_v2.py +++ b/test/unit/test_ibm_primitives_v2.py @@ -707,7 +707,6 @@ def test_faulty_edge_not_used(self, primitive): inst.run([pub]) mock_run.assert_called_once() - @data(EstimatorV2, SamplerV2) def test_abstract_circuits(self, primitive): """Test passing in abstract circuit would fail.""" From fe02f71a37b06489fc17698f63d08b9d2806a283 Mon Sep 17 00:00:00 2001 From: gadial Date: Fri, 23 Feb 2024 17:43:06 +0200 Subject: [PATCH 8/9] linting --- test/integration/test_sampler_v2.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/integration/test_sampler_v2.py b/test/integration/test_sampler_v2.py index bee399730..7479b62fd 100644 --- a/test/integration/test_sampler_v2.py +++ b/test/integration/test_sampler_v2.py @@ -83,9 +83,8 @@ def _assert_allclose( 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()) - ) # pylint: disable=nested-min-max + # pylint: disable=nested-min-max + 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}") From b55663b552c2b8b3c524eda1c5c78cdae1511f39 Mon Sep 17 00:00:00 2001 From: gadial Date: Tue, 27 Feb 2024 15:22:56 +0200 Subject: [PATCH 9/9] Fixes according to code review --- qiskit_ibm_runtime/sampler.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qiskit_ibm_runtime/sampler.py b/qiskit_ibm_runtime/sampler.py index cbec5f54f..c48684d11 100644 --- a/qiskit_ibm_runtime/sampler.py +++ b/qiskit_ibm_runtime/sampler.py @@ -42,7 +42,7 @@ class Sampler: version = 0 -class SamplerV2(BasePrimitiveV2, Sampler, BaseSamplerV2): +class SamplerV2(BasePrimitiveV2[SamplerOptions], Sampler, BaseSamplerV2): """Class for interacting with Qiskit Runtime Sampler primitive service. This class supports version 2 of the Sampler interface, which uses different @@ -150,11 +150,16 @@ class SamplerV1(BasePrimitiveV1, Sampler, BaseSampler): Example:: - from qiskit.test.reference_circuits import ReferenceCircuits + from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit_ibm_runtime import QiskitRuntimeService, Session, Sampler service = QiskitRuntimeService(channel="ibm_cloud") - bell = ReferenceCircuits.bell() + quantum_register = QuantumRegister(2, name="qr") + classical_register = ClassicalRegister(2, name="cr") + bell = QuantumCircuit(quantum_register, classical_register, name="bell") + bell.h(quantum_register[0]) + bell.cx(quantum_register[0], quantum_register[1]) + bell.measure(quantum_register, classical_register) with Session(service, backend="ibmq_qasm_simulator") as session: sampler = Sampler(session=session)