def test_channel_process_fidelity(self):
        """Test the process_fidelity function for channel inputs"""
        depol = Choi(np.eye(4) / 2)
        iden = Choi(Operator.from_label('I'))

        # Completely depolarizing channel
        f_pro = process_fidelity(depol, require_cp=True, require_tp=True)
        self.assertAlmostEqual(f_pro, 0.25, places=7)

        # Identity
        f_pro = process_fidelity(iden, require_cp=True, require_tp=True)
        self.assertAlmostEqual(f_pro, 1.0, places=7)

        # Depolarizing channel
        prob = 0.3
        chan = prob * depol + (1 - prob) * iden
        f_pro = process_fidelity(chan, require_cp=True, require_tp=True)
        f_target = prob * 0.25 + (1 - prob)
        self.assertAlmostEqual(f_pro, f_target, places=7)

        # Depolarizing channel
        prob = 0.5
        op = Operator.from_label('Y')
        chan = (prob * depol + (1 - prob) * iden) @ op
        f_pro = process_fidelity(chan, op, require_cp=True, require_tp=True)
        target = prob * 0.25 + (1 - prob)
        self.assertAlmostEqual(f_pro, target, places=7)
Esempio n. 2
0
    def test_channel_average_gate_fidelity(self):
        """Test the average_gate_fidelity function for channel inputs"""
        depol = Choi(np.eye(4) / 2)
        iden = Choi(Operator.from_label('I'))

        # Completely depolarizing channel
        f_ave = average_gate_fidelity(depol, require_cp=True, require_tp=True)
        self.assertAlmostEqual(f_ave, 0.5, places=7)

        # Identity
        f_ave = average_gate_fidelity(iden, require_cp=True, require_tp=True)
        self.assertAlmostEqual(f_ave, 1.0, places=7)

        # Depolarizing channel
        prob = 0.11
        chan = prob * depol + (1 - prob) * iden
        f_ave = average_gate_fidelity(chan, require_cp=True, require_tp=True)
        f_target = (2 * (prob * 0.25 + (1 - prob)) + 1) / 3
        self.assertAlmostEqual(f_ave, f_target, places=7)

        # Depolarizing channel
        prob = 0.5
        op = Operator.from_label('Y')
        chan = (prob * depol + (1 - prob) * iden).compose(op)
        f_ave = average_gate_fidelity(chan,
                                      op,
                                      require_cp=True,
                                      require_tp=True)
        target = (2 * (prob * 0.25 + (1 - prob)) + 1) / 3
        self.assertAlmostEqual(f_ave, target, places=7)
Esempio n. 3
0
    def _fidelity_result(
        state_result: AnalysisResultData,
        target: Union[Choi, DensityMatrix],
        input_dim: int = 1,
    ):
        """Faster computation of fidelity from eigen decomposition"""
        evals = state_result.extra["eigvals"]
        evecs = state_result.extra["eigvecs"]

        # Format target to statevector or densitymatrix array
        name = "process_fidelity" if input_dim > 1 else "state_fidelity"
        if target is None:
            raise AnalysisError("No target state provided")
        if isinstance(target, QuantumChannel):
            target_state = Choi(target).data / input_dim
        elif isinstance(target, BaseOperator):
            target_state = np.ravel(Operator(target),
                                    order="F") / np.sqrt(input_dim)
        else:
            # Statevector or density matrix
            target_state = np.array(target)

        if target_state.ndim == 1:
            rho = evecs @ (evals / input_dim * evecs).T.conj()
            fidelity = np.real(target_state.conj() @ rho @ target_state)
        else:
            sqrt_rho = evecs @ (np.sqrt(evals / input_dim) * evecs).T.conj()
            eig = la.eigvalsh(sqrt_rho @ target_state @ sqrt_rho)
            fidelity = np.sum(np.sqrt(np.maximum(eig, 0)))**2
        return AnalysisResultData(name, fidelity)
