def two_qubit_cliffords():
    '''returns the two qubit clifford group as a list of qutip superoperators'''
    rot_cliffords = [qt.identity(2), qt.rotation((2/(np.sqrt(3))),(qt.sigmax()+qt.sigmay()+qt.sigmaz())/3),qt.rotation((4/(np.sqrt(3))),(qt.sigmax()+qt.sigmay()+qt.sigmaz())/3)]
    rot_class_cliffords  = [qt.to_super(qt.tensor(x,y)) for x in rot_cliffords for y in rot_cliffords]
    class_one_cliffords = [qt.to_super(qt.tensor(x,y)) for x in qt.qubit_clifford_group() for y in qt.qubit_clifford_group()]
    class_two_cliffords = [x*qt.to_super(qt.cnot())*y for x in class_one_cliffords for y in rot_class_cliffords]
    class_three_cliffords = [x*qt.to_super(qt.iswap())*y for x in class_one_cliffords for y in rot_class_cliffords]
    class_four_cliffords = [x*qt.to_super(qt.swap()) for x in class_one_cliffords]
    return class_one_cliffords+class_two_cliffords+ class_three_cliffords+ class_four_cliffords
Пример #2
0
def max_fidelity(rho1: qtp.Qobj, rho2: qtp.Qobj, thetas1, thetas2):

    fid_vec = np.zeros((len(thetas1), len(thetas2)))
    rho1 = qtp.Qobj(rho1, dims=[[2, 2], [2, 2]], shape=(4, 4))
    target_bell = qtp.Qobj(rho2.flatten(), dims=[[2, 2], [1, 1]], shape=(4, 1))

    for i, theta1 in enumerate(thetas1):
        for j, theta2 in enumerate(thetas2):
            state_rotation = qtp.tensor(qtp.rotation(qtp.sigmaz(), theta1),
                                        qtp.rotation(qtp.sigmaz(), theta2))
            target_state = state_rotation*target_bell
            fid_vec[i][j] = float(
                np.real((target_state.dag()*rho1*target_state).data[0, 0]))


    idxs_f_max = np.unravel_index(np.argmax(fid_vec),
                                  dims=(len(thetas1),
                                        len(thetas1)))

    max_F = 100*np.max(fid_vec)
    phase1 = thetas1[idxs_f_max[0]]*180./np.pi
    phase2 = thetas2[idxs_f_max[1]]*180./np.pi

    return max_F, phase1, phase2
Пример #3
0
    def setUpClass(self):

        self.standard_pulses = {
            'I': qtp.qeye(2),
            'Z0': qtp.qeye(2),
            'X180': qtp.sigmax(),
            'mX180': qtp.sigmax(),
            'Y180': qtp.sigmay(),
            'mY180': qtp.sigmay(),
            'X90': qtp.rotation(qtp.sigmax(), np.pi / 2),
            'mX90': qtp.rotation(qtp.sigmax(), -np.pi / 2),
            'Y90': qtp.rotation(qtp.sigmay(), np.pi / 2),
            'mY90': qtp.rotation(qtp.sigmay(), -np.pi / 2),
            'Z90': qtp.rotation(qtp.sigmaz(), np.pi / 2),
            'mZ90': qtp.rotation(qtp.sigmaz(), -np.pi / 2),
            'Z180': qtp.sigmaz(),
            'mZ180': qtp.sigmaz(),
            'CZ': qtp.gates.cphase(np.pi)
        }
Пример #4
0
import numpy as np
import qutip as qtp
from pycqed.analysis import analysis_toolbox as a_tools
from pycqed.analysis import composite_analysis as ca
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from plotting_tools import *
import time
import os

rotation_matrixes = [
    qtp.qeye(2).full(),
    qtp.sigmax().full(),
    qtp.rotation(qtp.sigmay(), np.pi / 2).full(),
    qtp.rotation(qtp.sigmay(), -np.pi / 2).full(),
    qtp.rotation(qtp.sigmax(), np.pi / 2).full(),
    qtp.rotation(qtp.sigmax(), -np.pi / 2).full()
]
pauli_matrixes = [
    qtp.qeye(2).full(),
    qtp.sigmax().full(),
    qtp.sigmay().full(),
    qtp.sigmaz().full()
]
pauli_ro = [qtp.qeye(2).full(), qtp.sigmaz().full()]


