コード例 #1
0
    def run_trotterized_solver(self, state: State, t0, tf, num=50, schedule=lambda t: None, times=None,
                               full_output=True, verbose=False):
        """Trotterized approximation of the Schrodinger equation"""
        assert state.is_ket

        # s is a ket specifying the initial codes
        # tf is the total simulation time
        if times is None:
            times = np.linspace(t0, tf, num=num)
        n = len(times)
        if full_output:
            z = np.zeros((n, state.shape[0], state.shape[1]), dtype=np.complex128)
        infodict = {'t': times}
        s = state.copy()
        for (i, t) in zip(range(n), times):
            schedule(t)
            if t == times[0] and full_output:
                z[i, ...] = state
            else:
                dt = times[i]-times[i-1]
                for hamiltonian in self.hamiltonians:
                    s = hamiltonian.evolve(s, dt)
            if full_output:
                z[i, ...] = s
        else:
            z = np.array([s])
        norms = np.linalg.norm(z, axis=(-2, -1))
        if verbose:
            print('Fraction of integrator results normalized:',
                  len(np.argwhere(np.isclose(norms, np.ones(norms.shape)) == 1)) / len(norms))
            print('Final state norm - 1:', norms[-1] - 1)
        norms = norms[:, np.newaxis, np.newaxis]
        z = z / norms
        return z, infodict
コード例 #2
0
    def run_trotterized_solver(self,
                               state: State,
                               t0,
                               tf,
                               num=50,
                               schedule=lambda t: None,
                               times=None,
                               full_output=True,
                               verbose=False):
        """Trotterized approximation of the Schrodinger equation"""
        assert not state.is_ket

        # s is a ket specifying the initial codes
        # tf is the total simulation time
        if times is None:
            times = np.linspace(0, 1, num=int(num)) * (tf - t0) + t0
        n = len(times)
        if full_output:
            z = np.zeros((n, state.shape[0], state.shape[1]),
                         dtype=np.complex128)
        infodict = {'t': times}
        s = state.copy()

        for (i, t) in zip(range(n), times):
            schedule(t)
            if t == times[0] and full_output:
                z[i, ...] = state
            else:
                if i != 0:
                    dt = times[i] - times[i - 1]
                else:
                    dt = times[i + 1] - times[i]
                for hamiltonian in self.hamiltonians:
                    s = hamiltonian.evolve(s, dt)
                for jump_operator in self.jump_operators:
                    if isinstance(jump_operator, LindbladJumpOperator):
                        if i == 1:
                            print(
                                'Warning: Evolving by a LindbladJumpOperator involves exponentiating a large matrix.',
                                'Consider a QuantumChannel.')
                        s = jump_operator.evolve(s, dt)
                    elif isinstance(jump_operator, QuantumChannel):
                        s = jump_operator.evolve(s, dt)
            if full_output:
                z[i, ...] = s
        if not full_output:
            z = np.array([s])
        norms = np.trace(z, axis1=-2, axis2=-1)
        if verbose:
            print(
                'Fraction of integrator results normalized:',
                len(np.argwhere(np.isclose(norms, np.ones(norms.shape)) == 1))
                / len(norms))
            print('Final state norm - 1:', norms[-1] - 1)
        norms = norms[:, np.newaxis, np.newaxis]
        z = z / norms
        return z, infodict
