Pauli exponentials¶
Download Notebook - pauli_exponential.ipynb
A Pauli exponential rotates a register about a Pauli string \(P\), such as \(Z_0X_1Y_2\).
angle(theta)uses half-turns, so the implemented unitary is
These rotations form the steps of Trotterized Hamiltonian simulation. For \(H=\sum_j a_jP_j\), the library’s time convention gives
This notebook builds a rotation, checks its matrix, and substitutes a different \(R_z\) implementation.
Run from a source checkout with development dependencies installed. The repository default is little endian.
1. Build the rotation¶
Describe the operator with
zqp.String; usezqp.RealTermSumfor a weighted Hamiltonian.pauli_expbuilds a guppy function for that string. Its angle is supplied when the function is called.The circuit changes to the Z basis, collects parity with a CX ladder, applies \(R_z\), then reverses the ladder and basis changes.
Here we choose
CXLadderLinear. Omitting the optional arguments usesCXLadderLogandrz.
n_state_qubits = 3
pauli_string = zqp.String.from_str("Z0 X1 Y2", n_state_qubits)
cx_ladder_method = CXLadderLinear
pauli_gadget = pauli_exp(pauli_string, n_state_qubits, cx_ladder_method, rz)
theta = 0.7
@guppy
def rotation(qreg: array[qubit, n_state_qubits]) -> None:
pauli_gadget(qreg, angle(theta))
2. Check the full unitary¶
get_unitarysimulates each computational-basis input and collects the output columns.Compare with the matrix exponential above, allowing an overall global phase. This checks relative phases as well as probabilities.
guppy_u = get_unitary(rotation, n_state_qubits)
pauli_mat = np.asarray(pauli_string.to_sparse_matrix(True).todense())
u_mat = expm(-0.5j * np.pi * theta * pauli_mat)
assert_allclose_ignorephase(u_mat, guppy_u)
print(f"All {2**n_state_qubits} basis inputs match the Pauli exponential, up to global phase.")
All 8 basis inputs match the Pauli exponential, up to global phase.
3. Swap in a repeat-until-success rotation¶
rz_methodaccepts another function with the same qubit-and-angle interface. The surrounding Pauli gadget stays unchanged.repeat_until_success_rzuses a resource qubit and measurement feedback. This demonstration usesdummy_theta_resource_state, which prepares its resource with a direct rotation; it does not demonstrate a rotation-synthesis resource saving.Replay three failures followed by success and verify the output for \(|+\rangle^{\otimes3}\):
from guppyalgos.primitives.rotations import repeat_until_success_rz, dummy_theta_resource_state
from guppyalgos.utils import qarray, transversal
from guppylang.std.debug import state_output
from guppylang.std.quantum import discard_array, h
from selene_sim import QuantumReplay, Quest
rus_rz = repeat_until_success_rz(dummy_theta_resource_state)
rus_gadget = pauli_exp(pauli_string, n_state_qubits, cx_ladder_method, rus_rz)
theta = 0.7
@guppy
def main() -> None:
qreg = qarray(n_state_qubits)
transversal(h, qreg)
rus_gadget(qreg, angle(theta))
state_output("result_state", qreg)
discard_array(qreg)
n_repeats = 3
# fail 3 times and then succeed
desired_rus_measurements = [[False] * n_repeats + [True]]
rus_replay_sim = QuantumReplay(simulator=Quest(), measurements=desired_rus_measurements)
em_result = (
main.emulator(n_state_qubits+1).with_simulator(rus_replay_sim).with_shots(1).run()
)
expected_state = u_mat @ (np.ones(2**n_state_qubits) / np.sqrt(2**n_state_qubits))
for shot_result in em_result.results:
actual_state = Quest.extract_states_dict(shot_result)["result_state"].get_single_state()
assert_allclose_ignorephase(expected_state, actual_state)
print("Three forced failures followed by success: output state matches the target.")
Three forced failures followed by success: output state matches the target.
Continue with the measurement-based Pauli exponential to replace the ladder construction.
The phase-estimation demo shows how controlled Trotter steps built from Pauli exponentials estimate an energy.