diff --git a/qiskit/providers/aer/noise/device/models.py b/qiskit/providers/aer/noise/device/models.py index daa6c1ab96..c6ec388ec1 100644 --- a/qiskit/providers/aer/noise/device/models.py +++ b/qiskit/providers/aer/noise/device/models.py @@ -245,6 +245,7 @@ def _device_thermal_relaxation_error(qubits, error = None for qubit in qubits: t1, t2, freq = relax_params[qubit] + t2 = _truncate_t2_value(t1, t2) population = _excited_population(freq, temperature) if first: error = thermal_relaxation_error(t1, t2, gate_time, population) @@ -255,6 +256,17 @@ def _device_thermal_relaxation_error(qubits, return error +def _truncate_t2_value(t1, t2): + """Return t2 value truncated to 2 * t1 (for t2 > 2 * t1)""" + new_t2 = t2 + if t2 > 2 * t1: + new_t2 = 2 * t1 + warn("Device model returned an invalid T_2 relaxation time greater than" + f" the theoretical maximum value 2 * T_1 ({t2} > 2 * {t1})." + " Truncating to maximum value.", UserWarning) + return new_t2 + + def _excited_population(freq, temperature): """Return excited state population""" population = 0 diff --git a/qiskit/providers/aer/noise/device/parameters.py b/qiskit/providers/aer/noise/device/parameters.py index ead1fa1836..7468e526d4 100644 --- a/qiskit/providers/aer/noise/device/parameters.py +++ b/qiskit/providers/aer/noise/device/parameters.py @@ -180,11 +180,6 @@ def thermal_relaxation_values(properties): # Convert to Gigahertz freq *= _GHZ_UNITS.get(freq_params.unit, 1) - # NOTE: T2 cannot be larger than 2 * T1 for a physical noise - # channel, however if a backend erroneously reports such a value we - # truncated it here: - t2 = min(2 * t1, t2) - values.append((t1, t2, freq)) return values diff --git a/qiskit/providers/aer/noise/noise_model.py b/qiskit/providers/aer/noise/noise_model.py index 6bcf58d265..082ec62e1a 100644 --- a/qiskit/providers/aer/noise/noise_model.py +++ b/qiskit/providers/aer/noise/noise_model.py @@ -25,7 +25,7 @@ from qiskit.providers.exceptions import BackendPropertyError from qiskit.providers.models import BackendProperties from qiskit.transpiler import PassManager -from .device.models import _excited_population +from .device.models import _excited_population, _truncate_t2_value from .device.models import basic_device_gate_errors from .device.models import basic_device_readout_errors from .errors.quantum_error import QuantumError @@ -373,9 +373,11 @@ def from_backend(cls, backend, except BackendPropertyError: excited_state_populations = None try: + t1s = [properties.t1(q) for q in range(num_qubits)] + t2s = [properties.t2(q) for q in range(num_qubits)] delay_pass = RelaxationNoisePass( - t1s=[properties.t1(q) for q in range(num_qubits)], - t2s=[properties.t2(q) for q in range(num_qubits)], + t1s=t1s, + t2s=[_truncate_t2_value(t1, t2) for t1, t2 in zip(t1s, t2s)], dt=dt, op_types=Delay, excited_state_populations=excited_state_populations diff --git a/releasenotes/notes/fix-invalid-t2-error-a3685e4a3ad0a1e7.yaml b/releasenotes/notes/fix-invalid-t2-error-a3685e4a3ad0a1e7.yaml new file mode 100644 index 0000000000..cb1abebbe7 --- /dev/null +++ b/releasenotes/notes/fix-invalid-t2-error-a3685e4a3ad0a1e7.yaml @@ -0,0 +1,12 @@ +--- +fixes: + - | + Fixes a bug in ``NoiseModel.from_backend()`` that raised an error when + T2 value greater than 2 * T1 was supplied by the backend. + After this fix, it becomes to truncate T2 value up to 2 * T1 and + issue a user warning if truncates. + The bug was introduced at #1391 and, before that, ``NoiseModel.from_backend()`` had + truncated the T2 value up to 2 * T1 silently. + + See `Issue 1464 `__ + for details. diff --git a/test/terra/noise/test_noise_model.py b/test/terra/noise/test_noise_model.py index 24f05e1bfc..9abd2af487 100644 --- a/test/terra/noise/test_noise_model.py +++ b/test/terra/noise/test_noise_model.py @@ -19,17 +19,18 @@ import numpy as np from qiskit.providers.aer.backends import AerSimulator from qiskit.providers.aer.noise import NoiseModel -from qiskit.providers.aer.utils.noise_transformation import transform_noise_model +from qiskit.providers.aer.noise.device.models import _excited_population from qiskit.providers.aer.noise.errors.standard_errors import amplitude_damping_error from qiskit.providers.aer.noise.errors.standard_errors import kraus_error from qiskit.providers.aer.noise.errors.standard_errors import pauli_error from qiskit.providers.aer.noise.errors.standard_errors import reset_error -from test.terra.common import QiskitAerTestCase +from qiskit.providers.aer.noise.errors.standard_errors import thermal_relaxation_error +from qiskit.providers.aer.utils.noise_transformation import transform_noise_model from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.compiler import transpile -from qiskit.transpiler import TranspilerError from qiskit.test import mock +from test.terra.common import QiskitAerTestCase class TestNoiseModel(QiskitAerTestCase): @@ -229,6 +230,66 @@ def test_noise_model_from_mumbai(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 issue user warning when creating a noise model from invalid t2 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(mock.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() + with self.assertWarns(UserWarning): + 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_transform_noise(self): org_error = reset_error(0.2) new_error = pauli_error([("I", 0.5), ("Z", 0.5)])