def test_apply_noise_to_gates_2QubitNoise_2( circuit_3qubit, noise_2qubit, noise_1qubit, noise_1qubit_2 ): circ = apply_noise_to_gates( circuit_3qubit, [noise_1qubit, noise_2qubit, noise_1qubit_2], target_gates=[Gate.CZ], target_qubits=QubitSet([1, 2]), ) expected = ( Circuit() .add_instruction(Instruction(Gate.X(), 0)) .add_instruction(Instruction(Gate.Y(), 1)) .add_instruction(Instruction(Gate.CNot(), [0, 1])) .add_instruction(Instruction(Gate.Z(), 2)) .add_instruction(Instruction(Gate.CZ(), [2, 1])) .add_instruction(Instruction(noise_1qubit, 1)) .add_instruction(Instruction(noise_1qubit, 2)) .add_instruction(Instruction(noise_2qubit, [2, 1])) .add_instruction(Instruction(noise_1qubit_2, 1)) .add_instruction(Instruction(noise_1qubit_2, 2)) .add_instruction(Instruction(Gate.CNot(), [0, 2])) .add_instruction(Instruction(Gate.CZ(), [1, 2])) .add_instruction(Instruction(noise_1qubit, 1)) .add_instruction(Instruction(noise_1qubit, 2)) .add_instruction(Instruction(noise_2qubit, [1, 2])) .add_instruction(Instruction(noise_1qubit_2, 1)) .add_instruction(Instruction(noise_1qubit_2, 2)) ) assert circ == expected
def test_apply_noise_to_gates_1QubitNoise_2(circuit_2qubit, noise_1qubit): circ = apply_noise_to_gates( circuit_2qubit, [noise_1qubit], target_gates=[Gate.X], target_qubits=QubitSet(0), ) expected = (Circuit().add_instruction(Instruction( Gate.X(), 0)).add_instruction(Instruction(noise_1qubit, 0)).add_instruction( Instruction(Gate.Y(), 1)).add_instruction(Instruction( Gate.X(), 0)).add_instruction(Instruction( noise_1qubit, 0)).add_instruction(Instruction( Gate.X(), 1)).add_instruction(Instruction(Gate.CNot(), [0, 1]))) assert circ == expected
def test_apply_noise_to_gates_1QubitNoise_not_dense(circuit_2qubit_not_dense, noise_1qubit): circ = apply_noise_to_gates( circuit_2qubit_not_dense, [noise_1qubit], target_qubits=QubitSet([0, 1]), target_gates=None, ) expected_moments = Moments() expected_moments._add(Instruction(Gate.X(), 0), noise_index=1) expected_moments.add_noise(Instruction(noise_1qubit, 0), "gate_noise", 1) expected_moments._add(Instruction(Gate.Y(), 1), noise_index=1) expected_moments.add_noise(Instruction(noise_1qubit, 1), "gate_noise", 1) expected_moments._add(Instruction(Gate.X(), 0), noise_index=1) expected_moments.add_noise(Instruction(noise_1qubit, 0), "gate_noise", 1) expected_moments._add(Instruction(Gate.CNot(), [0, 1]), noise_index=2) expected_moments.add_noise(Instruction(noise_1qubit, 0), "gate_noise", 1) expected_moments.add_noise(Instruction(noise_1qubit, 1), "gate_noise", 2) assert circ.moments == expected_moments
def apply_gate_noise( self, noise: Union[Type[Noise], Iterable[Type[Noise]]], target_gates: Optional[Union[Type[Gate], Iterable[Type[Gate]]]] = None, target_unitary: np.ndarray = None, target_qubits: Optional[QubitSetInput] = None, ) -> Circuit: """Apply `noise` to the circuit according to `target_gates`, `target_unitary` and `target_qubits`. For any parameter that is None, that specification is ignored (e.g. if `target_gates` is None then the noise is applied after every gate in `target_qubits`). If `target_gates` and `target_qubits` are both None, then `noise` is applied to every qubit after every gate. Noise is either applied to `target_gates` or `target_unitary`, so they cannot be provided at the same time. When `noise.qubit_count` == 1, ie. `noise` is single-qubit, `noise` is added to all qubits in `target_gates` or `target_unitary` (or to all qubits in `target_qubits` if `target_gates` is None). When `noise.qubit_count` > 1 and `target_gates` is not None, the number of qubits of any gate in `target_gates` must be the same as `noise.qubit_count`. When `noise.qubit_count` > 1, `target_gates` and `target_unitary` is None, noise is only applied to gates with the same qubit_count in target_qubits. Args: noise (Union[Type[Noise], Iterable[Type[Noise]]]): Noise channel(s) to be applied to the circuit. target_gates (Union[Type[Gate], Iterable[Type[Gate]], optional]): Gate class or List of Gate classes which `noise` is applied to. Default=None. target_unitary (np.ndarray): matrix of the target unitary gates. Default=None. target_qubits (Union[QubitSetInput, optional]): Index or indices of qubit(s). Default=None. Returns: Circuit: self Raises: TypeError: If `noise` is not Noise type. If `target_gates` is not a Gate type, Iterable[Gate]. If `target_unitary` is not a np.ndarray type. If `target_qubits` has non-integers or negative integers. IndexError: If applying noise to an empty circuit. If `target_qubits` is out of range of circuit.qubits. ValueError: If both `target_gates` and `target_unitary` are provided. If `target_unitary` is not a unitary. If `noise` is multi-qubit noise and `target_gates` contain gates with the number of qubits not the same as `noise.qubit_count`. Warning: If `noise` is multi-qubit noise while there is no gate with the same number of qubits in `target_qubits` or in the whole circuit when `target_qubits` is not given. If no `target_gates` or `target_unitary` exist in `target_qubits` or in the whole circuit when they are not given. Examples: >>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1) >>> print(circ) T : |0|1|2| q0 : -X-Z-C- | q1 : -Y-X-X- T : |0|1|2| >>> noise = Noise.Depolarizing(probability=0.1) >>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1) >>> print(circ.apply_gate_noise(noise, target_gates = Gate.X)) T : | 0 | 1 |2| q0 : -X-DEPO(0.1)-Z-----------C- | q1 : -Y-----------X-DEPO(0.1)-X- T : | 0 | 1 |2| >>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1) >>> print(circ.apply_gate_noise(noise, target_qubits = 1)) T : | 0 | 1 | 2 | q0 : -X-----------Z-----------C----------- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X-DEPO(0.1)- T : | 0 | 1 | 2 | >>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1) >>> print(circ.apply_gate_noise(noise, ... target_gates = [Gate.X,Gate.Y], ... target_qubits = [0,1]) ... ) T : | 0 | 1 |2| q0 : -X-DEPO(0.1)-Z-----------C- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X- T : | 0 | 1 |2| """ # check whether gate noise is applied to an empty circuit if not self.qubits: raise IndexError( "Gate noise cannot be applied to an empty circuit.") # check if target_gates and target_unitary are both given if (target_unitary is not None) and (target_gates is not None): raise ValueError( "target_unitary and target_gates cannot be input at the same time." ) # check target_qubits target_qubits = check_noise_target_qubits(self, target_qubits) if not all(qubit in self.qubits for qubit in target_qubits): raise IndexError( "target_qubits must be within the range of the current circuit." ) # make noise a list noise = wrap_with_list(noise) # make target_gates a list if target_gates is not None: target_gates = wrap_with_list(target_gates) # remove duplicate items target_gates = list(dict.fromkeys(target_gates)) for noise_channel in noise: if not isinstance(noise_channel, Noise): raise TypeError("Noise must be an instance of the Noise class") # check whether target_gates is valid if target_gates is not None: check_noise_target_gates(noise_channel, target_gates) if target_unitary is not None: check_noise_target_unitary(noise_channel, target_unitary) if target_unitary is not None: return apply_noise_to_gates(self, noise, target_unitary, target_qubits) else: return apply_noise_to_gates(self, noise, target_gates, target_qubits)