def get_rotation(idx):
    # j inner loop
    # i outer loop
    j = idx % 6
Пример #5
0
class TomoAnalysis():

    """Performs state tomography based on an overcomplete set of measurements
     and calibration measurements. Uses qutip to calculate resulting basis states
     from applied rotations

    Uses binary counting as general guideline in ordering states. Calculates
     rotations by using the qutip library
    BEFORE YOU USE THIS SET THE CORRECT ORDER BY CHANGING
        'rotation_matrices'
        'measurement_basis' + 'measurement_basis_labels'
        to values corresponding to your experiment
        and maybe 'readout_basis'
    """

    # The set of single qubit rotation matrices used in the tomography
    # measurement (will be assumed to be used on all qubits)
    rotation_matrices = [qt.identity(2), qt.sigmax(),
                         qt.rotation(
                             qt.sigmay(), np.pi / 2), qt.rotation(qt.sigmay(), -1*np.pi / 2),
                         qt.rotation(qt.sigmax(), np.pi / 2), qt.rotation(qt.sigmax(), -np.pi / 2)]
    measurement_operator_labels = ['I', 'X', 'y', '-y', 'x','-x']
    #MAKE SURE THE LABELS CORRESPOND TO THE ROTATION MATRICES DEFINED ABOVE

    # The set of single qubit basis operators and labels (normalized)
    measurement_basis = [
        qt.identity(2) , qt.sigmaz() , qt.sigmax() , qt.sigmay() ]
    measurement_basis_labels = ['I', 'Z', 'X', 'Y']
    # The operators used in the readout basis on each qubit
    readout_basis = [qt.identity(2) , qt.sigmaz() ]

    def __init__(self, n_qubits=2, check_labels=False):
        """
        keyword arguments:
        measurements_cal --- Should be an array of length 2 ** n_qubits
        measurements_tomo --- Should be an array of length length(rotation_matrices) ** n_qubits
        n_qubits --- default(2) the amount of qubits present in the expirement
        n_quadratures --- default(1(either I or Q)) The amount of complete measurement data sets. For example a combined IQ measurement has 2 measurement sets.
        tomo_vars  : since this tomo does not have access to the original data, the vars should be given by
                        tomo_var_i = 1 / N_i * np.var(M_i) where i stands for the data corresponding to rotation i.

        """
        self.n_qubits = n_qubits
        self.n_states = 2 ** n_qubits

        # Generate the vectors of matrices that correspond to all measurements,
        # readout bases and rotations
        self.basis_vector = self._calculate_matrix_set(
            self.measurement_basis, n_qubits)
        self.readout_vector = self._calculate_matrix_set(
            self.readout_basis, n_qubits)

        self.rotation_vector = self._calculate_matrix_set(
            self.rotation_matrices, n_qubits)

        # generate the basis change matrix from pauli to comp and back
        A = np.zeros((self.n_states**2, self.n_states**2), dtype=complex)
        for i in range(self.n_states**2):
            # do an orthonormal transformation.
            A[:,i] = np.ravel(self.basis_vector[i].full())
        self.basis_pauli_to_comp_trafo_matrix= A / self.n_states
        self.basis_comp_to_pauli_trafo_matrix= np.linalg.inv(A)
        #get dims of qutip objects
        self.qt_dims = [[2 for i in range(self.n_qubits)], [2 for i in range(self.n_qubits)]]
        if check_labels is True:
                # prints the order of basis set corresponding to the
                # tomographic rotations
                print(self.get_meas_operator_labels(n_qubits))
                print(self.get_basis_labels(n_qubits))




    def execute_pseudo_inverse_tomo(self, meas_operators, meas_tomo, use_pauli_basis=False,
        verbose=False):
        """
        Performs a linear tomography by simple inversion of the system of equations due to calibration points
        TE_correction_matrix: a matrix multiplying the calibration points to correct for estimated mixture due to Thermal excitation.
        """
        #some argument parsing to we allow more general input
        meas_operators = [meas_operators] if type(meas_operators) == qt.Qobj else meas_operators


        #for each independent set of measurements(with their own measurement operator, calculate the coeff matrix)
        coeff_matrices = []
        for measurement_operator in meas_operators:
            coeff_matrices.append(self.calculate_LI_coefficient_matrix(measurement_operator, do_in_pauli=use_pauli_basis))
        coefficient_matrix = np.vstack(coeff_matrices)
        basis_decomposition = np.zeros(4 ** self.n_qubits, dtype=complex)
        if use_pauli_basis:
            # first skip beta0
            #basis_decomposition[1:] = np.dot(np.linalg.pinv(coefficient_matrix[:, 1:]), meas_tomo)
            basis_decomposition[:] = np.dot(np.linalg.pinv(coefficient_matrix[:, :]), meas_tomo)
            # re-add beta0
            #basis_decomposition[0] = betas[0]
            rho = self.trans_pauli_to_comp(basis_decomposition)
        else:
            basis_decomposition = np.conj(np.linalg.pinv(coefficient_matrix)).dot(meas_tomo)
            rho = qt.Qobj(np.reshape(basis_decomposition, [self.n_states, self.n_states]),
                     dims=self.qt_dims)
        return (basis_decomposition, rho)



    def execute_mle_T_matrix_tomo(self, measurement_operators, meas_tomo, weights_tomo =False,
                                show_time=True, ftol=0.01, xtol=0.001, full_output=0, max_iter=100,
                                            TE_correction_matrix = None):
        """
        Performs a least squares optimization using fmin_powell in order to get the closest physically realisable state.

        This is done by constructing a lower triangular matrix T consisting of 4 ** n qubits params
        Keyword arguments:
        measurement_operators: list of meas operators
        meas_tomo: list of n_rot measurements beloning to the measurmeent operator
        use_weights : default(False) Weighs the quadrature data by the std in the estimator of the mean
                    : since this tomo does not have access to the original data, the vars should be given by
                        tomo_var_i = 1 / N_i * np.var(M_i) where i stands for the data corresponding to rotation i.
        --- arguments for scipy fmin_powel method below, see the powell documentation
        """
        # first we calculate the measurement matrices
        tstart = time.time()

        measurement_operators = [measurement_operators] if type(measurement_operators) == qt.Qobj else measurement_operators

        measurement_vectors = []
        for measurement_operator in measurement_operators:
            measurement_vectors.append([m.full() for m in  self.get_measurement_vector(measurement_operator)])
        measurement_vector = np.vstack(measurement_vectors)
        # initiate with equal weights
        self.weights = weights_tomo if weights_tomo else np.ones(len(measurement_vector))
        # save it in the object for use in optimization
        self.measurements_tomo = meas_tomo
        self.measurement_vector_numpy = measurement_vector
        tlinear = time.time()
        # find out the starting rho by the linear tomo
        discard, rho0 = self.execute_pseudo_inverse_tomo(measurement_operators, meas_tomo)

        # now fetch the starting t_params from the cholesky decomp of rho
        tcholesky = time.time()
        T0 = np.linalg.cholesky(scipy.linalg.sqrtm((rho0.dag() * rho0).full()))
        t0 = np.zeros(4 ** self.n_qubits, dtype='complex' )
        di = np.diag_indices(2 ** self.n_qubits)
        tri = np.tril_indices(2 ** self.n_qubits, -1)
        t0[0:2 ** self.n_qubits] = T0[di]
        t0[2**self.n_qubits::2] = T0[tri].real
        t0[2**self.n_qubits+1::2] = T0[tri].imag
        topt = time.time()
        # minimize the likelihood function using scipy
        t_optimal = scipy.optimize.fmin_powell(
            self._max_likelihood_optimization_function, t0, maxiter=max_iter, full_output=full_output, ftol=ftol, xtol=xtol)
        if show_time is True:
            print(" Time to calc rotation matrices %.2f " % (tlinear-tstart))
            print(" Time to do linear tomo %.2f " % (tcholesky-tlinear))
            print(" Time to build T %.2f " % (topt-tcholesky))
            print(" Time to optimize %.2f" % (time.time()-topt))
        return qt.Qobj(self.build_rho_from_triangular_params(t_optimal), dims=self.qt_dims)


    def execute_SDPA_2qubit_tomo(self, measurement_operators, counts_tomo, N_total=1, used_bins=[0,3],
                                 correct_measurement_operators=True, calc_chi_squared =False,
                                 correct_zero_count_bins=True, TE_correction_matrix = None):
        """
        Estimates a density matrix given single shot counts of 4 thresholded
        bins using a custom C semidefinite solver from Nathan Langford
        Each bin should correspond to a projection operator:
        0: 00, 1: 01, 2: 10, 3: 11
        The calibration counts are used in calculating corrections to the (ideal) measurement operators
        The tomo counts are used for the actual reconstruction.
        N_total is used for normalization. if counts_tomo is normalized use N_total=1

        """

        # If the tomography bins have zero counts, they not satisfy gaussian noise. If N>>1 then turning them into 1 fixes
        # convergence problems without screwing the total statistics/estimate.
        # if(np.sum(np.where(np.array(counts_tomo) == 0)) > 0):
                # print("WARNING: Some bins contain zero counts, this violates gaussian assumptions. \n \
                        # If correct_zero_count_bins=True these will be set to 1 to minimize errors")
        if correct_zero_count_bins:
            counts_tomo = np.array([[int(b) if b > 0 else  1 for b in bin_counts] for bin_counts in counts_tomo])


        #Select the correct data based on the bins used
        #(and therefore based on the projection operators used)
        data = counts_tomo[:,used_bins].T.flatten()
        #get the total number of counts per tomo
        N = np.array([np.sum(counts_tomo, axis=1) for k in used_bins]).flatten()

        # add weights based on the total number of data points kept each run
        # N_total is a bit arbitrary but should be the average number of total counts of all runs, since in nathans code this
        # average is estimated as a parameter.
        weights = N/float(N_total)
        #get the observables from the rotation operators and the bins kept(and their corresponding projection operators)
        measurement_vectors = []
        for k in used_bins:
            measurement_vectors.append([m.full() for m in  self.get_measurement_vector(measurement_operators[k])])
        measurement_vector = np.vstack(measurement_vectors)
        # print('length of the measurement vector'len(measurement_vector))
        #calculate the density matrix using the csdp solver
        a = time.time()
        rho_nathan = csdp_tomo.tomo_state(data, measurement_vector, weights)
        b=time.time()-a
        # print("time solving csdp: ", b)
        # print(rho_nathan)
        n_estimate = rho_nathan.trace()
        # print(n_estimate)
        rho = qt.Qobj(rho_nathan / n_estimate,dims=self.qt_dims)

        # if((np.abs(N_total - n_estimate) / N_total > 0.03)):
            # print('WARNING estimated N(%d) is not close to provided N(%d) '% (n_estimate,N_total))

        if calc_chi_squared:
            chi_squared = self._state_tomo_goodness_of_fit(rho, data, N, measurement_vector)
            return rho, chi_squared
        else:
            return rho

    def execute_SDPA_MC_2qubit_tomo(self,
                                    measurement_operators,
                                    counts_tomo,
                                    N_total,
                                    used_bins = [0,2],
                                    n_runs = 100,
                                    array_like = False,
                                    correct_measurement_operators = True,
                                    TE_correction_matrix=None):
        """
        Executes the SDPDA tomo n_runs times with data distributed via a Multinomial distribution
        in order to get a list of rhos from which one can calculate errorbars on various derived quantities
        returns a list of Qobjects (the rhos).
        If array_like is set to true it will just return a 3D array of rhos
        """

        rhos= []
        for i in range(n_runs):
            #generate a data set based on multinomial distribution with means according to the measured data
            mc = [np.random.multinomial(sum(counts),(np.array(counts)+0.0) / sum(counts)) for counts in counts_tomo]
            rhos.append(self.execute_SDPA_2qubit_tomo(measurement_operators,
                                                      mc,
                                                      N_total,
                                                      used_bins,
                                                      correct_measurement_operators,
                                                      TE_correction_matrix=TE_correction_matrix))

        if array_like:
            return np.array([rho.full() for rho in rhos])
        else:
            return rhos



    def calculate_LI_coefficient_matrix(self, measurement_operator, do_in_pauli=False):
        """
        Calculates the coefficient matrix used when inversing the linear system of equations needed to find rho
        Requires a calibrated measurement operator
        if do_in_pauli is true this will presume measurement operator is given in the  basis.
        """
        coefficient_matrix = np.zeros((len(self.rotation_matrices) ** self.n_qubits, 4 ** self.n_qubits), dtype=complex)
        Ms = self.get_measurement_vector(measurement_operator, do_in_pauli=do_in_pauli)
        for i in range(len(Ms)):
            coefficient_matrix[i,:] =  np.ravel(Ms[i].full())
        return coefficient_matrix

    def get_measurement_vector(self, measurement_operator, do_in_pauli=False):
        """
        Returns a list of rotated measurement operator based on an initial measurement operator which should be obtained from the calibration
        """
        n_rotations = len(self.rotation_matrices) ** self.n_qubits
        measurement_vector = []
        for i in range(n_rotations):
            R = self.rotation_vector[i]
            if do_in_pauli:
                M = self.trans_comp_to_pauli(R.dag() * measurement_operator * R)
            else:
                M = R.dag() * measurement_operator * R
            measurement_vector.append(M)
        return measurement_vector

    def calibrate_measurement_operator(self, cal_meas, calibration_points=None, TE_correction_matrix=None, transform_to_pauli=False):
        """
        Calibrates the measurement operator in any basis. Assumes cal_meas are the eigenvalues of the calibration_points
        defaults the calibration_points to be the computational basis projectors.
        If TE_correction_matrix is set, this alters the computational basis states
        """
        if calibration_points is None:
            calibration_points = comp_projectors
        M = sum([cal_meas[i] * calibration_points[i] for i in range(len(cal_meas))])
        return M if not transform_to_pauli else self.trans_comp_to_pauli(M)


    def calibrate_bin_operators(self, calibration_counts, calibration_points=None, normalize=False):
        M_bins = []
        #calculate bin probabilities normalized horizontally
        if normalize:
            cal_probs = (calibration_counts / np.sum(calibration_counts, axis = 1, dtype=float)[:,np.newaxis])
        else:
            cal_probs = np.array(calibration_counts)#(calibration_counts / np.sum(calibration_counts, axis = 1, dtype=float)[:,np.newaxis])
        for probs in cal_probs.T:
            #calibrate the measurement operator for each bin in the same way as done with average tomo.
            M_bins.append(self.calibrate_measurement_operator(probs, calibration_points))
        return M_bins



