def test_lifted_state_operator_backwards_qubits(): xz_state = plusX(5) * minusZ(6) plus = np.array([1, 1]) / np.sqrt(2) plus = plus[:, np.newaxis] proj_plus = plus @ plus.conj().T assert proj_plus.shape == (2, 2) one = np.array([0, 1]) one = one[:, np.newaxis] proj_one = one @ one.conj().T assert proj_one.shape == (2, 2) np.testing.assert_allclose(np.kron(proj_plus, proj_one), lifted_state_operator(xz_state, qubits=[6, 5]))
def _extract_from_results(results: List[ExperimentResult], qubits: List[int]): """ Construct the matrix A such that the probabilities p_ij of outcomes n_ij given an estimate E can be cast in a vectorized form. Specifically:: p = vec(p_ij) = A x vec(E) This yields convenient vectorized calculations of the cost and its gradient, in terms of A, n, and E. """ A = [] n = [] grand_total_shots = 0 for result in results: in_state_matrix = lifted_state_operator(result.setting.in_state, qubits=qubits) operator = lifted_pauli(result.setting.out_operator, qubits=qubits) proj_plus = (np.eye(2**len(qubits)) + operator) / 2 proj_minus = (np.eye(2**len(qubits)) - operator) / 2 # Constructing A per eq. (22) # TODO: figure out if we can avoid re-splitting into Pi+ and Pi- counts A += [ # vec() turns into a column vector; transpose to a row vector; index into the # 1 row to avoid an extra tensor dimension when we call np.asarray(A). vec(np.kron(in_state_matrix, proj_plus.T)).T[0], vec(np.kron(in_state_matrix, proj_minus.T)).T[0], ] expected_plus_ones = (1 + result.expectation) / 2 n += [ result.total_counts * expected_plus_ones, result.total_counts * (1 - expected_plus_ones) ] grand_total_shots += result.total_counts n_qubits = len(qubits) dimension = 2**n_qubits A = np.asarray(A) / dimension**2 n = np.asarray(n)[:, np.newaxis] / grand_total_shots return A, n