コード例 #3
0
ファイル: test_rydberg.py プロジェクト: maddiecain/qsim
    def test_single_qubit_operation(self):
        N = 6
        # Test single qubit operation
        psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
        psi0[0][0] = 1
        # Apply sigma_y on the second qubit to get 1j|020000>
        psi0 = rydberg.multiply(psi0, [1], rydberg.Y)
        self.assertTrue(psi0[(rydberg.d - 1) * rydberg.d**(N - 2), 0] == 1j)

        # Apply sigma_z on the second qubit, codes is -1j|010000>
        psi0 = rydberg.multiply(psi0, [1], rydberg.Z)
        self.assertTrue(psi0[(rydberg.d - 1) * rydberg.d**(N - 2), 0] == -1j)

        # Apply sigma_x on qubits
        psi0 = rydberg.multiply(psi0, [0, 2, 3, 4, 5],
                                tools.tensor_product([rydberg.X] * (N - 1)))

        # Vector is still normalized
        self.assertTrue(np.vdot(psi0, psi0) == 1)

        # Should be -1j|111111>
        self.assertTrue(psi0[-1, 0] == -1j)

        # Test rydberg operations with density matrices
        psi1 = np.array([
            tools.tensor_product([np.array([1, 0, 1]),
                                  np.array([1, 0, 0])]) / 2**(1 / 2)
        ]).T
        psi1 = State(tools.outer_product(psi1, psi1), code=rydberg)

        # Apply sigma_Y to first qubit
        psi2 = np.array([np.kron([1, 0, -1], [1, 0, 0]) * -1j / 2**(1 / 2)]).T
        rho2 = tools.outer_product(psi2, psi2)

        psi1 = rydberg.multiply(psi1, [0], ['Y'])
        self.assertTrue(np.linalg.norm(psi1 - rho2) <= 1e-10)

        psi0 = np.array([
            tools.tensor_product([np.array([1, 0, 1]),
                                  np.array([1, 0, 0])]) / 2**(1 / 2)
        ]).T
        psi0 = State(tools.outer_product(psi0, psi0), code=rydberg)
        psi1 = State(psi0.copy(), code=rydberg)

        # Apply sigma_Y to first qubit
        psi2 = np.array([np.kron([1, 0, -1], [1, 0, 0]) * -1j / 2**(1 / 2)]).T
        psi2 = tools.outer_product(psi2, psi2)

        # Apply single qubit operation to dmatrix
        psi0 = rydberg.multiply(psi0, [0], rydberg.Y)
        self.assertTrue(np.linalg.norm(psi0 - psi2) <= 1e-10)

        # Test on ket
        psi1 = rydberg.multiply(psi1, [0], rydberg.Y)
        self.assertTrue(np.linalg.norm(psi1 - psi2) <= 1e-10)
コード例 #4
0
    def test_multi_qubit(self):
        N = 5
        psi0 = State(
            tools.tensor_product([two_qubit_code.logical_basis[0]] * N))
        psi1 = psi0.copy()
        op = tools.tensor_product(
            [two_qubit_code.X, two_qubit_code.Y, two_qubit_code.Z])
        psi0 = two_qubit_code.multiply(psi0, [1, 3, 4], op)
        psi1 = two_qubit_code.multiply(psi1, [1, 3, 4], ['X', 'Y', 'Z'])

        self.assertTrue(np.allclose(psi0, psi1))
コード例 #5
0
    def test_multi_qubit(self):
        n = 5
        psi0 = State(
            tools.tensor_product([jordan_farhi_shor.logical_basis[0]] * n))
        psi1 = psi0.copy()
        op = tools.tensor_product(
            [jordan_farhi_shor.X, jordan_farhi_shor.Y, jordan_farhi_shor.Z])
        psi0 = jordan_farhi_shor.multiply(psi0, [1, 3, 4], op)
        psi1 = jordan_farhi_shor.multiply(psi1, [1, 3, 4], ['X', 'Y', 'Z'])

        self.assertTrue(np.allclose(psi0, psi1))
コード例 #6
0
ファイル: test_rydberg.py プロジェクト: maddiecain/qsim
 def test_multi_qubit_operation(self):
     N = 6
     psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
     psi0[0, 0] = 1
     psi1 = psi0.copy()
     op = tools.tensor_product([rydberg.X, rydberg.Y, rydberg.Z])
     psi0 = rydberg.multiply(psi0, [1, 3, 4], op)
     psi1 = rydberg.multiply(psi1, [1], 'X')
     psi1 = rydberg.multiply(psi1, [3], 'Y')
     psi1 = rydberg.multiply(psi1, [4], 'Z')
     self.assertTrue(np.allclose(psi0, psi1))