# HELPERS



    def trans_pauli_to_comp(self, rho_pauli):
        """
        Converts a rho in the pauli basis, or pauli basis vector or Qobj of rho in pauli basis to comp basis.
        """
        if(rho_pauli.shape[0] == self.n_states):
            basis_decomposition = np.ravel(rho_pauli.full()) if (type(rho_pauli) ==  qt.Qobj) else np.ravel(rho_pauli)
        else:
            basis_decomposition = rho_pauli

        return qt.Qobj(np.reshape(self.basis_pauli_to_comp_trafo_matrix.dot(basis_decomposition), [self.n_states, self.n_states]),
                     dims=self.qt_dims)

    def trans_comp_to_pauli(self, rho_comp):
        """
        Converts a rho in the computational basis, or comp basis vector or Qobj of rho in computational basis to Pauli basis.
        """
        if(rho_comp.shape[0] == self.n_states):
            basis_decomposition = np.ravel(rho_comp.full()) if (type(rho_comp) ==  qt.Qobj) else np.ravel(rho_comp)
        else:
            basis_decomposition = rho_comp

        return qt.Qobj(np.reshape(self.basis_comp_to_pauli_trafo_matrix.dot(basis_decomposition), [self.n_states, self.n_states]),
                     dims=self.qt_dims)


    def _calculate_matrix_set(self, starting_set, n_qubits):
        """recursive function that returns len(starting_set) ** n_qubits
        measurement_basis states tensored with eachother based
        on the amount of qubits

        So for 2 qubits assuming your basis set is {I, X, Y, Z} you get II IX IY IZ XI XX XY XZ ...
        """
        if(n_qubits > 1):
            return [qt.tensor(x, y) for x in self._calculate_matrix_set(starting_set, n_qubits - 1)
                    for y in starting_set]
        else:
            return starting_set

    def get_basis_labels(self, n_qubits):
        """
        Returns the basis labels in the same order as the basis vector is parsed.
        Requires self.measurement_basis_labels to be set with the correct order corresponding to the matrices in self.measurement_basis
        """
        if(n_qubits > 1):
            return [x + y for x in self.get_basis_labels(n_qubits - 1)
                    for y in self.measurement_basis_labels]
        else:
            return self.measurement_basis_labels

    def get_meas_operator_labels(self, n_qubits):
        """
        Returns a vector of the rotations in order based on self.measurement_operator_labels
        """
        if(n_qubits > 1):
            return [x + y for x in self.get_meas_operator_labels(n_qubits - 1)
                    for y in self.measurement_operator_labels]
        else:
            return self.measurement_operator_labels


    ###############################3
    # MLE T Matrix functions
    #
    def build_rho_from_triangular_params(self, t_params):
        # build the lower triangular matrix T
        T_mat = np.zeros(
            (2 ** self.n_qubits, 2 ** self.n_qubits), dtype="complex")
        di = np.diag_indices(2 ** self.n_qubits)
        T_mat[di] = t_params[0:2**self.n_qubits]
        tri = np.tril_indices(2 ** self.n_qubits, -1)
        T_mat[tri] = t_params[2**self.n_qubits::2]
        T_mat[tri] += 1j * t_params[2**self.n_qubits+1::2]
        rho = np.dot(np.conj(T_mat.T),  T_mat) / \
            np.trace(np.dot(np.conj(T_mat.T),  T_mat))
        return rho

    def _max_likelihood_optimization_function(self, t_params):
        """
        Optimization function that is evaluated many times in the maximum likelihood method.
        Calculates the difference between expected measurement values and the actual measurement values based on a guessed rho

        Keyword arguments:
        t_params : cholesky decomp parameters used to construct the initial rho
        Requires:
        self.weights :  weights per measurement vector used in calculating the loss
        """
        rho = self.build_rho_from_triangular_params(t_params)
        L = 0 + 0j
        for i in range(len(self.measurement_vector_numpy)):
            expectation = np.trace(
                np.dot(self.measurement_vector_numpy[i], rho))
            L += ((expectation -
                   self.measurements_tomo[i]) ** 2) * self.weights[i]
        return L

