def test_local_pbasis_default_densitymatrix(self):
     """Test default states kwarg"""
     default_states = [qi.random_density_matrix(2, seed=30 + i) for i in range(3)]
     basis = LocalPreparationBasis("fitter_basis", default_states=default_states)
     for i, state in enumerate(default_states):
         basis_state = qi.DensityMatrix(basis.matrix([i], [0]))
         fid = qi.state_fidelity(state, basis_state)
         self.assertTrue(isclose(fid, 1))
 def test_local_pbasis_qubit_states_no_default(self):
     """Test matrix method raises for invalid qubit with no default states"""
     size = 2
     qubits = [0, 2]
     qubit_states = {
         qubits[0]: [qi.random_density_matrix(2, seed=30 + i) for i in range(size)],
         qubits[1]: [qi.random_statevector(2, seed=40 + i) for i in range(size)],
     }
     basis = LocalPreparationBasis("fitter_basis", qubit_states=qubit_states)
     # No default states so should raise an exception
     with self.assertRaises(ValueError):
         basis.matrix([0, 0], [0, 1])
    def test_local_pbasis_default_and_qubit_states(self):
        """Test qubit states kwarg"""
        size = 3
        qubits = [2, 0]
        default_states = [qi.random_density_matrix(2, seed=20 + i) for i in range(size)]
        qubit_states = {2: [qi.random_statevector(2, seed=40 + i) for i in range(size)]}
        basis = LocalPreparationBasis(
            "fitter_basis", default_states=default_states, qubit_states=qubit_states
        )

        # Check states
        indices = it.product(range(size), repeat=2)
        states0 = qubit_states[qubits[0]] if qubits[0] in qubit_states else default_states
        states1 = qubit_states[qubits[1]] if qubits[1] in qubit_states else default_states
        for index in indices:
            basis_state = qi.DensityMatrix(basis.matrix(index, qubits))
            target = qi.DensityMatrix(states0[index[0]]).expand(states1[index[1]])
            fid = qi.state_fidelity(basis_state, target)
            self.assertTrue(isclose(fid, 1))
    def test_local_pbasis_qubit_states(self):
        """Test qubit states kwarg"""
        size = 3
        qubits = [0, 2]
        qubit_states = {
            qubits[0]: [qi.random_density_matrix(2, seed=30 + i) for i in range(size)],
            qubits[1]: [qi.random_statevector(2, seed=40 + i) for i in range(size)],
        }
        basis = LocalPreparationBasis("fitter_basis", qubit_states=qubit_states)

        # Check states
        indices = it.product(range(size), repeat=2)
        for index in indices:
            basis_state = qi.DensityMatrix(basis.matrix(index, qubits))
            target0 = qi.DensityMatrix(qubit_states[qubits[0]][index[0]])
            target1 = qi.DensityMatrix(qubit_states[qubits[1]][index[1]])
            target = target0.expand(target1)
            fid = qi.state_fidelity(basis_state, target)
            self.assertTrue(isclose(fid, 1))
 def test_local_pbasis_no_inst(self):
     """Test circuits method raises if no instructions"""
     default_states = [qi.random_statevector(2, seed=30 + i) for i in range(2)]
     basis = LocalPreparationBasis("fitter_basis", default_states=default_states)
     with self.assertRaises(NotImplementedError):
         basis.circuit([0], [0])
 def test_local_pbasis_unitary(self):
     """Test custom local measurement basis"""
     size = 5
     unitaries = [qi.random_unitary(2, seed=10 + i) for i in range(size)]
     basis = LocalPreparationBasis("unitary_basis", unitaries)
     self._test_ideal_basis(basis, [0, 1])
 def test_local_pbasis_inst(self):
     """Test custom local measurement basis"""
     basis = LocalPreparationBasis("custom_basis", [XGate(), YGate(), HGate()])
     self._test_ideal_basis(basis, [0, 1])