コード例 #7
0
ファイル: test_rydberg.py プロジェクト: maddiecain/qsim
 def test_multi_qubit_pauli(self):
     N = 6
     psi0 = State(np.zeros((rydberg.d**N, 1)), code=rydberg)
     psi0[0, 0] = 1
     psi1 = psi0.copy()
     psi2 = psi0.copy()
     psi3 = psi0.copy()
     psi4 = psi0.copy()
     psi0 = rydberg.multiply(psi0, [1, 3, 4], ['X', 'Y', 'Z'])
     psi1 = rydberg.multiply(psi1, [1], 'X')
     psi1 = rydberg.multiply(psi1, [3], 'Y')
     psi1 = rydberg.multiply(psi1, [4], 'Z')
     psi2 = rydberg.multiply(psi2, [4, 1, 3], ['Z', 'X', 'Y'])
     psi3 = rydberg.multiply(
         psi3, [1, 3, 4],
         tools.tensor_product([rydberg.X, rydberg.Y, rydberg.Z]))
     psi4 = rydberg.multiply(
         psi4, [4, 1, 3],
         tools.tensor_product([rydberg.Z, rydberg.X, rydberg.Y]))
     self.assertTrue(np.allclose(psi0, psi1))
     self.assertTrue(np.allclose(psi1, psi2))
     self.assertTrue(np.allclose(psi2, psi3))
     self.assertTrue(np.allclose(psi3, psi4))
コード例 #8
0
def multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    # Type handling
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tensor_product(op)
    if not state.is_ket:
        if pauli:
            out = state.copy()
            for i in range(len(apply_to)):
                ind = d**apply_to[i]
                out = out.reshape(
                    (-1, d, d**(state.number_physical_qudits - 1), d, ind),
                    order='F')
                if op[i] == 'X':  # Sigma_X
                    out = np.flip(out, axis=(1, 3))
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, axis=(1, 3))
                    out[:, d - 1, :, 0, :] = -out[:, d - 1, :, 0, :]
                    out[:, 0, :, d - 1, :] = -out[:, 0, :, d - 1, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :, 0, :] = -out[:, d - 1, :, 0, :]
                    out[:, 0, :, d - 1, :] = -out[:, 0, :, d - 1, :]

            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            # Note that the conjugate transpose it taken automatically in right_multiply
            return right_multiply(left_multiply(state, apply_to, op), apply_to,
                                  op)
    else:
        return left_multiply(state, apply_to, op)
コード例 #9
0
    def test_depolarize(self):
        # First test single qubit channel
        psi0 = State(np.zeros((4, 1)))
        psi0[0] = 1
        psi0 = State(tools.outer_product(psi0, psi0))
        psi1 = psi0.copy()
        psi2 = psi0.copy()
        psi3 = psi0.copy()
        psi4 = psi0.copy()
        p = 0.093
        op0 = quantum_channels.DepolarizingChannel()
        psi0 = op0.channel(psi0, p, apply_to=1)
        op1 = quantum_channels.DepolarizingChannel()
        psi1 = op1.channel(psi1, 2 * p, apply_to=0)
        self.assertTrue(psi1[2, 2] == 0.124)
        self.assertTrue(psi0[1, 1] == 0.062)

        # Now test multi qubit channel
        psi2 = op0.channel(psi2, p, apply_to=[0, 1])
        psi3 = op0.channel(psi3, p, apply_to=0)
        psi3 = op0.channel(psi3, p, apply_to=1)
        psi4 = op0.channel(psi4, p)
        self.assertTrue(np.allclose(psi2, psi3))
        self.assertTrue(np.allclose(psi2, psi4))

        expected = np.zeros((4, 4))
        expected[0, 0] = 0.46827
        expected[1, 1] = 0.03173
        expected[2, 2] = 0.46827
        expected[3, 3] = 0.03173
        psi0 = op0.evolve(psi0, 20)
        self.assertTrue(np.allclose(expected, psi0))

        psi0 = State(np.array([[1, 0], [0, 0]]))
        psi0 = op0.evolve(psi0, 20)
        self.assertTrue(np.allclose(psi0, .5 * np.identity(2)))