#############################################################################################
    # CDSP tomo functions for likelihood.
    def _state_tomo_likelihood_function(self, rho, data, normalisations, observables, fixedweight=False):
        data_predicted = []
        for ii in range(len(data)):
            data_predicted.append((rho.full().dot(observables[ii])).trace()*normalisations[ii])

        data_predicted = np.array(data_predicted)

        if fixedweight:
            likely_function = np.sum( (data-data_predicted)**2/data )
        else:
            likely_function = np.sum( (data-data_predicted)**2/data_predicted )

        return likely_function

    """
        Calculates the goodness of fit. It has a normalisation which is just
        the sum of the counts in the superconducting case since there are no
        missing counts like in the photon case.
    """
    def _state_tomo_goodness_of_fit(self, rho, data, normalisations, observables,
                                   fixedweight=False, eig_cutoff=1e-6):

        likely_function = self._state_tomo_likelihood_function(rho, data, normalisations, observables,
                                                         fixedweight=fixedweight)
        num_data = len(data)
        num_eigs = np.sum(rho.eigenenergies()>eig_cutoff)
        rho_dim = rho.shape[0]
        num_dofs = num_eigs*(2*rho_dim-num_eigs)
        out = {}
        out['pure'] = likely_function / (num_data-(2*rho_dim-1))
        out['mixed'] = likely_function / (num_data-rho_dim**2)
        out['dofs'] = likely_function / (num_data-num_dofs)

        return out
