Beispiel #1
0
    def test_expand_contract(self):
        # matrix that operates on 2x2 density matrices, but only on the 0-th and 3-rd
        # elements which correspond to the diagonals of the 2x2 density matrix.
        mxInStdBasis = np.array([[1,0,0,2],
                                 [0,0,0,0],
                                 [0,0,0,0],
                                 [3,0,0,4]],'d')

        # Reduce to a matrix operating on a density matrix space with 2 1x1 blocks (hence [1,1])
        begin = Basis('std', [1,1])
        end   = Basis('std', 2)
        
        mxInReducedBasis = bt.resize_std_mx(mxInStdBasis, 'contract', end, begin)
        #mxInReducedBasis = bt.change_basis(mxInStdBasis, begin, end)
        notReallyContracted = bt.change_basis(mxInStdBasis, 'std', 'std', 4)
        correctAnswer = np.array([[ 1.0,  2.0],
                                  [ 3.0,  4.0]])
        #self.assertArraysAlmostEqual( mxInReducedBasis, correctAnswer )
        self.assertArraysAlmostEqual( notReallyContracted, mxInStdBasis )

        expandedMx = bt.resize_std_mx(mxInReducedBasis, 'expand', begin, end)
        #expandedMx = bt.change_basis(mxInReducedBasis, end, begin)
        expandedMxAgain = bt.change_basis(expandedMx, 'std', 'std', 4)
        self.assertArraysAlmostEqual( expandedMx, mxInStdBasis )
        self.assertArraysAlmostEqual( expandedMxAgain, mxInStdBasis )
Beispiel #2
0
 def test_auto_expand(self):
     comp = Basis(matrices=[('std', 2,), ('std', 1)])
     std  = Basis('std', 3)
     mxStd = np.identity(5)
     test   = bt.resize_std_mx(mxStd, 'expand', comp, std)
     test2  = bt.resize_std_mx(test, 'contract', std, comp)
     self.assertArraysAlmostEqual(test2, mxStd)
Beispiel #3
0
    def test_general(self):
        std = Basis.cast('std', 4)
        std4 = Basis.cast('std', 16)
        std2x2 = Basis.cast([('std', 4), ('std', 4)])
        gm = Basis.cast('gm', 4)

        from_basis, to_basis = bt.build_basis_pair(np.identity(4, 'd'), "std",
                                                   "gm")
        from_basis, to_basis = bt.build_basis_pair(np.identity(4, 'd'), std,
                                                   "gm")
        from_basis, to_basis = bt.build_basis_pair(np.identity(4, 'd'), "std",
                                                   gm)

        mx = np.array([[1, 0, 0, 1], [0, 1, 2, 0], [0, 2, 1, 0], [1, 0, 0, 1]])

        bt.change_basis(mx, 'std', 'gm')  # shortname lookup
        bt.change_basis(mx, std, gm)  # object
        bt.change_basis(mx, std, 'gm')  # combination
        bt.flexible_change_basis(mx, std, gm)  # same dimension
        I2x2 = np.identity(8, 'd')
        I4 = bt.flexible_change_basis(I2x2, std2x2, std4)
        self.assertArraysAlmostEqual(
            bt.flexible_change_basis(I4, std4, std2x2), I2x2)

        with self.assertRaises(AssertionError):
            bt.change_basis(mx, std, std4)  # basis size mismatch

        mxInStdBasis = np.array(
            [[1, 0, 0, 2], [0, 0, 0, 0], [0, 0, 0, 0], [3, 0, 0, 4]], 'd')

        begin = Basis.cast('std', [1, 1])
        end = Basis.cast('std', 4)
        mxInReducedBasis = bt.resize_std_mx(mxInStdBasis, 'contract', end,
                                            begin)
        original = bt.resize_std_mx(mxInReducedBasis, 'expand', begin, end)
Beispiel #4
0
 def test_auto_expand(self):
     comp = Basis.cast([(
         'std',
         4,
     ), ('std', 1)])
     std = Basis.cast('std', 9)
     mxStd = np.identity(5)
     test = bt.resize_std_mx(mxStd, 'expand', comp, std)
     # TODO assert intermediate correctness
     test2 = bt.resize_std_mx(test, 'contract', std, comp)
     self.assertArraysAlmostEqual(test2, mxStd)
Beispiel #5
0
    def test_auto_expand(self):
        comp = Basis.cast([(
            'std',
            4,
        ), ('std', 1)])
        std = Basis.cast('std', 9)
        mxStd = np.identity(5)
        test = bt.resize_std_mx(mxStd, 'expand', comp, std)
        # Intermediate test
        mxInter = np.identity(9)
        mxInter[2, 2] = mxInter[5, 5] = mxInter[6, 6] = mxInter[7, 7] = 0
        self.assertArraysAlmostEqual(test, mxInter)

        test2 = bt.resize_std_mx(test, 'contract', std, comp)
        self.assertArraysAlmostEqual(test2, mxStd)
