def test_ghz_2_3(): """Produces the 3-qubit GHZ state: `1/sqrt(2) * (|000> + |111>)`.""" e_0, e_1 = basis(2, 0), basis(2, 1) expected_res = 1 / np.sqrt(2) * (tensor(e_0, e_0, e_0) + tensor(e_1, e_1, e_1)) res = ghz(2, 3).toarray() bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_w_state_3(): """The 3-qubit W-state.""" e_0, e_1 = basis(2, 0), basis(2, 1) expected_res = (1 / np.sqrt(3) * (tensor(e_1, e_0, e_0) + tensor(e_0, e_1, e_0) + tensor(e_0, e_0, e_1))) res = w_state(3) bool_mat = np.isclose(res, expected_res, atol=0.2) np.testing.assert_equal(np.all(bool_mat), True)
def test_w_state_generalized(): """Generalized 4-qubit W-state.""" e_0, e_1 = basis(2, 0), basis(2, 1) expected_res = ( 1 / np.sqrt(30) * (tensor(e_1, e_0, e_0, e_0) + 2 * tensor(e_0, e_1, e_0, e_0) + 3 * tensor(e_0, e_0, e_1, e_0) + 4 * tensor(e_0, e_0, e_0, e_1))) coeffs = np.array([1, 2, 3, 4]) / np.sqrt(30) res = w_state(4, coeffs) bool_mat = np.isclose(res, expected_res, atol=0.2) np.testing.assert_equal(np.all(bool_mat), True)
def __init__(self, prob_mat: np.ndarray, pred_mat: np.ndarray, reps: int = 1) -> None: """ Construct extended nonlocal game object. :param prob_mat: A matrix whose (x, y)-entry gives the probability that the referee will give Alice the value `x` and Bob the value `y`. :param pred_mat: :param reps: Number of parallel repetitions to perform. """ if reps == 1: self.prob_mat = prob_mat self.pred_mat = pred_mat self.reps = reps else: ( dim_x, dim_y, num_alice_out, num_bob_out, num_alice_in, num_bob_in, ) = pred_mat.shape self.prob_mat = tensor(prob_mat, reps) pred_mat2 = np.zeros(( dim_x**reps, dim_y**reps, num_alice_out**reps, num_bob_out**reps, num_alice_in**reps, num_bob_in**reps, )) i_ind = np.zeros(reps, dtype=int) j_ind = np.zeros(reps, dtype=int) for i in range(num_alice_in**reps): for j in range(num_bob_in**reps): to_tensor = np.empty( [reps, dim_x, dim_y, num_alice_out, num_bob_out]) for k in range(reps - 1, -1, -1): to_tensor[k] = pred_mat[:, :, :, :, i_ind[k], j_ind[k]] pred_mat2[:, :, :, :, i, j] = tensor(to_tensor) j_ind = update_odometer(j_ind, num_bob_in * np.ones(reps)) i_ind = update_odometer(i_ind, num_alice_in * np.ones(reps)) self.pred_mat = pred_mat2 self.reps = reps
def test_tensor_single_arg(): """Performing tensor product on one item should return item back.""" input_arr = np.array([[1, 2], [3, 4]]) res = tensor(input_arr) bool_mat = np.isclose(res, input_arr) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_n_0(): """Test tensor n=0 times.""" e_0 = basis(2, 0) expected_res = None res = tensor(e_0, 0) np.testing.assert_equal(res, expected_res)
def test_tensor_n_2(): """Test tensor n=2 times.""" e_0 = basis(2, 0) expected_res = np.kron(e_0, e_0) res = tensor(e_0, 2) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_n_1(): """Test tensor n=1 times.""" e_0 = basis(2, 0) expected_res = e_0 res = tensor(e_0, 1) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor(): """Test standard tensor on vectors.""" e_0 = basis(2, 0) expected_res = np.kron(e_0, e_0) res = tensor(e_0, e_0) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_array_of_numpy_arrays_three(): """Performing tensor product on three numpy array of numpy arrays.""" input_arr = np.array([np.identity(2), np.identity(2), np.identity(2)]) res = tensor(input_arr) expected_res = np.identity(8) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_list_3(): """Test tensor list with three items.""" e_0, e_1 = basis(2, 0), basis(2, 1) expected_res = np.kron(np.kron(e_0, e_1), e_0) res = tensor([e_0, e_1, e_0]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_list_1(): """Test tensor list with one item.""" e_0 = basis(2, 0) expected_res = e_0 res = tensor([e_0]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_multiple_args(): """Performing tensor product on multiple matrices.""" input_arr_1 = np.identity(2) input_arr_2 = np.identity(2) input_arr_3 = np.identity(2) input_arr_4 = np.identity(2) res = tensor(input_arr_1, input_arr_2, input_arr_3, input_arr_4) bool_mat = np.isclose(res, np.identity(16)) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_array_of_numpy_arrays_two(): """Performing tensor product on two numpy array of numpy arrays.""" input_arr = np.array( [np.array([[1, 2], [3, 4]]), np.array([[5, 6], [7, 8]])]) res = tensor(input_arr) expected_res = np.array([[5, 6, 10, 12], [7, 8, 14, 16], [15, 18, 20, 24], [21, 24, 28, 32]]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_ghz_4_7(): r""" The following generates the following GHZ state in `(C^4)^{\otimes 7}`. `1/sqrt(30) * (|0000000> + 2|1111111> + 3|2222222> + 4|3333333>)`. """ e0_4 = np.array([[1], [0], [0], [0]]) e1_4 = np.array([[0], [1], [0], [0]]) e2_4 = np.array([[0], [0], [1], [0]]) e3_4 = np.array([[0], [0], [0], [1]]) expected_res = (1 / np.sqrt(30) * (tensor(e0_4, e0_4, e0_4, e0_4, e0_4, e0_4, e0_4) + 2 * tensor(e1_4, e1_4, e1_4, e1_4, e1_4, e1_4, e1_4) + 3 * tensor(e2_4, e2_4, e2_4, e2_4, e2_4, e2_4, e2_4) + 4 * tensor(e3_4, e3_4, e3_4, e3_4, e3_4, e3_4, e3_4))) res = ghz(4, 7, np.array([1, 2, 3, 4]) / np.sqrt(30)).toarray() bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True)
def test_tensor_empty_args(): r"""Test tensor with no arguments.""" with np.testing.assert_raises(ValueError): tensor()
def brauer(dim: int, p_val: int) -> np.ndarray: r""" Produce all Brauer states [WikBrauer]_. Produce a matrix whose columns are all of the (unnormalized) "Brauer" states: states that are the :code:`p_val`-fold tensor product of the standard maximally-entangled pure state on :code:`dim` local dimensions. There are many such states, since there are many different ways to group the :code:`2 * p_val` parties into :code:`p_val` pairs (with each pair corresponding to one maximally-entangled state). The exact number of such states is: ```python np.factorial(2 * p_val) / (np.factorial(p_val) * 2**p_val) ``` which is the number of columns of the returned matrix. This function has been adapted from QETLAB. Examples ========== Generate a matrix whose columns are all Brauer states on 4 qubits. >>> from toqito.states import brauer >>> brauer(2, 2) [[1. 1. 1.] [0. 0. 0.] [0. 0. 0.] [1. 0. 0.] [0. 0. 0.] [0. 1. 0.] [0. 0. 1.] [0. 0. 0.] [0. 0. 0.] [0. 0. 1.] [0. 1. 0.] [0. 0. 0.] [1. 0. 0.] [0. 0. 0.] [0. 0. 0.] [1. 1. 1.]] References ========== .. [WikBrauer] Wikipedia: Brauer algebra https://en.wikipedia.org/wiki/Brauer_algebra :param dim: Dimension of each local subsystem :param p_val: Half of the number of parties (i.e., the state that this function computes will live in :math:`(\mathbb{C}^D)^{\otimes 2 P})` :return: Matrix whose columns are all of the unnormalized Brauer states. """ # The Brauer states are computed from perfect matchings of the complete graph. So compute all # perfect matchings first. phi = tensor(max_entangled(dim, False, False), p_val) matchings = perfect_matchings(2 * p_val) num_matchings = matchings.shape[0] state = np.zeros((dim**(2 * p_val), num_matchings)) # Turn these perfect matchings into the corresponding states. for i in range(num_matchings): state[:, i] = permute_systems(phi, matchings[i, :], dim * np.ones((1, 2 * p_val), dtype=int)[0]) return state
def test_tensor_list_0(): """Test tensor empty list.""" expected_res = None res = tensor([]) np.testing.assert_equal(res, expected_res)
def pauli(ind: Union[int, str, List[int], List[str]], is_sparse: bool = False) -> Union[np.ndarray, sparse.csr_matrix]: r""" Produce a Pauli operator [WikPauli]_. Provides the 2-by-2 Pauli matrix indicated by the value of `ind`. The variable `ind = 1` gives the Pauli-X operator, `ind = 2` gives the Pauli-Y operator, `ind =3` gives the Pauli-Z operator, and `ind = 0`gives the identity operator. Alternatively, `ind` can be set to "I", "X", "Y", or "Z" (case insensitive) to indicate the Pauli identity, X, Y, or Z operator. The 2-by-2 Pauli matrices are defined as the following matrices: .. math:: \begin{equation} \begin{aligned} X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}, \quad Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix}, \quad Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}, \quad I = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}. \end{aligned} \end{equation} Examples ========== Example for identity Pauli matrix. >>> from toqito.matrices import pauli >>> pauli("I") [[1., 0.], [0., 1.]]) Example for Pauli-X matrix. >>> from toqito.matrices import pauli >>> pauli("X") [[0, 1], [1, 0]]) Example for Pauli-Y matrix. >>> from toqito.matrices import pauli >>> pauli("Y") [[ 0.+0.j, -0.-1.j], [ 0.+1.j, 0.+0.j]]) Example for Pauli-Z matrix. >>> from toqito.matrices import pauli >>> pauli("Z") [[ 1, 0], [ 0, -1]]) References ========== .. [WikPauli] Wikipedia: Pauli matrices https://en.wikipedia.org/wiki/Pauli_matrices :param ind: The index to indicate which Pauli operator to generate. :param is_sparse: Returns a sparse matrix if set to True and a non-sparse matrix if set to False. """ if isinstance(ind, (int, str)): if ind in ("x", "X", 1): pauli_mat = np.array([[0, 1], [1, 0]]) elif ind in ("y", "Y", 2): pauli_mat = np.array([[0, -1j], [1j, 0]]) elif ind in ("z", "Z", 3): pauli_mat = np.array([[1, 0], [0, -1]]) else: pauli_mat = np.identity(2) if is_sparse: pauli_mat = sparse.csr_matrix(pauli_mat) return pauli_mat num_qubits = len(ind) pauli_mats = [] for i in range(num_qubits - 1, -1, -1): pauli_mats.append(pauli(ind[i], is_sparse)) return tensor(pauli_mats)
def optimal_clone( states: List[np.ndarray], probs: List[float], num_reps: int = 1, strategy: bool = False, ) -> Union[float, np.ndarray]: r""" Compute probability of counterfeiting quantum money [MVW12]_. The primal problem for the :math:`n`-fold parallel repetition is given as follows: .. math:: \begin{equation} \begin{aligned} \text{maximize:} \quad & \langle W_{\pi} \left(Q^{\otimes n} \right) W_{\pi}^*, X \rangle \\ \text{subject to:} \quad & \text{Tr}_{\mathcal{Y}^{\otimes n} \otimes \mathcal{Z}^{\otimes n}}(X) = \mathbb{I}_{\mathcal{X}^{\otimes n}},\\ & X \in \text{Pos}( \mathcal{Y}^{\otimes n} \otimes \mathcal{Z}^{\otimes n} \otimes \mathcal{X}^{\otimes n}) \end{aligned} \end{equation} The dual problem for the :math:`n`-fold parallel repetition is given as follows: .. math:: \begin{equation} \begin{aligned} \text{minimize:} \quad & \text{Tr}(Y) \\ \text{subject to:} \quad & \mathbb{I}_{\mathcal{Y}^{\otimes n} \otimes \mathcal{Z}^{\otimes n}} \otimes Y \geq W_{\pi} \left( Q^{\otimes n} \right) W_{\pi}^*, \\ & Y \in \text{Herm} \left(\mathcal{X}^{\otimes n} \right) \end{aligned} \end{equation} Examples ========== Wiesner's original quantum money scheme [Wies83]_ was shown in [MVW12]_ to have an optimal probability of 3/4 for succeeding a counterfeiting attack. Specifically, in the single-qubit case, Wiesner's quantum money scheme corresponds to the following ensemble: .. math:: \left\{ \left( \frac{1}{4}, |0\rangle \right), \left( \frac{1}{4}, |1\rangle \right), \left( \frac{1}{4}, |+\rangle \right), \left( \frac{1}{4}, |-\rangle \right) \right\}, which yields the operator .. math:: \begin{equation} Q = \frac{1}{4} \left(|000 \rangle \langle 000| + |111 \rangle \langle 111| + |+++ \rangle + \langle +++| + |--- \rangle \langle ---| \right) \end{equation} We can see that the optimal value we obtain in solving the SDP is 3/4. >>> from toqito.state_opt import optimal_clone >>> from toqito.states import basis >>> import numpy as np >>> e_0, e_1 = basis(2, 0), basis(2, 1) >>> e_p = (e_0 + e_1) / np.sqrt(2) >>> e_m = (e_0 - e_1) / np.sqrt(2) >>> >>> states = [e_0, e_1, e_p, e_m] >>> probs = [1 / 4, 1 / 4, 1 / 4, 1 / 4] >>> wiesner = optimal_clone(states, probs) 0.749999999967631 References ========== .. [MVW12] Abel Molina, Thomas Vidick, and John Watrous. "Optimal counterfeiting attacks and generalizations for Wiesner’s quantum money." Conference on Quantum Computation, Communication, and Cryptography. Springer, Berlin, Heidelberg, 2012. https://arxiv.org/abs/1202.4010 .. [Wies83] Stephen Wiesner "Conjugate coding." ACM Sigact News 15.1 (1983): 78-88. https://dl.acm.org/doi/pdf/10.1145/1008908.1008920 :return: The optimal probability with of counterfeiting quantum money. """ dim = len(states[0])**3 # Construct the following operator: # ___ ___ # Q = ∑_{k=1}^N p_k |ψ_k ⊗ ψ_k ⊗ ψ_k> <ψ_k ⊗ ψ_k ⊗ ψ_k| q_a = np.zeros((dim, dim)) for k, state in enumerate(states): q_a += (probs[k] * tensor(state, state, state.conj()) * tensor(state, state, state.conj()).conj().T) # The system is over: # Y_1 ⊗ Z_1 ⊗ X_1, ... , Y_n ⊗ Z_n ⊗ X_n. num_spaces = 3 # In the event of more than a single repetition, one needs to apply a # permutation operator to the variables in the SDP to properly align # the spaces. if num_reps == 1: pperm = np.array([1]) else: # The permutation vector `perm` contains elements of the # sequence from: https://oeis.org/A023123 q_a = tensor(q_a, num_reps) perm = [] for i in range(1, num_spaces + 1): perm.append(i) var = i for j in range(1, num_reps): perm.append(var + num_spaces * j) pperm = permutation_operator(2, perm) if strategy: return primal_problem(q_a, pperm, num_reps) return dual_problem(q_a, pperm, num_reps)