import numpy as np
import qutip as qt

# initialize handy shortcuts using qutip
zero = qt.basis(2, 0)  # |0>
one = qt.basis(2, 1)  # |1>

# shorthand for Paulis
Z = qt.sigmaz()
X = qt.sigmax()
Y = qt.sigmay()
Id = qt.identity(2)

# shorthand for rotations
Y2p = qt.rotation(Y, np.pi / 2)
Y2m = qt.rotation(Y, -np.pi / 2)
Z2p = qt.rotation(Z, np.pi / 2)
Z2m = qt.rotation(Z, -np.pi / 2)
X2p = qt.rotation(X, np.pi / 2)
X2m = qt.rotation(X, -np.pi / 2)

# Initialize density matrices for fiducial singlequbit states
rho_0 = qt.ket2dm(zero)
rho_1 = qt.ket2dm(one)
rho_Xp = qt.ket2dm(Y2p * zero)
rho_Xm = qt.ket2dm(Y2m * zero)
rho_Yp = qt.ket2dm(X2m * zero)
rho_Ym = qt.ket2dm(X2p * zero)

# Initialize two qubit states
Пример #7
0
    def evolve_densitymatrix_in_CPMG(self, m, rho0):
        """
        Implementation of Lindblad master equation, based on Markovian approach
        (weak coupling) is carried out by considering the collapse operator
        including the influence of flux noise in the Hamiltonian.
        
        This module performs the numerical evolution of the total Hamiltonian
        within CPMG pulse sequence.
        
        Evolution for each pulse duration or the precession time duration between 
        two adjacent/consecutive pulses can be handled by using QuTiP.mesolve
        that calculates the evolution of the density matrix.
        
        The mesolve in QuTiP is capable of handling time-dependent Hamiltonians 
        and the terms with collapse operators.
        
        Note: This module is capable of simulating an arbitrary number of Np pulses.
        
        The first CPMG and measurement
        
        (pi/2)x       +(pi)x                -(pi)x       (pi/2)y
         +---+      +--------+            +--------+      +---+
         |   |      |        |            |        |      |   |++++++++++
         |   |      |        |            |        |      |   |    M1   +
         +---+------+--------+------------+--------+------+---+---------+
         t0  t1     t2       t3           t4       t5     t6  t7         


        The second CPMG and measurement
        
        (pi/2)x       +(pi)x                -(pi)x       (pi/2)y
         +---+      +--------+            +--------+      +---+
         |   |      |        |            |        |      |   |++++++++++
         |   |      |        |            |        |      |   |    M2   +
         +---+------+--------+------------+--------+------+---+---------+
         t8  t9     t10     t11          t12      t13    t14  t15
        """
        #--------------------------------------------------
        # Generate subsequent measurement time interval (synchronized)
        #--------------------------------------------------
        t = self.time
        #The synchronized time is generated during parameter setup

        Np = pars['Np']
        # Index of m
        mp = 2 * m * (Np + 2)

        # Number of subintervals
        NumPro = pars['Num']
        #--------------------------------------------------------
        # Evolution in (pi/2)x between 0 and t0 for m=0; t[mp]-tau/2 and t[m] for m!=0
        #--------------------------------------------
        pars['Num'] = int(NumPro / 2)
        #test#print("For (pi/2)x: Num=",pars['Num'])
        rho = self.solveME(rho0,
                           t[mp],
                           t[mp + 1],
                           evolType='(pi/2)x',
                           theSys='qubit-HO')
        #rho = self.evolve_PiX(rho0, t[mp], t[mp+1], evolType='(pi/2)x', theSys='qubit-HO');

        #--------------------------------------------
        # Evolution in free precession time (half) between t[mp] and t[mp+1]
        #--------------------------------------------
        if self.collapse == []:
            pars['dirname'] = []

        pars['Num'] = int(NumPro)
        #test#print("For (free/2): Num=",pars['Num'])
        mm = m
        rho = self.solveME(rho,
                           t[mp + 1],
                           t[mp + 2],
                           evolType='free',
                           dirname=pars['dirname'],
                           theSys='qubit-HO',
                           n=0,
                           m=mm)
        #rho = self.evolve_free(rho, t[mp+1], t[mp+2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=0, m=mm);

        sgn = 1.0
        n = 1
        while n <= Np:
            #--------------------------------------------
            # Evolution in (pi)x between t[mp+2n-1] and t[mp+2n]
            #--------------------------------------------
            pars['Num'] = int(NumPro)
            #test#print("For (pi)[",n,"]x: Num=",pars['Num'])
            rho = self.solveME(rho,
                               t[mp + 2 * n],
                               t[mp + 2 * n + 1],
                               evolType='(pi)x',
                               theSys='qubit-HO',
                               sign=sgn)
            #rho = self.evolve_PiX(rho, t[mp+2*n], t[mp+2*n+1], evolType='(pi)x', theSys='qubit-HO', sign=sgn)

            #------------------------------------------
            # Evolution in free precessing time between t[mp+2n] and t[mp+2n+1]
            #------------------------------------------
            if n == pars['Np']:
                pars['Num'] = int(NumPro)
                #test#print("For (free/2): Num=",pars['Num'])
            else:
                pars['Num'] = int(2 * NumPro)
                #test#print("For (free): Num=",pars['Num'])

            mm = m
            nn = n
            rho = self.solveME(rho,
                               t[mp + 2 * n + 1],
                               t[mp + 2 * n + 2],
                               evolType='free',
                               dirname=pars['dirname'],
                               theSys='qubit-HO',
                               n=nn,
                               m=mm)
            #rho = self.evolve_free(rho, t[mp+2*n+1], t[mp+2*n+2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=nn, m=mm);

            # Sign update for the next adjacent (pi)x pulse
            sgn = -sgn
            #
            # Update n for next evolution
            n += 1

        # Evolution in (pi/2)y pulse
        pars['Num'] = int(NumPro / 2)
        #test#print("For (pi/2)y: Num=",pars['Num'])
        rho = self.solveME(rho,
                           t[mp + 2 * Np + 2],
                           t[mp + 2 * Np + 3],
                           evolType='(pi/2)y',
                           theSys='qubit-HO')
        #rho = self.evolve_PiY(rho, t[mp+2*Np+2], t[mp+2*Np+3], evolType='(pi/2)y', theSys='qubit-HO');

        #Invert Number of subintervals
        pars['Num'] = int(NumPro)
        #------------------------------------------------
        # Fidelity check for rho in CPMG and rho_avg
        #------------------------------------------------
        if (m == 0):
            Uavg = rotation(self.sx, np.pi / 2)
            #varphi= 2.0 / np.pi * param['lambdaz'] * param['Te'];
            varphi = 2.0 / np.pi * pars['g'] * pars['Te']
            #Time averaged Hamiltonian
            Up = (-1j * varphi * self.sz * (self.a + self.a.dag())).expm()
            Uavg = Up * Uavg
            if pars['Np'] % 2 != 0:
                Uavg = rotation(self.sx, np.pi) * Uavg
            Uavg = rotation(self.sy, np.pi / 2) * Uavg

            print ("State Fidelity(rho,rho_avg)=%.3lf where rho_avg=Uavg*rho0*Uavg.dag()"\
                   %fidelity(rho, Uavg * rho0 * Uavg.dag()))

        return rho