コード例 #10
0
def multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    # Type handling: to determine if Pauli, check if a list of strings
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tools.tensor_product(op)
    if not state.is_ket:
        if pauli:
            out = state.copy()
            for i in range(len(apply_to)):
                # Note index start from the right (sN,...,s3,s2,s1)
                # Note index start from the right (sN,...,s3,s2,s1)
                if op[i] == 'X':  # Sigma_X
                    out = qubit.left_multiply(
                        out, [n * apply_to[i], n * apply_to[i] + 2],
                        ['Y', 'Y'])
                elif op[i] == 'Y':  # Sigma_Y
                    out = -1 * qubit.left_multiply(
                        out, [n * apply_to[i] + 1, n * apply_to[i] + 2],
                        ['X', 'X'])
                elif op[i] == 'Z':  # Sigma_Z
                    out = qubit.left_multiply(
                        out, [n * apply_to[i], n * apply_to[i] + 1],
                        ['Z', 'Z'])
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            return right_multiply(left_multiply(state, apply_to, op), apply_to,
                                  op)
    else:
        return left_multiply(state, apply_to, op)
コード例 #11
0
    def test_single_qubit(self):
        psi0 = State(
            tools.tensor_product([
                three_qubit_code.logical_basis[0],
                three_qubit_code.logical_basis[0]
            ]))
        psi1 = psi0.copy()
        # Density matrix test
        psi2 = State(tools.outer_product(psi1, psi1))
        psi0 = three_qubit_code.multiply(psi0, [1], ['Y'])
        # Test non-pauli operation
        psi1 = three_qubit_code.multiply(psi1, [1], three_qubit_code.Y)
        psi2 = three_qubit_code.multiply(psi2, [1], three_qubit_code.Y)
        res = 1j * tools.tensor_product([
            three_qubit_code.logical_basis[0],
            three_qubit_code.logical_basis[1]
        ])
        # Should get out 1j|0L>|1L>
        self.assertTrue(np.allclose(psi0, res))
        self.assertTrue(np.allclose(psi1, res))
        self.assertTrue(np.allclose(psi2, tools.outer_product(res, res)))

        self.assertTrue(
            np.allclose(
                three_qubit_code.multiply(psi0, [1], ['Z']),
                -1j * tools.tensor_product([
                    three_qubit_code.logical_basis[0],
                    three_qubit_code.logical_basis[1]
                ])))
        psi0 = three_qubit_code.multiply(psi0, [0], ['X'])
        # Should get out -1j|1L>|1L>
        self.assertTrue(
            np.allclose(
                psi0, 1j * tools.tensor_product([
                    three_qubit_code.logical_basis[1],
                    three_qubit_code.logical_basis[1]
                ])))
        # Rotate all qubits
        for i in range(2):
            psi0 = three_qubit_code.rotation(psi0, [i], np.pi / 2,
                                             three_qubit_code.X)
        self.assertTrue(
            np.allclose(
                psi0, -1j * tools.tensor_product([
                    three_qubit_code.logical_basis[0],
                    three_qubit_code.logical_basis[0]
                ])))
コード例 #12
0
ファイル: quantum_channels.py プロジェクト: maddiecain/qsim
 def evolve(self,
            state: State,
            time,
            threshold=.05,
            apply_to: Union[int, list] = None):
     if state.is_ket:
         print('Converting ket to density matrix.')
         state = State(tools.outer_product(state, state))
     if apply_to is None:
         apply_to = list(range(state.number_physical_qudits))
     # Assume that apply_to is a list of integers
     if isinstance(apply_to, int):
         apply_to = [apply_to]
     n = 1
     # Find a number of repetitions n small enough so that channel evolution is well approximated
     while (self.rates[0] * time)**2 / n > threshold:
         n += 1
     p = self.rates[0] * time / n
     s = state.copy()
     # Apply channel n times
     for i in range(n):
         s = self.channel(s, p, apply_to=apply_to)
     return s