Beispiel #6
0
def jamiolkowski_iso_inv(choi_mx, choi_mx_basis='pp', op_mx_basis='pp'):
    """
    Given a choi matrix, return the corresponding operation matrix.

    This function performs the inverse of :function:`jamiolkowski_iso`.

    Parameters
    ----------
    choi_mx : numpy array
        the Choi matrix, normalized to have trace == 1, to compute operation matrix for.

    choi_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    op_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    Returns
    -------
    numpy array
        operation matrix in the desired basis.
    """
    choi_mx = _np.asarray(choi_mx)  # will have "expanded" dimension even if bases are for reduced...
    N = choi_mx.shape[0]  # dimension of full-basis (expanded) operation matrix
    if not isinstance(choi_mx_basis, _Basis):  # if we're not given a basis, build
        choi_mx_basis = _Basis.cast(choi_mx_basis, N)  # one with the full dimension

    dmDim = int(round(_np.sqrt(N)))  # density matrix dimension

    #get full list of basis matrices (in std basis)
    BVec = _bt.basis_matrices(choi_mx_basis.create_simple_equivalent(), N)
    assert(len(BVec) == N)  # make sure the number of basis matrices matches the dim of the choi matrix given

    # Invert normalization
    choiMx_unnorm = choi_mx * dmDim

    opMxInStdBasis = _np.zeros((N, N), 'complex')  # in matrix unit basis of entire density matrix
    for i in range(N):
        for j in range(N):
            BiBj = _np.kron(BVec[i], _np.conjugate(BVec[j]))
            opMxInStdBasis += choiMx_unnorm[i, j] * BiBj

    if not isinstance(op_mx_basis, _Basis):
        op_mx_basis = _Basis.cast(op_mx_basis, N)  # make sure op_mx_basis is a Basis; we'd like dimension to be N

    #project operation matrix so it acts only on the space given by the desired state space blocks
    opMxInStdBasis = _bt.resize_std_mx(opMxInStdBasis, 'contract',
                                       op_mx_basis.create_simple_equivalent('std'),
                                       op_mx_basis.create_equivalent('std'))

    #transform operation matrix into appropriate basis
    return _bt.change_basis(opMxInStdBasis, op_mx_basis.create_equivalent('std'), op_mx_basis)
Beispiel #7
0
def fast_jamiolkowski_iso_std(operation_mx, op_mx_basis):
    """
    The corresponding Choi matrix in the standard basis that is normalized to have trace == 1.

    This routine *only* computes the case of the Choi matrix being in the
    standard (matrix unit) basis, but does so more quickly than
    :func:`jamiolkowski_iso` and so is particuarly useful when only the
    eigenvalues of the Choi matrix are needed.

    Parameters
    ----------
    operation_mx : numpy array
        the operation matrix to compute Choi matrix of.

    op_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    Returns
    -------
    numpy array
        the Choi matrix, normalized to have trace == 1, in the std basis.
    """

    #first, get operation matrix into std basis
    operation_mx = _np.asarray(operation_mx)
    op_mx_basis = _bt.create_basis_for_matrix(operation_mx, op_mx_basis)
    opMxInStdBasis = _bt.change_basis(operation_mx, op_mx_basis, op_mx_basis.create_equivalent('std'))

    #expand operation matrix so it acts on entire space of dmDim x dmDim density matrices
    opMxInStdBasis = _bt.resize_std_mx(opMxInStdBasis, 'expand', op_mx_basis.create_equivalent('std'),
                                       op_mx_basis.create_simple_equivalent('std'))

    #Shuffle indices to go from process matrix to Jamiolkowski matrix (they vectorize differently)
    N2 = opMxInStdBasis.shape[0]; N = int(_np.sqrt(N2))
    assert(N * N == N2)  # make sure N2 is a perfect square
    Jmx = opMxInStdBasis.reshape((N, N, N, N))
    Jmx = _np.swapaxes(Jmx, 1, 2).flatten()
    Jmx = Jmx.reshape((N2, N2))

    # This construction results in a Jmx with trace == dim(H) = sqrt(gateMxInPauliBasis.shape[0])
    #  but we'd like a Jmx with trace == 1, so normalize:
    Jmx_norm = Jmx / N
    return Jmx_norm
