From ecce1716f4c83500dd4d4ada0d4fcf238b28b148 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 9 Aug 2024 17:10:40 +0900 Subject: [PATCH] merge code from #2187 partialy to reduce deprecation warnings --- .github/workflows/tests.yml | 1 - qiskit_aer/backends/aer_compiler.py | 5 +- qiskit_aer/backends/aerbackend.py | 67 +---------- qiskit_aer/backends/backend_utils.py | 43 ++++++++ src/framework/pybind_json.hpp | 11 +- .../backends/aer_simulator/test_executors.py | 64 +---------- test/terra/noise/test_device_models.py | 9 +- test/terra/noise/test_noise_model.py | 104 ------------------ test/terra/primitives/test_sampler_v2.py | 21 ++-- 9 files changed, 72 insertions(+), 253 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f3e080453..5614aaf1ec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -231,7 +231,6 @@ jobs: - name: Run Tests run: | set -e - pip check rm -rf qiskit_aer stestr run --slowest shell: bash diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index afa497e3df..51172f0ec7 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -42,7 +42,6 @@ from qiskit.transpiler.passes import Decompose -from qiskit.qobj import QobjExperimentHeader from qiskit_aer.aererror import AerError from qiskit_aer.noise import NoiseModel @@ -62,7 +61,7 @@ AerConfig, ) -from .backend_utils import circuit_optypes +from .backend_utils import circuit_optypes, CircuitHeader from ..library.control_flow_instructions import AerMark, AerJump, AerStore @@ -680,7 +679,7 @@ def assemble_circuit(circuit: QuantumCircuit, basis_gates=None): for inst in circuit.data ) - header = QobjExperimentHeader( + header = CircuitHeader( n_qubits=num_qubits, qreg_sizes=qreg_sizes, memory_slots=num_memory, diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 1ea2e8952e..1c2a488b4e 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -143,13 +143,12 @@ def _convert_binds(self, circuits, parameter_binds, idx_maps=None): return parameterizations # pylint: disable=arguments-renamed - def run(self, circuits, validate=False, parameter_binds=None, **run_options): + def run(self, circuits, parameter_binds=None, **run_options): """Run circuits on the backend. Args: circuits (QuantumCircuit or list): The QuantumCircuit (or list of QuantumCircuit objects) to run - validate (bool): validate the Qobj before running (default: False). parameter_binds (list): A list of parameter binding dictionaries. See additional information (default: None). run_options (kwargs): additional run time backend options. @@ -158,7 +157,7 @@ def run(self, circuits, validate=False, parameter_binds=None, **run_options): AerJob: The simulation job. Raises: - TypeError: If ``parameter_binds`` is specified with a qobj input or + TypeError: If ``parameter_binds`` is specified with an input or has a length mismatch with the number of circuits. Additional Information: @@ -183,67 +182,7 @@ def run(self, circuits, validate=False, parameter_binds=None, **run_options): if isinstance(circuits, (QuantumCircuit, Schedule, ScheduleBlock)): circuits = [circuits] - if isinstance(circuits, (QasmQobj, PulseQobj)): - warnings.warn( - "Using a qobj for run() is deprecated as of qiskit-aer 0.14" - " and will be removed no sooner than 3 months from that release" - " date. Transpiled circuits should now be passed directly using" - " `backend.run(circuits, **run_options).", - DeprecationWarning, - stacklevel=2, - ) - if parameter_binds: - raise TypeError("Parameter binds can't be used with an input qobj") - # A work around to support both qobj options and run options until - # qobj is deprecated is to copy all the set qobj.config fields into - # run_options that don't override existing fields. This means set - # run_options fields will take precidence over the value for those - # fields that are set via assemble. - if not run_options: - run_options = circuits.config.__dict__ - else: - run_options = copy.copy(run_options) - for key, value in circuits.config.__dict__.items(): - if key not in run_options and value is not None: - run_options[key] = value - if "parameter_binds" in run_options: - parameter_binds = run_options.pop("parameter_binds") - return self._run_qobj(circuits, validate, parameter_binds, **run_options) - - only_circuits = True - only_pulse = True - for circ in circuits: - only_circuits &= isinstance(circ, QuantumCircuit) - only_pulse &= isinstance(circ, (ScheduleBlock, Schedule)) - - if only_circuits and not only_pulse: - if validate: - raise TypeError( - "bad input to run() function;" - "`validation` argument is only effective for input qobj" - ) - - executor = run_options.get("executor", None) - if executor is None and "executor" in self.options.__dict__: - executor = self.options.__dict__.get("executor", None) - if executor: - # This path remains for DASK execution to split a qobj insttance - # into sub-qobj instances. This will be replaced with _run_circuits path - # in the near releases - return self._run_qobj(circuits, validate, parameter_binds, **run_options) - else: - return self._run_circuits(circuits, parameter_binds, **run_options) - elif not only_circuits and only_pulse: - return self._run_qobj(circuits, validate, parameter_binds, **run_options) - elif not only_circuits and not only_pulse: - raise TypeError( - "bad input to run() function;" - "circuits and schedules cannot be mixed in a single run" - ) - else: - raise TypeError( - "bad input to run() function; circuits must be either circuits or schedules" - ) + return self._run_circuits(circuits, parameter_binds, **run_options) def _run_circuits(self, circuits, parameter_binds, **run_options): """Run circuits by generating native circuits.""" diff --git a/qiskit_aer/backends/backend_utils.py b/qiskit_aer/backends/backend_utils.py index a2bd628c0e..a01958b7e5 100644 --- a/qiskit_aer/backends/backend_utils.py +++ b/qiskit_aer/backends/backend_utils.py @@ -17,6 +17,8 @@ import os from math import log2 +from types import SimpleNamespace + import psutil from qiskit.circuit import QuantumCircuit from qiskit.qobj import QasmQobjInstruction @@ -559,3 +561,44 @@ def circuit_optypes(circuit): optypes.update(type(instruction.operation).mro()) optypes.discard(object) return optypes + + +class CircuitHeader(SimpleNamespace): + """A class used to represent a dictionary header in circuit objects.""" + + def __init__(self, **kwargs): + """Instantiate a new circuit dict field object. + + Args: + kwargs: arbitrary keyword arguments that can be accessed as + attributes of the object. + """ + self.__dict__.update(kwargs) + + def to_dict(self): + """Return a dictionary format representation of the circuit. + + Returns: + dict: The dictionary form of the CircuitHeader. + """ + return self.__dict__ + + @classmethod + def from_dict(cls, data): + """Create a new header object from a dictionary. + + Args: + data (dict): A dictionary representing the header to create. It + will be in the same format as output by :func:`to_dict`. + + Returns: + CircuitHeader: The CircuitHeader from the input dictionary. + """ + + return cls(**data) + + def __eq__(self, other): + if isinstance(other, self.__class__): + if self.__dict__ == other.__dict__: + return True + return False diff --git a/src/framework/pybind_json.hpp b/src/framework/pybind_json.hpp index 108e1b34ec..0f23e7d0f2 100644 --- a/src/framework/pybind_json.hpp +++ b/src/framework/pybind_json.hpp @@ -221,10 +221,9 @@ json_t JSON::iterable_to_json_list(const py::handle &obj) { void std::to_json(json_t &js, const py::handle &obj) { static py::object PyNoiseModel = py::module::import("qiskit_aer.noise.noise_model").attr("NoiseModel"); - static py::object PyQasmQobj = - py::module::import("qiskit.qobj.qasm_qobj").attr("QasmQobj"); - static py::object PyQasmQobjHeader = - py::module::import("qiskit.qobj.common").attr("QobjExperimentHeader"); + static py::object PyCircuitHeader = + py::module::import("qiskit_aer.backends.backend_utils") + .attr("CircuitHeader"); if (py::isinstance(obj)) { js = obj.cast(); } else if (py::isinstance(obj)) { @@ -249,9 +248,7 @@ void std::to_json(json_t &js, const py::handle &obj) { return; } else if (py::isinstance(obj, PyNoiseModel)) { std::to_json(js, obj.attr("to_dict")()); - } else if (py::isinstance(obj, PyQasmQobj)) { - std::to_json(js, obj.attr("to_dict")()); - } else if (py::isinstance(obj, PyQasmQobjHeader)) { + } else if (py::isinstance(obj, PyCircuitHeader)) { std::to_json(js, obj.attr("to_dict")()); } else { auto type_str = std::string(py::str(obj.get_type())); diff --git a/test/terra/backends/aer_simulator/test_executors.py b/test/terra/backends/aer_simulator/test_executors.py index 28fc99d57c..6f5f2fa336 100644 --- a/test/terra/backends/aer_simulator/test_executors.py +++ b/test/terra/backends/aer_simulator/test_executors.py @@ -26,20 +26,10 @@ from qiskit.quantum_info import Statevector from qiskit_aer.noise.noise_model import AerJSONEncoder from test.terra.reference import ref_kraus_noise -from qiskit_aer.jobs import AerJob, AerJobSet +from qiskit_aer.jobs import AerJob from test.terra.backends.simulator_test_case import SimulatorTestCase, supported_methods -DASK = False - -try: - from dask.distributed import LocalCluster, Client - - DASK = True -except ImportError: - DASK = False - - def run_random_circuits(backend, shots=None, **run_options): """Test random circuits on different executor fictures""" job_size = 10 @@ -107,58 +97,6 @@ def backend(self, **options): return super().backend(executor=self._test_executor, **options) -@ddt -class TestDaskExecutor(CBFixture): - """Tests of Dask executor""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - if DASK: - cls._test_executor = Client(address=LocalCluster(n_workers=1, processes=True)) - - def setUp(self): - super().setUp() - if not DASK: - self.skipTest("Dask not installed, skipping ClusterBackend-dask tests") - - @supported_methods(["statevector"], [None, 1, 2, 3]) - def test_random_circuits_job(self, method, device, max_job_size): - """Test random circuits with custom executor.""" - shots = 4000 - backend = self.backend(method=method, device=device, max_job_size=max_job_size) - result, circuits, targets = run_random_circuits(backend, shots=shots) - self.assertSuccess(result) - self.compare_counts(result, circuits, targets, hex_counts=False, delta=0.05 * shots) - - @supported_methods(["statevector"], [None, 1, 1, 1], [None, 100, 500, 1000]) - def test_noise_circuits_job(self, method, device, max_job_size, max_shot_size): - """Test random circuits with custom executor.""" - shots = 4000 - backend = self.backend( - method=method, device=device, max_job_size=max_job_size, max_shot_size=max_shot_size - ) - - circuits = ref_kraus_noise.kraus_gate_error_circuits() - noise_models = ref_kraus_noise.kraus_gate_error_noise_models() - targets = ref_kraus_noise.kraus_gate_error_counts(shots) - - for circuit, noise_model, target in zip(circuits, noise_models, targets): - backend.set_options(noise_model=noise_model) - result = backend.run(circuit, shots=shots).result() - self.assertSuccess(result) - self.compare_counts(result, [circuit], [target], delta=0.05 * shots) - - @supported_methods(["statevector"], [None, 1, 2, 3]) - def test_result_time_val(self, method, device, max_job_size): - """Test random circuits with custom executor.""" - shots = 4000 - backend = self.backend(method=method, device=device, max_job_size=max_job_size) - result, _, _ = run_random_circuits(backend, shots=shots) - self.assertSuccess(result) - self.assertGreaterEqual(result.time_taken, 0) - - @ddt class TestThreadPoolExecutor(CBFixture): """Tests of ThreadPool executor""" diff --git a/test/terra/noise/test_device_models.py b/test/terra/noise/test_device_models.py index 43d90fda91..ab2946c167 100644 --- a/test/terra/noise/test_device_models.py +++ b/test/terra/noise/test_device_models.py @@ -85,8 +85,11 @@ def test_basic_device_gate_errors_from_target_with_non_operational_qubits(self): target = target_7q() # tweak target to have non-operational qubits faulty_qubits = (1, 2) + q_prop = target.qubit_properties for q in faulty_qubits: - target.qubit_properties[q] = QubitProperties(t1=None, t2=None, frequency=0) + q_prop[q] = QubitProperties(t1=None, t2=None, frequency=0) + target.qubit_properties = q_prop + # build gate errors with only relaxation errors i.e. without depolarizing errors gate_errors = basic_device_gate_errors(target=target, gate_error=False) errors_on_sx = {qubits: error for name, qubits, error in gate_errors if name == "sx"} @@ -128,7 +131,9 @@ def test_non_zero_temperature(self): """Test if non-zero excited_state_population is obtained when positive temperature is supplied. See https://github.com/Qiskit/qiskit-aer/issues/1937 for the details.""" t1, t2, frequency, duration = 1e-4, 1e-4, 5e9, 5e-8 - target = Target(qubit_properties=[QubitProperties(t1=t1, t2=t2, frequency=frequency)]) + target = Target( + num_qubits=1, qubit_properties=[QubitProperties(t1=t1, t2=t2, frequency=frequency)] + ) target.add_instruction(library.XGate(), {(0,): InstructionProperties(duration=duration)}) errors = basic_device_gate_errors(target=target, gate_error=False, temperature=100) _, _, x_error = errors[0] diff --git a/test/terra/noise/test_noise_model.py b/test/terra/noise/test_noise_model.py index 297e38d188..850e025192 100644 --- a/test/terra/noise/test_noise_model.py +++ b/test/terra/noise/test_noise_model.py @@ -37,9 +37,6 @@ if qiskit.__version__.startswith("0."): from qiskit.providers.fake_provider import ( - FakeBackend, - FakeAlmaden as Fake20QV1, - FakeMumbai as Fake27QPulseV1, FakeLagosV2, ) @@ -49,9 +46,6 @@ def fake_7q_v2(): else: from qiskit.providers.fake_provider import ( - FakeBackend, - Fake20QV1, - Fake27QPulseV1, GenericBackendV2, ) @@ -213,30 +207,6 @@ def test_noise_models_not_equal(self): model2 = NoiseModel(basis_gates=["u3", "cx"]) model2.add_all_qubit_quantum_error(error, ["u3"], False) - def test_noise_model_from_backend_20(self): - circ = QuantumCircuit(2) - circ.x(0) - circ.x(1) - circ.measure_all() - - backend = Fake20QV1() - noise_model = NoiseModel.from_backend(backend) - circ = transpile(circ, backend, optimization_level=0) - result = AerSimulator().run(circ, noise_model=noise_model).result() - self.assertTrue(result.success) - - def test_noise_model_from_backend_27_pulse(self): - circ = QuantumCircuit(2) - circ.x(0) - circ.x(1) - circ.measure_all() - - backend = Fake27QPulseV1() - noise_model = NoiseModel.from_backend(backend) - circ = transpile(circ, backend, optimization_level=0) - result = AerSimulator().run(circ, noise_model=noise_model).result() - self.assertTrue(result.success) - def test_noise_model_from_backend_v2(self): circ = QuantumCircuit(2) circ.x(0) @@ -269,67 +239,6 @@ def test_noise_model_from_backend_v2_with_non_operational_qubits(self): result = AerSimulator().run(circ, noise_model=noise_model).result() self.assertTrue(result.success) - def test_noise_model_from_invalid_t2_backend(self): - """Test if silently truncate invalid T2 values when creating a noise model from backend""" - from qiskit.providers.models.backendproperties import BackendProperties, Gate, Nduv - import datetime - - t1_ns, invalid_t2_ns = 75_1000, 200_1000 - u3_time_ns = 320 - frequency = 4919.96800692 - - class InvalidT2Fake1Q(FakeBackend): - def __init__(self): - mock_time = datetime.datetime.now() - dt = 1.3333 - configuration = BackendProperties( - backend_name="invalid_t2", - backend_version="0.0.0", - num_qubits=1, - basis_gates=["u3"], - qubits=[ - [ - Nduv(date=mock_time, name="T1", unit="µs", value=t1_ns / 1000), - Nduv(date=mock_time, name="T2", unit="µs", value=invalid_t2_ns / 1000), - Nduv(date=mock_time, name="frequency", unit="MHz", value=frequency), - ], - ], - gates=[ - Gate( - gate="u3", - name="u3_0", - qubits=[0], - parameters=[ - Nduv(date=mock_time, name="gate_error", unit="", value=0.001), - Nduv( - date=mock_time, name="gate_length", unit="ns", value=u3_time_ns - ), - ], - ), - ], - last_update_date=mock_time, - general=[], - ) - super().__init__(configuration) - - def defaults(self): - """defaults == configuration""" - return self._configuration - - def properties(self): - """properties == configuration""" - return self._configuration - - backend = InvalidT2Fake1Q() - noise_model = NoiseModel.from_backend(backend, gate_error=False) - expected = thermal_relaxation_error( - t1=t1_ns, - t2=2 * t1_ns, - time=u3_time_ns, - excited_state_population=_excited_population(frequency, temperature=0), - ) - self.assertEqual(expected, noise_model._local_quantum_errors["u3"][(0,)]) - def test_create_noise_model_without_user_warnings(self): """Test if never issue user warnings when creating a noise model from backend. See issue#1631 for the details.""" @@ -359,19 +268,6 @@ def run(self, run_input, **options): user_warnings = [w for w in warns if issubclass(w.category, UserWarning)] self.assertEqual(len(user_warnings), 0) - def test_noise_model_from_backend_properties(self): - circ = QuantumCircuit(2) - circ.x(0) - circ.x(1) - circ.measure_all() - - backend = Fake20QV1() - backend_propeties = backend.properties() - noise_model = NoiseModel.from_backend_properties(backend_propeties) - circ = transpile(circ, backend, optimization_level=0) - result = AerSimulator().run(circ, noise_model=noise_model).result() - self.assertTrue(result.success) - def test_transform_noise(self): org_error = reset_error(0.2) new_error = pauli_error([("I", 0.5), ("Z", 0.5)]) diff --git a/test/terra/primitives/test_sampler_v2.py b/test/terra/primitives/test_sampler_v2.py index 3ae9c98fad..8676ad69ec 100644 --- a/test/terra/primitives/test_sampler_v2.py +++ b/test/terra/primitives/test_sampler_v2.py @@ -575,17 +575,21 @@ def test_circuit_with_aliased_cregs(self): 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] + # Note: qc2 has aliased cregs, c0 -> c[2] and c1 -> c[4]. + # copy_empty_like copies the aliased cregs of qc2 to qc3. + qc3 = QuantumCircuit.copy_empty_like(qc2) + qc3.ry(np.pi / 4, 2) + qc3.cx(2, 1) + qc3.cx(0, 1) + qc3.h(0) + qc3.measure(0, 2) + qc3.measure(1, 4) + self.assertEqual(len(qc3.cregs), 3) + cregs = [creg.name for creg in qc3.cregs] target = { cregs[0]: {0: 4255, 4: 4297, 16: 720, 20: 726}, cregs[1]: {0: 5000, 1: 5000}, @@ -593,8 +597,7 @@ def test_circuit_with_aliased_cregs(self): } sampler = SamplerV2(**self._options) - qc2 = self._pm.run(qc2) - result = sampler.run([qc2], shots=self._shots).result() + result = sampler.run([qc3], shots=self._shots).result() self.assertEqual(len(result), 1) data = result[0].data self.assertEqual(len(data), 3)