コード例 #13
0
    def variational_grad(self, param, initial_state=None):
        """Calculate the objective function F and its gradient exactly
            Input:
                param = parameters of QAOA

            Output: (F, Fgrad)
               F = <HamC> for minimization
               Fgrad = gradient of F with respect to param
        """
        # TODO: make this work for continuous noise models
        if self.noise_model == 'continuous':
            raise NotImplementedError(
                'Variational gradient does not currently support continuous noise model'
            )
        param = np.asarray(param)
        # Preallocate space for storing copies of wavefunction - necessary for efficient computation of analytic
        # gradient
        if self.code.logical_code and initial_state is None:
            if isinstance(self.cost_hamiltonian, HamiltonianMIS):
                initial_state = State(tensor_product(
                    [self.code.logical_basis[1]] * self.N),
                                      code=self.code)
        elif initial_state is None:
            if isinstance(self.cost_hamiltonian, HamiltonianMIS):
                initial_state = State(np.zeros(
                    (self.cost_hamiltonian.hamiltonian.shape[0], 1)),
                                      code=self.code)
                initial_state[-1, -1] = 1
            else:
                initial_state = State(
                    np.ones((self.cost_hamiltonian.hamiltonian.shape[0], 1)) /
                    np.sqrt(self.cost_hamiltonian.hamiltonian.shape[0]),
                    code=self.code)
        if not (self.noise_model is None or self.noise_model == 'monte_carlo'):
            # Initial s should be a density matrix
            initial_state = State(outer_product(initial_state, initial_state),
                                  code=self.code)
        psi = initial_state
        if initial_state.is_ket:
            memo = np.zeros([psi.shape[0], 2 * self.depth + 2],
                            dtype=np.complex128)
            memo[:, 0] = np.squeeze(psi.T)
            tester = psi.copy()
        else:
            memo = np.zeros([psi.shape[0], psi.shape[0], self.depth + 1],
                            dtype=np.complex128)
            memo[..., 0] = np.squeeze(outer_product(psi, psi))
            tester = State(outer_product(psi, psi), code=self.code)
        # Evolving forward
        for j in range(self.depth):
            if initial_state.is_ket:
                tester = self.hamiltonian[j].evolve(tester, param[j])
                memo[:, j + 1] = np.squeeze(tester.T)
            else:
                self.hamiltonian[j].evolve(tester, param[j])
                # Goes through memo, evolves every density matrix in it, and adds one more in the j*m+i+1 position
                # corresponding to H_i*p
                s0_prenoise = memo[..., 0]
                for k in range(j + 1):
                    s = State(memo[..., k], code=self.code)
                    s = self.hamiltonian[j].evolve(s, param[j])
                    if k == 0:
                        s0_prenoise = s.copy()
                    if self.noise_model is not None:
                        if not (self.noise[j] is None):
                            s = self.noise[j].evolve(s, param[j])
                    memo[..., k] = s.copy()
                s0_prenoise = self.hamiltonian[j].left_multiply(s0_prenoise)
                if self.noise_model is not None:
                    if not (self.noise[j] is None):
                        s0_prenoise = self.noise[j].evolve(
                            s0_prenoise, param[j])
                memo[..., j + 1] = s0_prenoise.copy()

        # Multiply by cost_hamiltonian
        if initial_state.is_ket:
            memo[:, self.depth +
                 1] = self.cost_hamiltonian.hamiltonian @ memo[:, self.depth]

            s = State(np.array([memo[:, self.depth + 1]]).T, code=self.code)
        else:
            for k in range(self.depth + 1):
                s = memo[..., k]
                s = State(self.cost_hamiltonian.hamiltonian * s,
                          code=self.code)
                memo[..., k] = s

        # Evolving backwards, if ket:
        if initial_state.is_ket:
            for k in range(self.depth):
                s = self.hamiltonian[self.depth - k - 1].evolve(
                    s, -1 * param[self.depth - k - 1])
                memo[:, self.depth + k + 2] = np.squeeze(s.T)

        # Evaluating objective function
        if initial_state.is_ket:
            F = np.real(np.vdot(memo[:, self.depth], memo[:, self.depth + 1]))
        else:
            F = np.real(np.trace(memo[..., 0]))

        # Evaluating gradient analytically
        Fgrad = np.zeros(self.depth)
        for r in range(self.depth):
            if initial_state.is_ket:
                s = State(np.array([memo[:, 2 * self.depth + 1 - r]]).T,
                          code=self.code)
                s = self.hamiltonian[r].left_multiply(s)
                Fgrad[r] = -2 * np.imag(np.vdot(memo[:, r], np.squeeze(s.T)))
            else:
                Fgrad[r] = 2 * np.imag(np.trace(memo[..., r + 1]))
        return F, Fgrad