Beispiel #8
0
    def checkBasis(self, cmb):
        #Op with Jamio map on gate in std and gm bases
        Jmx1 = j.jamiolkowski_iso(self.testGate,
                                  op_mx_basis=self.stdSmall,
                                  choi_mx_basis=cmb)
        Jmx2 = j.jamiolkowski_iso(self.testGateGM_mx,
                                  op_mx_basis=self.gmSmall,
                                  choi_mx_basis=cmb)
        #print("Jmx1.shape = ", Jmx1.shape)

        #Make sure these yield the same trace == 1 matrix
        self.assertArraysAlmostEqual(Jmx1, Jmx2)
        self.assertAlmostEqual(np.trace(Jmx1), 1.0)

        #Op on expanded gate in std and gm bases
        JmxExp1 = j.jamiolkowski_iso(self.expTestGate_mx,
                                     op_mx_basis=self.std,
                                     choi_mx_basis=cmb)
        JmxExp2 = j.jamiolkowski_iso(self.expTestGateGM_mx,
                                     op_mx_basis=self.gm,
                                     choi_mx_basis=cmb)
        #print("JmxExp1.shape = ", JmxExp1.shape)

        #Make sure these are the same as operating on the contracted basis
        self.assertArraysAlmostEqual(Jmx1, JmxExp1)
        self.assertArraysAlmostEqual(Jmx1, JmxExp2)

        #Reverse transform should yield back the operation matrix
        revTestGate_mx = j.jamiolkowski_iso_inv(Jmx1,
                                                choi_mx_basis=cmb,
                                                op_mx_basis=self.gmSmall)
        self.assertArraysAlmostEqual(revTestGate_mx, self.testGateGM_mx)

        #Reverse transform without specifying stateSpaceDims, then contraction, should yield same result
        revExpTestGate_mx = j.jamiolkowski_iso_inv(Jmx1,
                                                   choi_mx_basis=cmb,
                                                   op_mx_basis=self.std)
        self.assertArraysAlmostEqual(
            bt.resize_std_mx(revExpTestGate_mx, 'contract', self.std,
                             self.stdSmall), self.testGate)
Beispiel #9
0
def fast_jamiolkowski_iso_std_inv(choi_mx, op_mx_basis):
    """
    Given a choi matrix in the standard basis, return the corresponding operation matrix.

    This function performs the inverse of :function:`fast_jamiolkowski_iso_std`.

    Parameters
    ----------
    choi_mx : numpy array
        the Choi matrix in the standard (matrix units) basis, normalized to
        have trace == 1, to compute operation matrix for.

    op_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    Returns
    -------
    numpy array
        operation matrix in the desired basis.
    """

    #Shuffle indices to go from process matrix to Jamiolkowski matrix (they vectorize differently)
    N2 = choi_mx.shape[0]; N = int(_np.sqrt(N2))
    assert(N * N == N2)  # make sure N2 is a perfect square
    opMxInStdBasis = choi_mx.reshape((N, N, N, N)) * N
    opMxInStdBasis = _np.swapaxes(opMxInStdBasis, 1, 2).flatten()
    opMxInStdBasis = opMxInStdBasis.reshape((N2, N2))
    op_mx_basis = _bt.create_basis_for_matrix(opMxInStdBasis, op_mx_basis)

    #project operation matrix so it acts only on the space given by the desired state space blocks
    opMxInStdBasis = _bt.resize_std_mx(opMxInStdBasis, 'contract',
                                       op_mx_basis.create_simple_equivalent('std'),
                                       op_mx_basis.create_equivalent('std'))

    #transform operation matrix into appropriate basis
    return _bt.change_basis(opMxInStdBasis, op_mx_basis.create_equivalent('std'), op_mx_basis)
