def test_statevector() -> None: c = circuit_gen() b = ProjectQBackend() state = b.get_state(c) assert np.allclose( state, [math.sqrt(0.5), 0, 0, math.sqrt(0.5)], atol=1e-10) c.add_phase(0.5) state1 = b.get_state(c) assert np.allclose(state1, state * 1j, atol=1e-10)
def test_default_pass() -> None: b = ProjectQBackend() for ol in range(3): comp_pass = b.default_compilation_pass(ol) c = Circuit(3, 3) c.H(0) c.CX(0, 1) c.CSWAP(1, 0, 2) c.ZZPhase(0.84, 2, 0) comp_pass.apply(c) for pred in b.required_predicates: assert pred.verify(c)
def test_ilo() -> None: b = ProjectQBackend() c = Circuit(2) c.X(1) assert np.allclose(b.get_state(c), np.asarray([0, 1, 0, 0], dtype=complex), atol=1e-10) assert np.allclose( b.get_state(c, basis=BasisOrder.dlo), np.asarray([0, 0, 1, 0], dtype=complex), atol=1e-10, )
def test_operator() -> None: c = circuit_gen() b = ProjectQBackend() zz = QubitPauliOperator( {QubitPauliString([Qubit(0), Qubit(1)], [Pauli.Z, Pauli.Z]): 1.0}) assert np.isclose(get_operator_expectation_value(c, zz, b), complex(1.0)) c.X(0) assert np.isclose(get_operator_expectation_value(c, zz, b), complex(-1.0))
def test_pauli() -> None: c = Circuit(2) c.Rz(0.5, 0) b = ProjectQBackend() zi = QubitPauliString({Qubit(0): Pauli.Z}) assert np.isclose(get_pauli_expectation_value(c, zi, b), complex(1)) c.X(0) assert np.isclose(get_pauli_expectation_value(c, zi, b), complex(-1))
def test_resulthandle() -> None: c = Circuit(4, 4).H(0).CX(0, 2) b = ProjectQBackend() handles = b.process_circuits([c, c.copy()]) ids = [han[0] for han in handles] assert all(isinstance(idval, str) for idval in ids) assert ids[0] != ids[1] assert len(b.get_result(handles[0]).get_state()) == (1 << 4) assert b.circuit_status(handles[1]).status == StatusEnum.COMPLETED with pytest.raises(TypeError) as errorinfo: _ = b.get_result(ResultHandle("43", 5)) assert "ResultHandle('43', 5) does not match expected identifier types" in str( errorinfo.value) wronghandle = ResultHandle("asdf") with pytest.raises(CircuitNotRunError) as errorinfocirc: _ = b.get_result(wronghandle) assert "Circuit corresponding to {0!r} ".format( wronghandle) + "has not been run by this backend instance." in str( errorinfocirc.value)
def test_swaps_basisorder() -> None: # Check that implicit swaps can be corrected irrespective of BasisOrder b = ProjectQBackend() c = Circuit(4) c.X(0) c.CX(0, 1) c.CX(1, 0) CliffordSimp(True).apply(c) assert c.n_gates_of_type(OpType.CX) == 1 b.compile_circuit(c) s_ilo = b.get_state(c, basis=BasisOrder.ilo) s_dlo = b.get_state(c, basis=BasisOrder.dlo) correct_ilo = np.zeros((16, )) correct_ilo[4] = 1.0 assert np.allclose(s_ilo, correct_ilo) correct_dlo = np.zeros((16, )) correct_dlo[2] = 1.0 assert np.allclose(s_dlo, correct_dlo)
def test_shots_bits_edgecases(n_shots, n_bits) -> None: projectq_backend = ProjectQBackend() c = Circuit(n_bits, n_bits) # TODO TKET-813 add more shot based backends and move to integration tests h = projectq_backend.process_circuit(c, n_shots) res = projectq_backend.get_result(h) correct_shots = np.zeros((n_shots, n_bits), dtype=int) correct_shape = (n_shots, n_bits) correct_counts = Counter({(0, ) * n_bits: n_shots}) # BackendResult assert np.array_equal(res.get_shots(), correct_shots) assert res.get_shots().shape == correct_shape assert res.get_counts() == correct_counts # Direct assert np.array_equal(projectq_backend.get_shots(c, n_shots), correct_shots) assert projectq_backend.get_shots(c, n_shots).shape == correct_shape assert projectq_backend.get_counts(c, n_shots) == correct_counts
0.17141282644776884 * QubitOperator("Z0") + 0.16868898170361213 * QubitOperator("Z0 Z1") + 0.12062523483390425 * QubitOperator("Z0 Z2") + 0.16592785033770352 * QubitOperator("Z0 Z3") + 0.17141282644776884 * QubitOperator("Z1") + 0.16592785033770352 * QubitOperator("Z1 Z2") + 0.12062523483390425 * QubitOperator("Z1 Z3") + -0.22343153690813597 * QubitOperator("Z2") + 0.17441287612261608 * QubitOperator("Z2 Z3") + -0.22343153690813597 * QubitOperator("Z3")) # We can simulate this exactly using a statevector simulator like ProjectQ. This has a built-in method for fast calculations of expectation values that works well for small examples like this. from pytket.extensions.projectq import ProjectQBackend backend = ProjectQBackend() ideal_energy = backend.get_operator_expectation_value( ansatz, QubitPauliOperator.from_OpenFermion(hamiltonian)) print(ideal_energy) # Ideally the state generated by this ansatz will only span the computational basis states with exactly two of the four qubits in state $\lvert 1 \rangle$. This is because these basis states correspond to two electrons being present in the molecule. # # This ansatz is a hardware-efficient model that is designed to explore a large portion of the Hilbert space with relatively few entangling gates. Unfortunately, with this much freedom, it will regularly generate states that have no physical interpretation such as states spanning multiple basis states corresponding to different numbers of electrons in the system (which we assume is fixed and conserved). # # We can mitigate this by using a syndrome qubit that calculates the parity of the other qubits. Post-selecting this syndrome with $\langle 0 \rvert$ will project the remaining state onto the subspace of basis states with even parity, increasing the likelihood the observed state will be a physically admissible state. # # Even if the ansatz parameters are tuned to give a physical state, real devices have noise and imperfect gates, so in practice we may also measure bad states with a small probability. If this syndrome qubit is measured as 1, it means an error has definitely occurred, so we should discard the shot. syn = Qubit("synq", 0) syn_res = Bit("synres", 0) ansatz.add_qubit(syn)