コード例 #14
0
def left_multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    # Handle typing
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tensor_product(op)
    n_op = len(apply_to)
    if not pauli:
        if is_sorted(apply_to):
            # Generate all shapes for left multiplication
            preshape = d * np.ones((2, n_op), dtype=int)
            preshape[1,
                     0] = int(state.dimension / (d**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[1, 1:] = np.flip(d**np.diff(apply_to)) / 2

            shape1 = np.zeros(2 * n_op + 1, dtype=int)
            shape2 = np.zeros(2 * n_op + 1, dtype=int)
            order1 = np.zeros(2 * n_op + 1, dtype=int)
            order2 = np.zeros(2 * n_op + 1, dtype=int)

            shape1[:-1] = np.flip(preshape, axis=0).reshape((2 * n_op),
                                                            order='F')
            shape1[-1] = -1
            shape2[:-1] = preshape.reshape((-1), order='C')
            shape2[-1] = -1

            preorder = np.arange(2 * n_op)
            order1[:-1] = np.flip(preorder.reshape((-1, 2), order='C'),
                                  axis=1).reshape((-1), order='F')
            order2[:-1] = np.flip(preorder.reshape((2, -1), order='C'),
                                  axis=0).reshape((-1), order='F')
            order1[-1] = 2 * n_op
            order2[-1] = 2 * n_op

            # Now left multiply
            out = state.reshape(shape1, order='F').transpose(order1)
            out = np.dot(op, out.reshape((d**n_op, -1), order='F'))
            out = out.reshape(shape2, order='F').transpose(order2)
            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            # Need to reshape the operator given
            apply_to = np.asarray(apply_to, dtype=int)
            new_shape = d * np.ones(2 * n_op, dtype=int)
            permut = np.argsort(apply_to)
            transpose_ord = np.zeros(2 * n_op, dtype=int)

            transpose_ord[:n_op] = (n_op - 1) * np.ones(
                n_op, dtype=int) - np.flip(permut, axis=0)
            transpose_ord[n_op:] = (2 * n_op - 1) * np.ones(
                n_op, dtype=int) - np.flip(permut, axis=0)

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   (d**n_op, d**n_op),
                                   order='F')
            sorted_apply_to = apply_to[permut]

            return left_multiply(state, sorted_apply_to, sorted_op)
    else:
        # op should be a list of Pauli operators
        out = state.copy()
        for i in range(len(apply_to)):
            ind = d**apply_to[i]
            if state.is_ket:
                # Note index start from the right (sN,...,s3,s2,s1)
                out = out.reshape((-1, d, ind), order='F')
                if op[i] == 'X':  # Sigma_X
                    out = np.flip(out, 1)
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, 1)
                    out[:, 0, :] = -1j * out[:, 0, :]
                    out[:, d - 1, :] = 1j * out[:, d - 1, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :] = -out[:, d - 1, :]

                out = out.reshape(state.shape, order='F')
            else:
                out = out.reshape(
                    (-1, d, d**(state.number_physical_qudits - 1), d, ind),
                    order='F')
                if op[i] == 'X':  # Sigma_X
                    out = np.flip(out, 1)
                elif op[i] == 'Y':  # Sigma_Y
                    out = np.flip(out, axis=1)
                    out[:, 0, :, :, :] = -1j * out[:, 0, :, :, :]
                    out[:, d - 1, :, :, :] = 1j * out[:, d - 1, :, :, :]
                elif op[i] == 'Z':  # Sigma_Z
                    out[:, d - 1, :, :, :] = -out[:, d - 1, :, :, :]

                out = out.reshape(state.shape, order='F')
        return State(out,
                     is_ket=state.is_ket,
                     IS_subspace=state.IS_subspace,
                     code=state.code)