Пример #8
0
def standard_qubit_pulses_to_rotations(pulse_list: List[Tuple]) \
        -> List[qtp.Qobj]:
    """
    Converts lists of n-tuples of standard PycQED single-qubit pulse names to
    the corresponding rotation matrices on the n-qubit Hilbert space.
    """
    standard_pulses = {
        'I': qtp.qeye(2),
        'X0': qtp.qeye(2),
        'Z0': qtp.qeye(2),
        'X180': qtp.rotation(qtp.sigmax(), np.pi),
        'mX180': qtp.rotation(qtp.sigmax(), -np.pi),
        'Y180': qtp.rotation(qtp.sigmay(), np.pi),
        'mY180': qtp.rotation(qtp.sigmay(), -np.pi),
        'X90': qtp.rotation(qtp.sigmax(), np.pi/2),
        'mX90': qtp.rotation(qtp.sigmax(), -np.pi/2),
        'Y90': qtp.rotation(qtp.sigmay(), np.pi/2),
        'mY90': qtp.rotation(qtp.sigmay(), -np.pi/2),
        'Z90': qtp.rotation(qtp.sigmaz(), np.pi/2),
        'mZ90': qtp.rotation(qtp.sigmaz(), -np.pi/2),
        'Z180': qtp.rotation(qtp.sigmaz(), np.pi),
        'mZ180': qtp.rotation(qtp.sigmaz(), -np.pi),
    }
    rotations = [qtp.tensor(*[standard_pulses[pulse] for pulse in qb_pulses])
                 for qb_pulses in pulse_list]
    for i in range(len(rotations)):
        rotations[i].dims = [[d] for d in rotations[i].shape]
    return rotations