Esempio n. 1
0
def apply_noise_to_moments(
    circuit: Circuit, noise: Iterable[Type[Noise]], target_qubits: QubitSet, position: str
) -> Circuit:
    """
    Apply initialization/readout noise to the circuit.

    When `noise.qubit_count` == 1, `noise` is added to all qubits in `target_qubits`.

    When `noise.qubit_count` > 1, `noise.qubit_count` must be the same as the length of
    `target_qubits`.

    Args:
        circuit (Circuit): A ciruit where `noise` is applied to.
        noise (Iterable[Type[Noise]]): Noise channel(s) to be applied
            to the circuit.
        target_qubits (QubitSet): Index or indices of qubits. `noise` is applied to.

    Returns:
        Circuit: modified circuit.
    """
    noise_instructions = []
    for noise_channel in noise:
        if noise_channel.qubit_count == 1:
            new = [Instruction(noise_channel, qubit) for qubit in target_qubits]
            noise_instructions = noise_instructions + new
        else:
            noise_instructions.append(Instruction(noise_channel, target_qubits))

    new_moments = Moments()

    if position == "initialization":
        for noise in noise_instructions:
            new_moments.add_noise(noise, "initialization_noise")

    # add existing instructions
    for moment_key in circuit.moments:
        instruction = circuit.moments[moment_key]
        # if the instruction is noise instruction
        if isinstance(instruction.operator, Noise):
            new_moments.add_noise(instruction, moment_key.moment_type, moment_key.noise_index)
        # if the instruction is a gate instruction
        else:
            new_moments.add([instruction], moment_key.noise_index)

    if position == "readout":
        for noise in noise_instructions:
            new_moments.add_noise(noise, "readout_noise")

    circuit._moments = new_moments

    return circuit
Esempio n. 2
0
def apply_noise_to_gates(
    circuit: Circuit,
    noise: Iterable[Type[Noise]],
    target_gates: Union[Iterable[Type[Gate]], np.ndarray],
    target_qubits: QubitSet,
) -> Circuit:
    """Apply noise after target gates in target qubits.

    When `noise.qubit_count` == 1, `noise` is applied to target_qubits after `target_gates`.

    When `noise.qubit_count` > 1, all elements in `target_gates`, if is given, must have
    the same number of qubits as `noise.qubit_count`.

    Args:
        circuit (Circuit): A ciruit where `noise` is applied to.
        noise (Iterable[Type[Noise]]): Noise channel(s) to be applied
            to the circuit.
        target_gates (Union[Iterable[Type[Gate]], np.ndarray]): List of gates, or a unitary matrix
            which `noise` is applied to.
        target_qubits (QubitSet): Index or indices of qubits which `noise` is applied to.

    Returns:
        Circuit: modified circuit.

    Raises:
        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` exist in `target_qubits` or in the whole circuit
            when `target_qubits` is not given.
    """

    new_moments = Moments()
    noise_applied = False

    for moment_key in circuit.moments:
        instruction = circuit.moments[moment_key]

        # add the instruction to new_moments if it is noise instruction
        if isinstance(instruction.operator, Noise):
            new_moments.add_noise(instruction, moment_key.moment_type, moment_key.noise_index)

        # if the instruction is a gate instruction
        else:
            new_noise_instruction = []
            noise_index = moment_key.noise_index
            if isinstance(target_gates, np.ndarray):
                if (instruction.operator.name == "Unitary") and (
                    np.array_equiv(instruction.operator._matrix, target_gates)
                ):
                    intersection = list(set(instruction.target) & set(target_qubits))
                    (
                        new_noise_instruction,
                        noise_index,
                        noise_applied,
                    ) = _apply_noise_to_gates_helper(
                        noise,
                        target_qubits,
                        instruction,
                        noise_index,
                        intersection,
                        noise_applied,
                        new_noise_instruction,
                    )

            elif (target_gates is None) or (
                instruction.operator.name in [g.__name__ for g in target_gates]
            ):
                intersection = list(set(instruction.target) & set(target_qubits))
                new_noise_instruction, noise_index, noise_applied = _apply_noise_to_gates_helper(
                    noise,
                    target_qubits,
                    instruction,
                    noise_index,
                    intersection,
                    noise_applied,
                    new_noise_instruction,
                )

            # add the gate and gate noise instructions to new_moments
            new_moments.add([instruction], noise_index=noise_index)
            for instruction, noise_index in new_noise_instruction:
                new_moments.add_noise(instruction, "gate_noise", noise_index)

    no_noise_applied_warning(noise_applied)
    circuit._moments = new_moments
    return circuit