Esempio n. 4
0
 def test_noncp_process_fidelity(self):
     """Test process_fidelity for non-CP channel"""
     u1 = Operator.from_label('X')
     u2 = Operator.from_label('Z')
     chan = 1.01 * Choi(u1) - 0.01 * Choi(u2)
     fid = process_fidelity(chan)
     self.assertLogs('qiskit.quantum_info.operators.measures', level='WARNING')
     self.assertAlmostEqual(fid, 0, places=15)
    def _target_quantum_channel(self) -> Union[Choi, Operator]:
        """Return the process tomography target"""
        # Check if circuit contains measure instructions
        # If so we cannot return target state
        circuit_ops = self._circuit.count_ops()
        if "measure" in circuit_ops:
            return None

        try:
            circuit = self._permute_circuit()
            if "reset" in circuit_ops or "kraus" in circuit_ops or "superop" in circuit_ops:
                channel = Choi(circuit)
            else:
                channel = Operator(circuit)
        except QiskitError:
            # Circuit couldn't be simulated
            return None

        total_qubits = self._circuit.num_qubits
        num_meas = total_qubits if self._meas_qubits is None else len(self._meas_qubits)
        num_prep = total_qubits if self._prep_qubits is None else len(self._prep_qubits)

        # If all qubits are prepared or measurement we are done
        if num_meas == total_qubits and num_prep == total_qubits:
            return channel

        # Convert channel to a state to project and trace out non-tomography
        # input and output qubits
        if isinstance(channel, Operator):
            chan_state = Statevector(np.ravel(channel, order="F"))
        else:
            chan_state = DensityMatrix(channel.data)

        # Get qargs for non measured and prepared subsystems
        non_meas_qargs = list(range(num_meas, total_qubits))
        non_prep_qargs = list(range(total_qubits + num_prep, 2 * total_qubits))

        # Project non-prepared subsystems on to the zero state
        if non_prep_qargs:
            proj0 = Operator([[1, 0], [0, 0]])
            for qarg in non_prep_qargs:
                chan_state = chan_state.evolve(proj0, [qarg])

        # Trace out indices to remove
        tr_qargs = non_meas_qargs + non_prep_qargs
        chan_state = partial_trace(chan_state, tr_qargs)
        channel = Choi(chan_state.data, input_dims=[2] * num_prep, output_dims=[2] * num_meas)
        return channel
Esempio n. 6
0
 def test_rank(self, rank):
     """Test random_quantum_channel with fixed rank {rank}"""
     choi = Choi(random_quantum_channel(2, rank=rank))
     # Get number of non-zero eigenvalues
     evals = np.linalg.eigvals(choi.data).round(8)
     value = len(evals[evals > 0])
     self.assertEqual(value, rank)
    def _join_input_vector(self,
                           E: np.array,
                           rho: np.array,
                           Gs: List[np.array]
                           ) -> np.array:
        """Converts the GST data into a vector representation
        Args:
            E: The POVM measurement operator
            rho: The initial state
            Gs: The gates list

        Returns:
            The vector representation of (E, rho, Gs)

        Additional information:
            This function performs the inverse operation to
            split_input_vector; the notations are the same.
        """
        d = (2 ** self.qubits)

        E_T = get_cholesky_like_decomposition(E.reshape((d, d)))
        rho_T = get_cholesky_like_decomposition(rho.reshape((d, d)))
        Gs_Choi = [Choi(PTM(G)).data for G in Gs]
        Gs_T = [get_cholesky_like_decomposition(G) for G in Gs_Choi]
        E_vec = self._complex_matrix_to_vec(E_T)
        rho_vec = self._complex_matrix_to_vec(rho_T)
        result = E_vec + rho_vec
        for G_T in Gs_T:
            result += self._complex_matrix_to_vec(G_T)
        return np.array(result)
 def test_nontp_process_fidelity(self):
     """Test process_fidelity for non-TP channel"""
     chan = 0.99 * Choi(Operator.from_label('X'))
     fid = process_fidelity(chan)
     self.assertLogs('qiskit.quantum_info.operators.measures',
                     level='WARNING')
     self.assertAlmostEqual(fid, 0, places=15)
def run_circuit_and_tomography(circuit, qubits, method):
    choi_ideal = Choi(circuit).data
    qst = tomo.process_tomography_circuits(circuit, qubits)
    job = qiskit.execute(qst, Aer.get_backend('qasm_simulator'), shots=5000)
    tomo_fit = tomo.ProcessTomographyFitter(job.result(), qst)
    choi = tomo_fit.fit(method=method).data
    return (choi, choi_ideal)
