def test_compose(self): """Test compose method.""" op1 = Operator(self.UX) op2 = Operator(self.UY) targ = Operator(np.dot(self.UY, self.UX)) self.assertEqual(op1.compose(op2), targ) self.assertEqual(op1 @ op2, targ) targ = Operator(np.dot(self.UX, self.UY)) self.assertEqual(op2.compose(op1), targ) self.assertEqual(op2 @ op1, targ)
def test_compose_subsystem(self): """Test subsystem compose method.""" # 3-qubit operator mat = self.rand_matrix(8, 8) mat_a = self.rand_matrix(2, 2) mat_b = self.rand_matrix(2, 2) mat_c = self.rand_matrix(2, 2) op = Operator(mat) op1 = Operator(mat_a) op2 = Operator(np.kron(mat_b, mat_a)) op3 = Operator(np.kron(mat_c, np.kron(mat_b, mat_a))) # op3 qargs=[0, 1, 2] targ = np.dot(np.kron(mat_c, np.kron(mat_b, mat_a)), mat) self.assertEqual(op.compose(op3, qargs=[0, 1, 2]), Operator(targ)) self.assertEqual(op.compose(op3([0, 1, 2])), Operator(targ)) self.assertEqual(op & op3([0, 1, 2]), Operator(targ)) # op3 qargs=[2, 1, 0] targ = np.dot(np.kron(mat_a, np.kron(mat_b, mat_c)), mat) self.assertEqual(op.compose(op3, qargs=[2, 1, 0]), Operator(targ)) self.assertEqual(op & op3([2, 1, 0]), Operator(targ)) # op2 qargs=[0, 1] targ = np.dot(np.kron(np.eye(2), np.kron(mat_b, mat_a)), mat) self.assertEqual(op.compose(op2, qargs=[0, 1]), Operator(targ)) self.assertEqual(op & op2([0, 1]), Operator(targ)) # op2 qargs=[2, 0] targ = np.dot(np.kron(mat_a, np.kron(np.eye(2), mat_b)), mat) self.assertEqual(op.compose(op2, qargs=[2, 0]), Operator(targ)) self.assertEqual(op & op2([2, 0]), Operator(targ)) # op1 qargs=[0] targ = np.dot(np.kron(np.eye(4), mat_a), mat) self.assertEqual(op.compose(op1, qargs=[0]), Operator(targ)) self.assertEqual(op & op1([0]), Operator(targ)) # op1 qargs=[1] targ = np.dot(np.kron(np.eye(2), np.kron(mat_a, np.eye(2))), mat) self.assertEqual(op.compose(op1, qargs=[1]), Operator(targ)) self.assertEqual(op & op1([1]), Operator(targ)) # op1 qargs=[2] targ = np.dot(np.kron(mat_a, np.eye(4)), mat) self.assertEqual(op.compose(op1, qargs=[2]), Operator(targ)) self.assertEqual(op & op1([2]), Operator(targ))
def test_compose_front_subsystem(self): """Test subsystem front compose method.""" # 3-qubit operator mat = self.rand_matrix(8, 8) mat_a = self.rand_matrix(2, 2) mat_b = self.rand_matrix(2, 2) mat_c = self.rand_matrix(2, 2) op = Operator(mat) op1 = Operator(mat_a) op2 = Operator(np.kron(mat_b, mat_a)) op3 = Operator(np.kron(mat_c, np.kron(mat_b, mat_a))) # op3 qubits=[0, 1, 2] targ = np.dot(mat, np.kron(mat_c, np.kron(mat_b, mat_a))) self.assertEqual(op.compose(op3, qubits=[0, 1, 2], front=True), Operator(targ)) # op3 qubits=[2, 1, 0] targ = np.dot(mat, np.kron(mat_a, np.kron(mat_b, mat_c))) self.assertEqual(op.compose(op3, qubits=[2, 1, 0], front=True), Operator(targ)) # op2 qubits=[0, 1] targ = np.dot(mat, np.kron(np.eye(2), np.kron(mat_b, mat_a))) self.assertEqual(op.compose(op2, qubits=[0, 1], front=True), Operator(targ)) # op2 qubits=[2, 0] targ = np.dot(mat, np.kron(mat_a, np.kron(np.eye(2), mat_b))) self.assertEqual(op.compose(op2, qubits=[2, 0], front=True), Operator(targ)) # op1 qubits=[0] targ = np.dot(mat, np.kron(np.eye(4), mat_a)) self.assertEqual(op.compose(op1, qubits=[0], front=True), Operator(targ)) # op1 qubits=[1] targ = np.dot(mat, np.kron(np.eye(2), np.kron(mat_a, np.eye(2)))) self.assertEqual(op.compose(op1, qubits=[1], front=True), Operator(targ)) # op1 qubits=[2] targ = np.dot(mat, np.kron(mat_a, np.eye(4))) self.assertEqual(op.compose(op1, qubits=[2], front=True), Operator(targ))
def process_fidelity(channel, target=None, require_cp=True, require_tp=False, require_cptp=False): r"""Return the process fidelity of a noisy quantum channel. This process fidelity :math:`F_{\text{pro}}` is given by .. math:: F_{\text{pro}}(\mathcal{E}, U) = \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2} where :math:`S_{\mathcal{E}}, S_{U}` are the :class:`~qiskit.quantum_info.SuperOp` matrices for the input quantum *channel* :math:`\cal{E}` and *target* unitary :math:`U` respectively, and :math:`d` is the dimension of the *channel*. Args: channel (QuantumChannel or Operator): noisy quantum channel. target (Operator or None): target unitary operator. If `None` target is the identity operator [Default: None]. require_cp (bool): require channel to be completely-positive [Default: True]. require_tp (bool): require channel to be trace-preserving [Default: False]. require_cptp (bool): (DEPRECATED) require input channels to be CPTP [Default: False]. Returns: float: The process fidelity :math:`F_{\text{pro}}`. Raises: QiskitError: if the channel and target do not have the same dimensions, or have different input and output dimensions. QiskitError: if the channel and target or are not completely-positive (with ``require_cp=True``) or not trace-preserving (with ``require_tp=True``). """ # Format inputs if isinstance(channel, (list, np.ndarray, Operator, Pauli, Clifford)): channel = Operator(channel) else: channel = SuperOp(channel) if target is not None: try: target = Operator(target) except QiskitError: raise QiskitError( 'Target channel is not a unitary channel.') if channel.dim != target.dim: raise QiskitError( 'Input quantum channel and target unitary must have the same ' 'dimensions ({} != {}).'.format(channel.dim, target.dim)) # Validate complete-positivity and trace-preserving if require_cptp: # require_cptp kwarg is DEPRECATED # Remove in future qiskit version warnings.warn( "Please use `require_cp=True, require_tp=True` " "instead of `require_cptp=True`.", DeprecationWarning) require_cp = True require_tp = True if require_cp or require_tp: # Validate target channel if target is not None: is_unitary = target.is_unitary() if require_cp and not is_unitary: raise QiskitError('Target unitary channel is not completely-positive') if require_tp and not is_unitary: raise QiskitError('Target unitary channel is not trace-preserving') # Validate input channel if isinstance(channel, Operator): is_unitary = channel.is_unitary() # Validate as unitary if require_cp and not is_unitary: raise QiskitError('Input quantum channel is not completely-positive') if require_tp and not is_unitary: raise QiskitError('Input quantum channel is not trace-preserving') else: # Validate as QuantumChannel if require_cp and not channel.is_cp(): raise QiskitError('Input quantum channel is not completely-positive') if require_tp and not channel.is_tp(): raise QiskitError('Input quantum channel is not trace-preserving') # Compute process fidelity with identity channel if target is not None: channel = channel.compose(target.adjoint()) input_dim, _ = channel.dim if isinstance(channel, Operator): # |Tr[U]/dim| ** 2 fid = np.abs(np.trace(channel.data) / input_dim)**2 else: # Tr[S] / (dim ** 2) fid = np.trace(SuperOp(channel).data) / (input_dim**2) return float(np.real(fid))