Ejemplo n.º 1
0
 def _gate_matrix(self, gate):
     """Gets a PTM representation of the gate"""
     if isinstance(gate, Gate):
         return PTM(gate).data
     if callable(gate):
         c = QuantumCircuit(1)
         gate(c, c.qubits[0])
         return PTM(c).data
     return None
    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)
Ejemplo n.º 3
0
    def linear_inversion(self) -> Dict[str, PTM]:
        """
        Reconstruct a gate set from measurement data using linear inversion.

        Returns:
            For each gate in the gateset: its approximation found
            using the linear inversion process.

        Additional Information:
            Given a gate set (G1,...,Gm)
            and SPAM circuits (F1,...,Fn) constructed from those gates
            the data should contain the probabilities of the following types:
            p_ijk = E*F_i*G_k*F_j*rho
            p_ij = E*F_i*F_j*rho

            We have p_ijk = self.probs[(Fj, Gk, Fi)] since in self.probs
            (Fj, Gk, Fi) indicates first applying Fj, then Gk, then Fi.

            One constructs the Gram matrix g = (p_ij)_ij
            which can be described as a product g=AB
            where A = sum (i> <E F_i) and B=sum (F_j rho><j)
            For each gate Gk one can also construct the matrix Mk=(pijk)_ij
            which can be described as Mk=A*Gk*B
            Inverting g we obtain g^-1 = B^-1A^-1 and so
            g^1 * Mk = B^-1 * Gk * B
            This gives us a matrix similiar to Gk's representing matrix.
            However, it will not be the same as Gk,
            since the observable results cannot distinguish
            between (G1,...,Gm) and (B^-1*G1*B,...,B^-1*Gm*B)
            a further step of *Gauge optimization* is required on the results
            of the linear inversion stage.
            One can also use the linear inversion results as a starting point
            for a MLE optimization for finding a physical gateset, since
            unless the probabilities are accurate, the resulting gateset
            need not be physical.
        """
        n = len(self.gateset_basis.spam_labels)
        m = len(self.gateset_basis.gate_labels)
        gram_matrix = np.zeros((n, n))
        gate_matrices = []
        for i in range(m):
            gate_matrices.append(np.zeros((n, n)))

        for i in range(n):  # row
            for j in range(n):  # column
                F_i = self.gateset_basis.spam_labels[i]
                F_j = self.gateset_basis.spam_labels[j]
                gram_matrix[i][j] = self.probs[(F_j, F_i)]

                for k in range(m):  # gate
                    G_k = self.gateset_basis.gate_labels[k]
                    gate_matrices[k][i][j] = self.probs[(F_j, G_k, F_i)]

        gram_inverse = np.linalg.inv(gram_matrix)

        gates = [
            PTM(gram_inverse @ gate_matrix) for gate_matrix in gate_matrices
        ]
        return dict(zip(self.gateset_basis.gate_labels, gates))
 def test_depolarization_standard_basis(self):
     p = 0.05
     noise_ptm = PTM(
         np.array([[1, 0, 0, 0], [0, 1 - p, 0, 0], [0, 0, 1 - p, 0],
                   [0, 0, 0, 1 - p]]))
     noise_model = NoiseModel()
     noise_model.add_all_qubit_quantum_error(noise_ptm, ['u1', 'u2', 'u3'])
     self.run_test_on_basis_and_noise(noise_model=noise_model,
                                      noise_ptm=np.real(noise_ptm.data))
 def test_amplitude_damping_standard_basis(self):
     gamma = 0.05
     noise_ptm = PTM(
         np.array([[1, 0, 0, 0], [0, np.sqrt(1 - gamma), 0, 0],
                   [0, 0, np.sqrt(1 - gamma), 0], [gamma, 0, 0,
                                                   1 - gamma]]))
     noise_model = NoiseModel()
     noise_model.add_all_qubit_quantum_error(noise_ptm, ['u1', 'u2', 'u3'])
     self.run_test_on_basis_and_noise(noise_model=noise_model,
                                      noise_ptm=np.real(noise_ptm.data))
    def _process_result(self, x: np.array) -> Dict:
        """Transforms the optimization result to a friendly format
        Args:
            x: the optimization result vector

        Returns:
            The final GST data, as dictionary.
        """
        E, rho, G_matrices = self._split_input_vector(x)
        result = {}
        result['E'] = Operator(self._convert_from_ptm(E))
        result['rho'] = DensityMatrix(self._convert_from_ptm(rho))
        for i in range(len(self.Gs)):
            result[self.Gs[i]] = PTM(G_matrices[i])
        return result
    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)
    def _x_to_gateset(self, x: np.array) -> Dict[str, PTM]:
        """Converts the gauge to the gateset defined by it
        Args:
            x: An array representation of the B matrix

        Returns:
            The gateset obtained from B

        Additional information:
            Given a vector representation of B, this functions
            produces the list [B*G1*B^-1,...,B*Gn*B^-1]
            of gates correpsonding to the gauge B

        """
        B = np.array(x).reshape((self.d, self.d))
        try:
            BB = np.linalg.inv(B)
        except np.linalg.LinAlgError:
            return None
        gateset = {label: PTM(BB @ self.initial_gateset[label].data @ B)
                   for label in self.gateset_basis.gate_labels}
        gateset['E'] = self.initial_gateset['E'] @ B
        gateset['rho'] = BB @ self.initial_gateset['rho']
        return gateset
 def _ideal_gateset(self, size):
     ideal_gateset = {label: PTM(self.gateset_basis.gate_matrices[label])
                      for label in self.gateset_basis.gate_labels}
     ideal_gateset['E'] = self._default_measurement_op(size)
     ideal_gateset['rho'] = self._default_init_state(size)
     return ideal_gateset