From 8d9e9ff25683e336478f8b9ca3baa4064371f75b Mon Sep 17 00:00:00 2001 From: obliviateandsurrender Date: Sat, 13 Jul 2024 00:57:51 -0400 Subject: [PATCH] gate times test --- pennylane_qiskit/noise_models.py | 43 ++++++++++++++++++++++++-------- tests/test_noise_models.py | 27 +++++++++++++++++++- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/pennylane_qiskit/noise_models.py b/pennylane_qiskit/noise_models.py index 868dd436..71ef2d4d 100644 --- a/pennylane_qiskit/noise_models.py +++ b/pennylane_qiskit/noise_models.py @@ -156,17 +156,35 @@ def _process_kraus_ops( def _extract_gate_time(gate_data: dict, gate_name: str, gate_wires: int) -> float: """Helper method to extract gate time for a quantum error""" tg = 1.0 - if fgates := dict(filter(lambda item: item[0][0] == gate_name, gate_data)): - for g_d, g_t in fgates.items(): - g_d[1] = (g_d[1],) if isinstance(int, g_d[1]) else g_d[1] - if gate_wires in g_d: + if fgates := dict(filter(lambda item: item[0][0] == gate_name, gate_data.items())): + for (_, w_r), g_t in fgates.items(): + if w_r in gate_wires if isinstance(w_r, int) else set(gate_wires).issubset(w_r): tg = g_t break return tg -def _process_thermal_relaxation(choi_matrix, **kwargs): - """Computes parameters for thermal relaxation error from a Choi matrix of Kraus matrices""" +def _process_thermal_relaxation(choi_matrix, **kwargs) -> Tuple[bool, str, np.ndarray] or None: + r"""Computes the parameters for thermal relaxation error from a Choi matrix of Kraus matrices. + + Args: + choi_matrix (ndarray): Choi matrix of the channel to be processed. + + For plugin developers: This assumes :math:`T_1 < T_2 \leq 2 T_1`, where the error is expressed + as a general non-unitary Kraus error channel. + + .. math:: + + \begin{bmatrix} + 1 - p_e p_{r} & 0 & 0 & \exp{-T_g/T_2} \\ + 0 & p_e p_{r} & 0 & 0 \\ + 0 & 0 & (1 - p_e) p_{r} & 0 \\ + \exp{-T_g/T_2} & 0 & 0 & 1 - (1 - p_e) p_{r} + \end{bmatrix} + + Parameters :math:`p_e` is the excited-state population and :math:`p_r = 1 - \exp{-T_g/T_1}` + with :math:`T_g` as gate time and :math:`T_1\ (T_2)` as the relaxation (dephasing) constants. + """ nt_values = choi_matrix[tuple(zip(*sorted(kraus_indice_map["ThermalRelaxation"])))] decimals, atol, rtol = tuple(map(kwargs.get, default_option_map)) @@ -249,7 +267,7 @@ def _process_depolarization(error_dict: dict, multi_pauli: bool = False) -> dict def _process_reset(error_dict: dict, **kwargs) -> dict: - """Checks parity of a qunatum error with ``Reset`` instruction to a PennyLane Channel. + r"""Checks parity of a qunatum error with ``Reset`` instruction to a PennyLane Channel. Args: error_dict (dict): error dictionary for the quantum error @@ -257,6 +275,9 @@ def _process_reset(error_dict: dict, **kwargs) -> dict: Returns: dict: An updated error dictionary based on parity with existing PennyLane channel. + + For plugin developers: second branch of the condition assumes reset error expressed + from a thermal relaxation error with :math:`T_2 \leq T_1`. """ error_probs = error_dict["probs"] @@ -351,7 +372,7 @@ def _build_qerror_op(error, **kwargs) -> qml.operation.Operation: Args: error (QuantumError): Quantum error object - kwargs: Optional keyword arguments used during conversion + kwargs: Optional keyword arguments used during conversion. Returns: qml.operation.Channel: converted PennyLane quantum channel which is @@ -411,15 +432,15 @@ def _build_noise_model_map(noise_model, **kwargs) -> Tuple[dict, dict]: Keyword Arguments: thermal_relaxation (bool): prefer conversion of ``QiskitErrors`` to thermal relaxation errors over damping errors. Default is ``False``. + readout_error (bool): include readout error in the converted noise model. Default is ``True``. gate_times (Dict[Tuple(str, Tuple[int]), float]): a dictionary to provide gate times for building thermal relaxation error. Each key will be a tuple of instruction name and qubit indices and the corresponding value will be the time in seconds. If it is not provided or a gate/qubit is missing, then a default value of `1.0 s`` will be used for the specific constructions. - multi_pauli (bool): assume depolarization channel to be multi-qubit. This is currently not - supported with ``qml.DepolarizationChannel``, which is a single qubit channel. - readout_error (bool): include readout error in the converted noise model. Default is ``True``. optimize (bool): controls if a contraction order optimization is used for ``einsum`` while transforming Kraus operators to a Choi matrix, wherever required. Default is ``False``. + multi_pauli (bool): assume depolarization channel to be multi-qubit. This is currently not + supported with ``qml.DepolarizationChannel``, which is a single qubit channel. options (dict[str, Union[int, float]]): optional parameters related to tolerance and rounding: - decimals (int): number of decimal places to round the Kraus matrices. Default is ``10``. diff --git a/tests/test_noise_models.py b/tests/test_noise_models.py index bfb4bd98..c5fd287b 100644 --- a/tests/test_noise_models.py +++ b/tests/test_noise_models.py @@ -174,7 +174,6 @@ def test_build_model_map(self, depol1, depol2, exc_pop): error_2 = noise.depolarizing_error(depol2, 2) error_3 = noise.phase_amplitude_damping_error(0.14, 0.24, excited_state_population=exc_pop) - # Add errors to noise model noise_model = noise.NoiseModel() noise_model.add_all_qubit_quantum_error(error_1, ["rz", "sx", "x"]) noise_model.add_all_qubit_quantum_error(error_2, ["cx"]) @@ -201,3 +200,29 @@ def test_build_model_map(self, depol1, depol2, exc_pop): {AnyWires: ["CNOT"]}, {AnyWires: ["RY", "RX"]}, ] + + @pytest.mark.parametrize( + "gate_times", + [ + {("sx", (0, 1)): 2.0, ("rx", (0,)): 2.5, ("rx", (1,)): 3.0}, + {("sx", (0,)): 2.0, ("sx", (1,)): 2.5, ("rx", (0, 1)): 3.0}, + ], + ) + def test_thermal_gate_times(self, gate_times): + """Tests that a quantum error can be correctly converted into a PennyLane QubitChannel.""" + + pl_channels, pl_vals = [], [] + noise_model = noise.NoiseModel() + for gate_wires, time in gate_times.items(): + gate, wires = gate_wires + for wire in wires: + noise_model.add_quantum_error( + noise.thermal_relaxation_error(0.14, 0.24, time, 0.02), gate, (wire,) + ) + pl_channels.append(qml.ThermalRelaxationError(0.02, 0.14, 0.24, time, wires=AnyWires)) + pl_vals.append({(wire,): [gate.upper()] for wire in wires}) + + model_map, _ = _build_noise_model_map(noise_model, gate_times=gate_times) + + assert list(model_map.keys()) == pl_channels + assert list(model_map.values()) == pl_vals