diff --git a/qermit/noise_model/noise_model.py b/qermit/noise_model/noise_model.py index 190e93eb..dfad76fb 100644 --- a/qermit/noise_model/noise_model.py +++ b/qermit/noise_model/noise_model.py @@ -222,6 +222,9 @@ def _scale_error_rate(scaling_factor: float, error_rate: float) -> float: :return: Scaled error rate. :rtype: float """ + if error_rate == 1: + return 1 + return 1 - math.exp(scaling_factor * math.log(1 - error_rate)) def scale(self, scaling_factor: float) -> ErrorDistribution: diff --git a/qermit/spam/spam_mitres.py b/qermit/spam/spam_mitres.py index 952f42c2..93447e9b 100644 --- a/qermit/spam/spam_mitres.py +++ b/qermit/spam/spam_mitres.py @@ -101,6 +101,10 @@ def gen_UnCorrelated_SPAM_MitRes( """ if backend.backend_info is None: raise ValueError("Backend has no backend_info attribute.") + if backend.backend_info.architecture is None: + raise ValueError( + "Backend Architecture has no specified Nodes, please use a Backend with a specified Architecture." + ) if len(backend.backend_info.architecture.nodes) == 0: raise ValueError( "Backend Architecture has no specified Nodes, please use a Backend with a specified Architecture." diff --git a/tests/noise_model_test.py b/tests/noise_model_test.py index 4ac2911b..f0a64cd9 100644 --- a/tests/noise_model_test.py +++ b/tests/noise_model_test.py @@ -722,6 +722,30 @@ def test_is_measureable() -> None: assert not stab.is_measureable(qubit_list=[Qubit(name='B', index=0)]) +def test_noise_model_scaling() -> None: + + error_distribution = ErrorDistribution( + distribution={ + (Pauli.X, Pauli.I): 0.1, + (Pauli.Z, Pauli.I): 0.01, + } + ) + noise_model = NoiseModel( + noise_model={ + OpType.CZ: error_distribution, + OpType.CX: error_distribution + } + ) + + two_scaled_noise_model = noise_model.scale(scaling_factor=2) + assert abs(two_scaled_noise_model.noise_model[OpType.CZ].distribution[(Pauli.X, Pauli.I)] - 0.19) < 0.001 + assert abs(two_scaled_noise_model.noise_model[OpType.CZ].distribution[(Pauli.Z, Pauli.I)] - 0.02) < 0.001 + + zero_scaled_noise_model = noise_model.scale(scaling_factor=0) + assert abs(zero_scaled_noise_model.noise_model[OpType.CX].distribution[(Pauli.X, Pauli.I)]) < 0.01 + assert abs(zero_scaled_noise_model.noise_model[OpType.CX].distribution[(Pauli.Z, Pauli.I)]) < 0.01 + + if __name__ == "__main__": test_is_measureable() diff --git a/tests/zne_test.py b/tests/zne_test.py index a8c6d7ff..609f8384 100644 --- a/tests/zne_test.py +++ b/tests/zne_test.py @@ -27,6 +27,7 @@ extrapolation_task_gen, digital_folding_task_gen, gen_qubit_relabel_task, + merge_experiments_task_gen, ) from pytket.predicates import GateSetPredicate # type: ignore from pytket.extensions.qiskit import AerBackend, IBMQEmulatorBackend # type: ignore @@ -844,7 +845,7 @@ def test_end_to_end_noise_scaled_mitex(): @pytest.mark.high_compute -def test_end_to_end_noise_aware_zne(): +def test_end_to_end_noise_aware_zne_mitex(): error_rate = 0.1 error_distribution = ErrorDistribution( @@ -897,6 +898,50 @@ def test_end_to_end_noise_aware_zne(): assert abs(qubit_pauli_operator_list[0]._dict[qps_noisy_noisy] - 1) < 0.1 +def test_noise_aware_folding(): + + error_distribution = ErrorDistribution( + distribution={(Pauli.X, Pauli.I): 1} + ) + noise_model = NoiseModel( + noise_model={OpType.CZ: error_distribution} + ) + circ = Circuit(2).CZ(0, 1) + scaled_circ = Folding.noise_aware( + circ=circ, + noise_scaling=2, + noise_model=noise_model, + n_noisy_circuit_samples=1, + ) + scaled_circ[0] + assert scaled_circ[0] == Circuit(2).CZ(0, 1).X(0, opgroup='noisy') + + +def test_merge_experiments_task_gen(): + + task = merge_experiments_task_gen() + + qps_one = QubitPauliString(map={Qubit(0): Pauli.Z}) + qps_two = QubitPauliString(map={Qubit(1): Pauli.X}) + + qpo_one = QubitPauliOperator( + dictionary={qps_one: 1, qps_two: 1} + ) + qpo_two = QubitPauliOperator( + dictionary={qps_one: 1, qps_two: -1} + ) + qpo_three = QubitPauliOperator( + dictionary={qps_one: 1, qps_two: -1} + ) + merged_qpo_list = task( + ([qpo_one, qpo_two, qpo_three], [0, 0, 1]) + ) + assert merged_qpo_list[0][0]._dict[qps_one] == 1 + assert merged_qpo_list[0][0]._dict[qps_two] == 0 + assert merged_qpo_list[0][1]._dict[qps_one] == 1 + assert merged_qpo_list[0][1]._dict[qps_two] == -1 + + if __name__ == "__main__": test_no_qubit_relabel() test_extrapolation_task_gen() @@ -911,3 +956,6 @@ def test_end_to_end_noise_aware_zne(): test_two_qubit_gate_folding() test_gen_initial_compilation_task_quantinuum_qubit_names() test_end_to_end_noise_scaled_mitex() + test_end_to_end_noise_aware_zne_mitex() + test_merge_experiments_task_gen() + test_noise_aware_folding()