Beispiel #8
0
def linear_inversion(
    outcome_data: np.ndarray,
    shot_data: np.ndarray,
    measurement_data: np.ndarray,
    preparation_data: np.ndarray,
    measurement_basis: Optional[MeasurementBasis] = None,
    preparation_basis: Optional[PreparationBasis] = None,
    measurement_qubits: Optional[Tuple[int, ...]] = None,
    preparation_qubits: Optional[Tuple[int, ...]] = None,
) -> Tuple[np.ndarray, Dict]:
    r"""Linear inversion tomography fitter.

    Overview
        This fitter uses linear inversion to reconstructs the maximum-likelihood
        estimate of the least-squares log-likelihood function

        .. math::
            \hat{\rho}
                &= -\mbox{argmin }\log\mathcal{L}{\rho} \\
                &= \mbox{argmin }\sum_i (\mbox{Tr}[E_j\rho] - \hat{p}_i)^2 \\
                &= \mbox{argmin }\|Ax - y \|_2^2

        where

        * :math:`A = \sum_j |j \rangle\!\langle\!\langle E_j|` is the matrix of measured
          basis elements.
        * :math:`y = \sum_j \hat{p}_j |j\rangle` is the vector of estimated measurement
          outcome probabilites for each basis element.
        * :math:`x = |\rho\rangle\!\rangle` is the vectorized density matrix.

    Additional Details
        The linear inversion solution is given by

        .. math::
            \hat{\rho} = \sum_i \hat{p}_i D_i

        where measurement probabilities :math:`\hat{p}_i = f_i / n_i` are estimated
        from the observed count frequencies :math:`f_i` in :math:`n_i` shots for each
        basis element :math:`i`, and :math:`D_i` is the *dual basis* element constructed
        from basis :math:`\{E_i\}` via:

        .. math:

            |D_i\rangle\!\rangle = M^{-1}|E_i \rangle\!\rangle \\
            M = \sum_j |E_j\rangle\!\rangle\!\langle\!\langle E_j|

    .. note::

        The Linear inversion fitter treats the input measurement and preparation
        bases as local bases and constructs separate 1-qubit dual basis for each
        individual qubit.

        Linear inversion is only possible if the input bases are local and a spanning
        set for the vector space of the reconstructed matrix
        (*tomographically complete*). If the basis is not tomographically complete
        the :func:`~qiskit_experiments.library.tomography.fitters.scipy_linear_lstsq`
        or :func:`~qiskit_experiments.library.tomography.fitters.cvxpy_linear_lstsq`
        function can be used to solve the same objective function via
        least-squares optimization.

    Args:
        outcome_data: basis outcome frequency data.
        shot_data: basis outcome total shot data.
        measurement_data: measurement basis indice data.
        preparation_data: preparation basis indice data.
        measurement_basis: the tomography measurement basis.
        preparation_basis: the tomography preparation basis.
        measurement_qubits: Optional, the physical qubits that were measured.
                            If None they are assumed to be [0, ..., M-1] for
                            M measured qubits.
        preparation_qubits: Optional, the physical qubits that were prepared.
                            If None they are assumed to be [0, ..., N-1] for
                            N preparated qubits.

    Raises:
        AnalysisError: If the fitted vector is not a square matrix

    Returns:
        The fitted matrix rho.
    """
    # Construct dual bases
    meas_dual_basis = None
    if measurement_basis:
        if not measurement_qubits:
            measurement_qubits = tuple(range(measurement_data.shape[1]))
        meas_duals = {
            i: _dual_povms(measurement_basis, i)
            for i in measurement_qubits
        }
        meas_dual_basis = LocalMeasurementBasis(
            f"Dual_{measurement_basis.name}", qubit_povms=meas_duals)

    prep_dual_basis = None
    if preparation_basis:
        if not preparation_qubits:
            preparation_qubits = tuple(range(preparation_data.shape[1]))
        prep_duals = {
            i: _dual_states(preparation_basis, i)
            for i in preparation_qubits
        }
        prep_dual_basis = LocalPreparationBasis(
            f"Dual_{preparation_basis.name}", qubit_states=prep_duals)

    if shot_data is None:
        shot_data = np.ones(len(outcome_data))

    # Construct linear inversion matrix
    rho_fit = 0.0
    for i, outcomes in enumerate(outcome_data):
        shots = shot_data[i]
        midx = measurement_data[i]
        pidx = preparation_data[i]

        # Get prep basis component
        if prep_dual_basis:
            # TODO: Add prep qubits
            p_mat = np.transpose(
                prep_dual_basis.matrix(pidx, preparation_qubits))
        else:
            p_mat = None

        # Get probabilities and optional measurement basis component
        for outcome, freq in enumerate(outcomes):
            if freq == 0:
                # Skip component with zero probability
                continue

            if meas_dual_basis:
                # TODO: Add meas qubits
                dual_op = meas_dual_basis.matrix(midx, outcome,
                                                 measurement_qubits)
                if prep_dual_basis:
                    dual_op = np.kron(p_mat, dual_op)
            else:
                dual_op = p_mat

            # Add component to linear inversion reconstruction
            prob = freq / shots
            rho_fit = rho_fit + prob * dual_op

    return rho_fit, {}