Beispiel #10
0
    def test_general(self):
        Basis('pp', 2)
        Basis('std', [2, 1])
        Basis([('std', 2), ('gm', 2)])

        std  = Basis('std', 2)
        std4  = Basis('std', 4)
        std2x2 = Basis([('std', 2), ('std', 2)])
        gm   = Basis('gm', 2)
        ungm = Basis('gm_unnormalized', 2)
        empty = Basis([]) #special "empty" basis
        self.assertEqual(empty.name, "*Empty*")

        from_basis,to_basis = pygsti.tools.build_basis_pair(np.identity(4,'d'),"std","gm")
        from_basis,to_basis = pygsti.tools.build_basis_pair(np.identity(4,'d'),std,"gm")
        from_basis,to_basis = pygsti.tools.build_basis_pair(np.identity(4,'d'),"std",gm)

        gm_mxs = gm.get_composite_matrices()
        unnorm = Basis(matrices=[ gm_mxs[0], 2*gm_mxs[1] ])

        std[0]
        std.get_sub_basis_matrices(0)

        print(gm.get_composite_matrices())
        self.assertTrue(gm.is_normalized())
        self.assertFalse(ungm.is_normalized())
        self.assertFalse(unnorm.is_normalized())

        transMx = bt.transform_matrix(std, gm)

        composite = Basis([std, gm])

        comp = Basis(matrices=[std, gm], name='comp', longname='CustomComposite')

        comp.labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
        comp = Basis(matrices=[std, gm], name='comp', longname='CustomComposite', labels=[
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'
            ])

        std2x2Matrices = np.array([
            [[1, 0],
             [0, 0]],
            
            [[0, 1],
             [0, 0]],
            
            [[0, 0],
             [1, 0]],
            
            [[0, 0],
             [0, 1]]
        ],'complex')

        empty = Basis(matrices=[])
        alt_standard = Basis(matrices=std2x2Matrices)
        print("MXS = \n",alt_standard._matrices)
        alt_standard = Basis(matrices=std2x2Matrices,
                             name='std',
                             longname='Standard'
                            )
        self.assertEqual(alt_standard, std2x2Matrices)

        mx = np.array([
                [1, 0, 0, 1],
                [0, 1, 2, 0],
                [0, 2, 1, 0],
                [1, 0, 0, 1]
            ])

        bt.change_basis(mx, 'std', 'gm') # shortname lookup
        bt.change_basis(mx, std, gm) # object
        bt.change_basis(mx, std, 'gm') # combination
        bt.flexible_change_basis(mx, std, gm) #same dimension
        I2x2 = np.identity(8,'d')
        I4 = bt.flexible_change_basis(I2x2, std2x2, std4)
        self.assertArraysAlmostEqual(bt.flexible_change_basis(I4, std4, std2x2), I2x2)
        
        with self.assertRaises(ValueError):
            bt.change_basis(mx, std, std4) # basis size mismatch
        
        
        mxInStdBasis = np.array([[1,0,0,2],
                                 [0,0,0,0],
                                 [0,0,0,0],
                                 [3,0,0,4]],'d')

        begin = Basis('std', [1,1])
        end   = Basis('std', 2)
        mxInReducedBasis = bt.resize_std_mx(mxInStdBasis, 'contract', end, begin)
        original         = bt.resize_std_mx(mxInReducedBasis, 'expand', begin, end)
Beispiel #11
0
def jamiolkowski_iso(operation_mx, op_mx_basis='pp', choi_mx_basis='pp'):
    """
    Given a operation matrix, return the corresponding Choi matrix that is normalized to have trace == 1.

    Parameters
    ----------
    operation_mx : numpy array
        the operation matrix to compute Choi matrix of.

    op_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    choi_mx_basis : Basis object
        The source and destination basis, respectively.  Allowed
        values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
        and Qutrit (qt) (or a custom basis object).

    Returns
    -------
    numpy array
        the Choi matrix, normalized to have trace == 1, in the desired basis.
    """
    operation_mx = _np.asarray(operation_mx)
    op_mx_basis = _bt.create_basis_for_matrix(operation_mx, op_mx_basis)
    opMxInStdBasis = _bt.change_basis(operation_mx, op_mx_basis, op_mx_basis.create_equivalent('std'))

    #expand operation matrix so it acts on entire space of dmDim x dmDim density matrices
    #  so that we can take dot products with the BVec matrices below
    opMxInStdBasis = _bt.resize_std_mx(opMxInStdBasis, 'expand', op_mx_basis.create_equivalent(
        'std'), op_mx_basis.create_simple_equivalent('std'))

    N = opMxInStdBasis.shape[0]  # dimension of the full-basis (expanded) gate
    dmDim = int(round(_np.sqrt(N)))  # density matrix dimension

    #Note: we need to use the *full* basis of Matrix Unit, Gell-Mann, or Pauli-product matrices when
    # generating the Choi matrix, even when the original operation matrix doesn't include the entire basis.
    # This is because even when the original operation matrix doesn't include a certain basis element (B0 say),
    # conjugating with this basis element and tracing, i.e. trace(B0^dag * Operation * B0), is not necessarily zero.

    #get full list of basis matrices (in std basis) -- i.e. we use dmDim
    if not isinstance(choi_mx_basis, _Basis):
        choi_mx_basis = _Basis.cast(choi_mx_basis, N)  # we'd like a basis of dimension N

    BVec = choi_mx_basis.create_simple_equivalent().elements
    M = len(BVec)  # can be < N if basis has multiple block dims
    assert(M == N), 'Expected {}, got {}'.format(M, N)

    choiMx = _np.empty((N, N), 'complex')
    for i in range(M):
        for j in range(M):
            BiBj = _np.kron(BVec[i], _np.conjugate(BVec[j]))
            BiBj_dag = _np.transpose(_np.conjugate(BiBj))
            choiMx[i, j] = _mt.trace(_np.dot(opMxInStdBasis, BiBj_dag)) \
                / _mt.trace(_np.dot(BiBj, BiBj_dag))

    # This construction results in a Jmx with trace == dim(H) = sqrt(operation_mx.shape[0])
    #  (dimension of density matrix) but we'd like a Jmx with trace == 1, so normalize:
    choiMx_normalized = choiMx / dmDim
    return choiMx_normalized