コード例 #15
0
ファイル: two_qubit_code.py プロジェクト: maddiecain/qsim
def left_multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    # Type handling: to determine if Pauli, check if a list of strings
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tools.tensor_product(op)
    n_op = len(apply_to)
    if not pauli:
        if tools.is_sorted(apply_to):
            # Generate all shapes for left multiplication
            preshape = (d**n) * np.ones((2, n_op), dtype=int)
            preshape[1, 0] = int(state.dimension /
                                 ((d**n)**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[1, 1:] = np.flip((d**n)**np.diff(apply_to)) / (d**n)

            shape1 = np.zeros(2 * n_op + 1, dtype=int)
            shape2 = np.zeros(2 * n_op + 1, dtype=int)
            order1 = np.zeros(2 * n_op + 1, dtype=int)
            order2 = np.zeros(2 * n_op + 1, dtype=int)

            shape1[:-1] = np.flip(preshape, axis=0).reshape((2 * n_op),
                                                            order='F')
            shape1[-1] = -1
            shape2[:-1] = preshape.reshape((-1), order='C')
            shape2[-1] = -1

            preorder = np.arange(2 * n_op)
            order1[:-1] = np.flip(preorder.reshape((-1, 2), order='C'),
                                  axis=1).reshape((-1), order='F')
            order2[:-1] = np.flip(preorder.reshape((2, -1), order='C'),
                                  axis=0).reshape((-1), order='F')
            order1[-1] = 2 * n_op
            order2[-1] = 2 * n_op

            # Now left multiply
            out = state.reshape(shape1, order='F').transpose(order1)
            out = np.dot(op, out.reshape(((d**n)**n_op, -1), order='F'))
            out = out.reshape(shape2, order='F').transpose(order2)
            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            # Need to reshape the operator given
            new_shape = (d**n) * np.ones(2 * n_op, dtype=int)
            permut = np.argsort(apply_to)
            transpose_ord = np.zeros(2 * n_op, dtype=int)
            transpose_ord[:n_op] = permut
            transpose_ord[n_op:] = n_op * np.ones(n_op, dtype=int) + permut

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   ((d**n)**n_op, (d**n)**n_op),
                                   order='F')
            sorted_apply_to = apply_to[permut]

            return left_multiply(state, sorted_apply_to, sorted_op)
    else:
        # op should be a list of Pauli operators, or
        out = state.copy()
        for i in range(len(apply_to)):
            if op[i] == 'X':  # Sigma_X
                out = qubit.left_multiply(out, [n * apply_to[i]], ['X'])
            elif op[i] == 'Y':  # Sigma_Y
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Y', 'Z'])
            elif op[i] == 'Z':  # Sigma_Z
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Z', 'Z'])
        return State(out,
                     is_ket=state.is_ket,
                     IS_subspace=state.IS_subspace,
                     code=state.code)
コード例 #16
0
ファイル: two_qubit_code.py プロジェクト: maddiecain/qsim
def right_multiply(state: State, apply_to: Union[int, list], op):
    """
    Apply a multi-qubit operator on several qubits (indexed in apply_to) of the input codes.
    :param state: input wavefunction or density matrix
    :type state: np.ndarray
    :param apply_to: zero-based indices of qudit locations to apply the operator
    :type apply_to: list of int
    :param op: Operator to act with.
    :type op: np.ndarray (2-dimensional)
    """
    if isinstance(apply_to, int):
        apply_to = [apply_to]
    if not isinstance(op, list):
        op = [op]
    # Type handling: to determine if Pauli, check if a list of strings
    pauli = False
    if isinstance(op, list):
        if all(isinstance(elem, str) for elem in op):
            pauli = True
        else:
            op = tools.tensor_product(op)
    if state.is_ket:
        print(
            'Warning: right multiply functionality currently applies the operator and daggers the s.'
        )
    n_op = len(apply_to)
    if not pauli:
        if tools.is_sorted(apply_to):
            # generate necessary shapes
            preshape = (d**n) * np.ones((2, n_op), dtype=int)
            preshape[0, 0] = int(state.dimension /
                                 ((d**n)**(1 + apply_to[n_op - 1])))
            if n_op > 1:
                preshape[0, 1:] = np.flip((d**n)**np.diff(apply_to)) / (d**n)

            shape3 = np.zeros(2 * n_op + 2, dtype=int)
            shape3[0] = state.dimension
            shape3[1:-1] = np.reshape(preshape, (2 * n_op), order='F')
            shape3[-1] = -1

            shape4 = np.zeros(2 * n_op + 2, dtype=int)
            shape4[0] = state.dimension
            shape4[1:n_op + 1] = preshape[0]
            shape4[n_op + 1] = -1
            shape4[n_op + 2:] = preshape[1]

            order3 = np.zeros(2 * n_op + 2, dtype=int)
            order3[0] = 0
            order3[1:n_op + 2] = 2 * np.arange(n_op + 1) + np.ones(n_op + 1)
            order3[n_op + 2:] = 2 * np.arange(1, n_op + 1)

            order4 = np.zeros(2 * n_op + 2, dtype=int)
            order4[0] = 0
            order4[1] = 1
            order4[2:] = np.flip(np.arange(2, 2 * n_op + 2).reshape((2, -1),
                                                                    order='C'),
                                 axis=0).reshape((-1), order='F')

            # right multiply
            out = state.reshape(shape3, order='F').transpose(order3)
            out = np.dot(out.reshape((-1, (d**n)**n_op), order='F'),
                         op.conj().T)
            out = out.reshape(shape4, order='F').transpose(order4)
            out = out.reshape(state.shape, order='F')
            return State(out,
                         is_ket=state.is_ket,
                         IS_subspace=state.IS_subspace,
                         code=state.code)
        else:
            new_shape = 2 * np.ones(2 * n_op, dtype=int)
            permut = np.argsort(apply_to)
            transpose_ord = np.zeros(2 * n_op, dtype=int)
            transpose_ord[:n_op] = permut
            transpose_ord[n_op:] = n_op * np.ones(n_op, dtype=int) + permut

            sorted_op = np.reshape(np.transpose(np.reshape(op,
                                                           new_shape,
                                                           order='F'),
                                                axes=transpose_ord),
                                   ((d**n)**n_op, (d**n)**n_op),
                                   order='F')
            sorted_apply_to = apply_to[permut]

            return right_multiply(state, sorted_apply_to, sorted_op)
    else:
        out = state.copy()
        for i in range(len(apply_to)):
            # Note index start from the right (sN,...,s3,s2,s1)
            if op[i] == 'X':  # Sigma_X
                out = qubit.left_multiply(out, [n * apply_to[i]], ['X'])
            elif op[i] == 'Y':  # Sigma_Y
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Y', 'Z'])
            elif op[i] == 'Z':  # Sigma_Z
                out = qubit.left_multiply(
                    out, [n * apply_to[i], n * apply_to[i] + 1], ['Z', 'Z'])
        return State(out,
                     is_ket=state.is_ket,
                     IS_subspace=state.IS_subspace,
                     code=state.code)
コード例 #17
0
ファイル: quantum_channels.py プロジェクト: maddiecain/qsim
    def channel(self,
                state: State,
                p: float,
                apply_to: Union[int, list] = None):
        """
        Applies ``povm`` homogeneously to the qudits identified in apply_to.

        :param p:
        :param apply_to:
        :param state: State to operate on.
        :type state: np.ndarray
        :return:
        """
        # If the input s is a ket, convert it to a density matrix
        if state.is_ket:
            print('Converting ket to density matrix.')
            state = State(tools.outer_product(state, state))
        if apply_to is None:
            apply_to = list(range(state.number_physical_qudits))

        # Assume that apply_to is a list of integers
        if isinstance(apply_to, int):
            apply_to = [apply_to]

        if state.code.logical_code:
            # Assume that logical codes are composed of qubits
            code = self.code
        else:
            code = state.code

        if self.IS_subspace:
            povm = self.povm(p)
            temp = state.copy()
            out = None
            for i in apply_to:
                out = State(np.zeros_like(state),
                            is_ket=state.is_ket,
                            code=state.code,
                            IS_subspace=state.IS_subspace,
                            graph=state.graph)
                for j in range(len(povm[i])):
                    out = out + povm[i][j] @ temp @ povm[i][j].conj().T
                temp = out
            return out
        # Handle apply_to recursively
        # Only apply to one qudit
        else:
            # Empty s to store the output
            out = State(np.zeros_like(state),
                        is_ket=state.is_ket,
                        code=state.code,
                        IS_subspace=state.IS_subspace,
                        graph=state.graph)

            if len(apply_to) == 1:
                povm = self.povm(p)
                for j in range(len(povm)):
                    out = out + code.multiply(state, apply_to, povm[j])
                return out
            else:
                last_element = apply_to.pop()
                recursive_solution = self.channel(state, p, apply_to=apply_to)
                povm = self.povm(p)
                for j in range(len(povm)):
                    out = out + code.multiply(recursive_solution,
                                              [last_element], povm[j])
                return out