Esempio n. 10
0
    def test_channel_gate_error(self):
        """Test the gate_error function for channel inputs"""
        depol = Choi(np.eye(4) / 2)
        iden = Choi(Operator.from_label('I'))

        # Depolarizing channel
        prob = 0.11
        chan = prob * depol + (1 - prob) * iden
        err = gate_error(chan, require_cp=True, require_tp=True)
        target = 1 - average_gate_fidelity(chan)
        self.assertAlmostEqual(err, target, places=7)

        # Depolarizing channel
        prob = 0.5
        op = Operator.from_label('Y')
        chan = (prob * depol + (1 - prob) * iden) @ op
        err = gate_error(chan, op, require_cp=True, require_tp=True)
        target = 1 - average_gate_fidelity(chan, op)
        self.assertAlmostEqual(err, target, places=7)
Esempio n. 11
0
    def test_diamond_norm(self, num_qubits):
        """Test the diamond_norm for {num_qubits}-qubit pauli channel."""
        try:
            import cvxpy
        except ImportError:
            # Skip test if CVXPY not installed
            self.skipTest("CVXPY not installed.")

        # Pauli channels have an analytic expression for the
        # diamond norm so we can easily test it
        op = Choi(np.zeros((4 ** num_qubits, 4 ** num_qubits)))
        labels = [num_qubits * i for i in ['I', 'X', 'Y', 'Z']]
        coeffs = [-1.0, 0.5, 2.5, -0.1]
        for coeff, label in zip(coeffs, labels):
            op = op + coeff * Choi(Operator.from_label(label))
        target = np.sum(np.abs(coeffs))

        try:
            value = diamond_norm(op)
            self.assertAlmostEqual(value, target, places=4)
        except cvxpy.SolverError:
            self.skipTest("CVXPY solver failed.")
Esempio n. 12
0
    def _fidelity_result(evals, evecs, target, qpt=False):
        """Faster computation of fidelity from eigen decomposition"""
        # Format target to statevector or densitymatrix array
        trace = np.sqrt(len(evals)) if qpt else 1
        name = "process_fidelity" if qpt else "state_fidelity"
        if target is None:
            raise AnalysisError("No target state provided")
        if isinstance(target, QuantumChannel):
            target_state = Choi(target).data / trace
        elif isinstance(target, BaseOperator):
            target_state = np.ravel(Operator(target), order="F") / np.sqrt(trace)
        else:
            target_state = np.array(target)

        if target_state.ndim == 1:
            rho = evecs @ (evals / trace * evecs).T.conj()
            fidelity = np.real(target_state.conj() @ rho @ target_state)
        else:
            sqrt_rho = evecs @ (np.sqrt(evals / trace) * evecs).T.conj()
            eig = la.eigvalsh(sqrt_rho @ target_state @ sqrt_rho)
            fidelity = np.sum(np.sqrt(np.maximum(eig, 0))) ** 2
        return AnalysisResultData(name, fidelity)
Esempio n. 13
0
    def _run_analysis(self, experiment_data, **options):
        # Extract tomography measurement data
        outcome_data, shot_data, measurement_data, preparation_data = self._fitter_data(
            experiment_data.data())

        # Get tomography options
        measurement_basis = options.pop("measurement_basis")
        preparation_basis = options.pop("preparation_basis", None)
        rescale_positive = options.pop("rescale_positive")
        rescale_trace = options.pop("rescale_trace")
        target_state = options.pop("target")

        # Get target state from circuit metadata
        if target_state == "default":
            metadata = experiment_data.metadata
            target_state = metadata.get("target", None)

        # Get tomography fitter function
        fitter = self._get_fitter(options.pop("fitter", None))
        try:
            t_fitter_start = time.time()
            state, fitter_metadata = fitter(
                outcome_data,
                shot_data,
                measurement_data,
                preparation_data,
                measurement_basis,
                preparation_basis,
                **options,
            )
            t_fitter_stop = time.time()
            if fitter_metadata is None:
                fitter_metadata = {}
            state = Choi(state) if preparation_basis else DensityMatrix(state)
            fitter_metadata["fitter"] = fitter.__name__
            fitter_metadata["fitter_time"] = t_fitter_stop - t_fitter_start

            analysis_results = self._postprocess_fit(
                state,
                metadata=fitter_metadata,
                target_state=target_state,
                rescale_positive=rescale_positive,
                rescale_trace=rescale_trace,
                qpt=bool(preparation_basis),
            )

        except AnalysisError as ex:
            raise AnalysisError(
                f"Tomography fitter failed with error: {str(ex)}") from ex

        return analysis_results, []
