Пример #1
0
 def _compose_instr(instr0, instr1, num_qubits):
     """Helper function for compose a kraus with another instruction."""
     # If one of the instructions is an identity we only need
     # to return the other instruction
     if instr0['name'] == 'id':
         return instr1
     if instr1['name'] == 'id':
         return instr0
     # Convert to ops
     op0 = QuantumError._instr2op(instr0)
     op1 = QuantumError._instr2op(instr1)
     # Check if at least one of the instructions is a channel
     # and if so convert both to SuperOp representation
     if isinstance(op0,
                   (SuperOp, Kraus)) or isinstance(op1, (SuperOp, Kraus)):
         name = 'kraus'
         op0 = SuperOp(op0)
         op1 = SuperOp(op1)
     else:
         name = 'unitary'
     # Check qubits for compositions
     qubits0 = instr0['qubits']
     qubits1 = instr1['qubits']
     if qubits0 == qubits1:
         composed = op0.compose(op1)
         qubits = qubits0
     else:
         # If qubits don't match we compose with total number of qubits
         # for the error
         if name == 'kraus':
             composed = SuperOp(np.eye(4**num_qubits))
         else:
             composed = Operator(np.eye(2**num_qubits))
         composed = composed.compose(op0,
                                     qargs=qubits0).compose(op1,
                                                            qargs=qubits1)
         qubits = list(range(num_qubits))
     # Get instruction params
     if name == 'kraus':
         params = Kraus(composed).data
     else:
         params = [composed.data]
     return {'name': name, 'qubits': qubits, 'params': params}
Пример #2
0
 def _combine_kraus(noise_ops, num_qubits):
     """Combine any noise circuits containing only Kraus instructions."""
     kraus_instr = []
     kraus_probs = []
     new_circuits = []
     new_probs = []
     # Partion circuits into Kraus and non-Kraus
     for circuit, prob in noise_ops:
         if len(circuit) == 1 and circuit[0]['name'] == 'kraus':
             kraus_instr.append(circuit[0])
             kraus_probs.append(prob)
         else:
             new_circuits.append(circuit)
             new_probs.append(prob)
     # Combine matching Kraus instructions via Choi rep
     if len(kraus_probs) == 1:
         new_circuits.append([kraus_instr[0]])
         new_probs.append(kraus_probs[0])
     elif len(kraus_probs) > 1:
         dim = 2**num_qubits
         iden = SuperOp(np.eye(dim**2))
         choi_sum = Choi(np.zeros((dim**2, dim**2)))
         for prob, instr in zip(kraus_probs, kraus_instr):
             choi_sum = choi_sum + prob * iden.compose(
                 Kraus(instr['params']), instr['qubits'])
         # Renormalize the Choi operator to find probability
         # of Kraus error
         chan_prob = abs(np.trace(choi_sum.data) / dim)
         chan_instr = {
             "name": "kraus",
             "qubits": list(range(num_qubits)),
             "params": Kraus(choi_sum / chan_prob).data
         }
         new_circuits.append([chan_instr])
         new_probs.append(chan_prob)
     return list(zip(new_circuits, new_probs))
Пример #3
0
def process_fidelity(channel1, channel2, require_cptp=True):
    """Return the process fidelity between two quantum channels.

    This is given by

        F_p(E1, E2) = Tr[S2^dagger.S1])/dim^2

    where S1 and S2 are the SuperOp matrices for channels E1 and E2,
    and dim is the dimension of the input output statespace.

    Args:
        channel1 (QuantumChannel or matrix): a quantum channel or unitary matrix.
        channel2 (QuantumChannel or matrix): a quantum channel or unitary matrix.
        require_cptp (bool): require input channels to be CPTP [Default: True].

    Returns:
        array_like: The state fidelity F(state1, state2).

    Raises:
        QiskitError: if inputs channels do not have the same dimensions,
        have different input and output dimensions, or are not CPTP with
        `require_cptp=True`.
    """
    # First we must determine if input is to be interpreted as a unitary matrix
    # or as a channel.
    # If input is a raw numpy array we will interpret it as a unitary matrix.
    is_cptp1 = None
    is_cptp2 = None
    if isinstance(channel1, (list, np.ndarray)):
        channel1 = Operator(channel1)
        if require_cptp:
            is_cptp1 = channel1.is_unitary()
    if isinstance(channel2, (list, np.ndarray)):
        channel2 = Operator(channel2)
        if require_cptp:
            is_cptp2 = channel2.is_unitary()

    # Next we convert inputs SuperOp objects
    # This works for objects that also have a `to_operator` or `to_channel` method
    s1 = SuperOp(channel1)
    s2 = SuperOp(channel2)

    # Check inputs are CPTP
    if require_cptp:
        # Only check SuperOp if we didn't already check unitary inputs
        if is_cptp1 is None:
            is_cptp1 = s1.is_cptp()
        if not is_cptp1:
            raise QiskitError('channel1 is not CPTP')
        if is_cptp2 is None:
            is_cptp2 = s2.is_cptp()
        if not is_cptp2:
            raise QiskitError('channel2 is not CPTP')

    # Check dimensions match
    input_dim1, output_dim1 = s1.dim
    input_dim2, output_dim2 = s2.dim
    if input_dim1 != output_dim1 or input_dim2 != output_dim2:
        raise QiskitError('Input channels must have same size input and output dimensions.')
    if input_dim1 != input_dim2:
        raise QiskitError('Input channels have different dimensions.')

    # Compute process fidelity
    fidelity = np.trace(s1.compose(s2.adjoint()).data) / (input_dim1 ** 2)
    return fidelity