Esempio n. 14
0
    def _state_result(
        cls,
        fit: np.ndarray,
        make_positive: bool = False,
        trace: Optional[float] = None,
        input_dims: Optional[Tuple[int, ...]] = None,
        output_dims: Optional[Tuple[int, ...]] = None,
    ) -> AnalysisResultData:
        """Convert fit data to state result data"""
        # Get eigensystem of state fit
        raw_eigvals, eigvecs = cls._state_eigensystem(fit)

        # Optionally rescale eigenvalues to be non-negative
        if make_positive and np.any(raw_eigvals < 0):
            eigvals = cls._make_positive(raw_eigvals)
            fit = eigvecs @ (eigvals * eigvecs).T.conj()
            rescaled_psd = True
        else:
            eigvals = raw_eigvals
            rescaled_psd = False

        # Optionally rescale fit trace
        fit_trace = np.sum(eigvals)
        if trace is not None and not np.isclose(
                fit_trace - trace, 0, atol=1e-12):
            scale = trace / fit_trace
            fit = fit * scale
            eigvals = eigvals * scale
        else:
            trace = fit_trace

        # Convert class of value
        if input_dims and np.prod(input_dims) > 1:
            value = Choi(fit, input_dims=input_dims, output_dims=output_dims)
        else:
            value = DensityMatrix(fit, dims=output_dims)

        # Construct state result extra metadata
        extra = {
            "trace": trace,
            "eigvals": eigvals,
            "raw_eigvals": raw_eigvals,
            "rescaled_psd": rescaled_psd,
            "eigvecs": eigvecs,
        }
        return AnalysisResultData("state", value, extra=extra)
Esempio n. 15
0
    def _run_analysis(self, experiment_data):
        # Extract tomography measurement data
        outcome_data, shot_data, measurement_data, preparation_data = self._fitter_data(
            experiment_data.data())

        # Get tomography fitter function
        fitter = self._get_fitter(self.options.fitter)
        try:
            t_fitter_start = time.time()
            state, fitter_metadata = fitter(
                outcome_data,
                shot_data,
                measurement_data,
                preparation_data,
                self.options.measurement_basis,
                self.options.preparation_basis,
                **self.options.fitter_options,
            )
            t_fitter_stop = time.time()
            if fitter_metadata is None:
                fitter_metadata = {}
            state = Choi(
                state) if self.options.preparation_basis else DensityMatrix(
                    state)
            fitter_metadata["fitter"] = fitter.__name__
            fitter_metadata["fitter_time"] = t_fitter_stop - t_fitter_start

            analysis_results = self._postprocess_fit(
                state,
                metadata=fitter_metadata,
                target_state=self.options.target,
                rescale_positive=self.options.rescale_positive,
                rescale_trace=self.options.rescale_trace,
                qpt=bool(self.options.preparation_basis),
            )

        except AnalysisError as ex:
            raise AnalysisError(
                f"Tomography fitter failed with error: {str(ex)}") from ex

        return analysis_results, []
    def _split_input_vector(self, x: np.array) -> Tuple:
        """Reconstruct the GST data from its vector representation
        Args:
            x: The vector representation of the GST data

        Returns:
            The GST data (E, rho, Gs) (see additional info)

        Additional information:
            The gate set tomography data is a tuple (E, rho, Gs) consisting of
            1) A POVM measurement operator E
            2) An initial quantum state rho
            3) A list Gs = (G1, G2, ..., Gk) of gates, represented as matrices

            This function reconstructs (E, rho, Gs) from the vector x
            Since the MLE optimization procedure has PSD constraints on
            E, rho and the Choi represetnation of the PTM of the Gs,
            we rely on the following property: M is PSD iff there exists
            T such that M = T @ T^{dagger}.
            Hence, x stores those T matrices for E, rho and the Gs
        """
        n = len(self.Gs)
        d = (2 ** self.qubits)
        ds = d ** 2  # d squared - the dimension of the density operator

        d_t = 2 * d ** 2
        ds_t = 2 * ds ** 2
        T_vars = self._split_list(x, [d_t, d_t] + [ds_t] * n)

        E_T = self._vec_to_complex_matrix(T_vars[0])
        rho_T = self._vec_to_complex_matrix(T_vars[1])
        Gs_T = [self._vec_to_complex_matrix(T_vars[2+i]) for i in range(n)]

        E = np.reshape(E_T @ np.conj(E_T.T), (1, ds))
        rho = np.reshape(rho_T @ np.conj(rho_T.T), (ds, 1))
        Gs = [PTM(Choi(G_T @ np.conj(G_T.T))).data for G_T in Gs_T]

        return (E, rho, Gs)