Positive Register-Incremented and Phase-Gradient Rotations¶
Download Notebook - register_incremented_rotation.ipynb
This notebook demonstrates the positive-angle convention used by the RotationRegisterIncremented and RotationPhaseGradient Guppy structs for a fixed computational-basis data register.
from typing import no_type_check
import numpy as np
import pandas as pd
from guppylang import comptime, guppy
from guppylang.std.debug import state_result
from guppylang.std.quantum import discard, discard_array, qubit, x
from selene_sim import Quest
from guppyalgos.primitives.rotations import (
RotationAxisY,
RotationPhaseGradient,
RotationRegisterIncremented,
)
from guppyalgos.primitives.state_preparation.phase_gradient import Convention, phase_gradient
from guppyalgos.utils import qarray
from guppyalgos.tests.helpers import assert_allclose_ignorephase
For a little-endian \(n\)-qubit data register with bits \(b_i \in \{0, 1\}\), the positive register-incremented per-bit controlled angles are
and the total positive angle applied to the ancilla is
This is the unscaled full-range fixed-point convention shared with phase-gradient kickback.
bits = [True, False, True, False, True]
n_phase_qubits = len(bits)
@guppy
@no_type_check
def main() -> None:
idx = comptime(bits)
phase_qreg = qarray(comptime(n_phase_qubits))
rotation_target = qubit()
for bit in range(comptime(n_phase_qubits)):
if idx[bit]:
x(phase_qreg[bit])
rotation = RotationRegisterIncremented[
comptime(n_phase_qubits), RotationAxisY
](RotationAxisY())
rotation.compose(phase_qreg, rotation_target)
state_result("ancilla", rotation_target)
discard_array(phase_qreg)
discard(rotation_target)
res = main.emulator(n_qubits=n_phase_qubits + 1).run()
states = Quest.extract_states_dict(res.results[0].entries)
ancilla_state = states["ancilla"].get_single_state()
per_bit = [2.0 ** (i + 1 - n_phase_qubits) for i in range(n_phase_qubits)]
contrib = [float(per_bit[i]) if bit else 0.0 for i, bit in enumerate(bits)]
pd.DataFrame(
{
"i": list(range(n_phase_qubits)),
"bit b_i": bits,
"theta_i = 2^(i+1-n)": per_bit,
"contribution b_i * theta_i": contrib,
}
)
expected_theta = sum(contrib)
print(contrib)
phase = np.pi * expected_theta / 2.0
expected_state = np.array([np.cos(phase), np.sin(phase)], dtype=np.complex128)
print(f"bits={bits}, axis=Y")
print(f"expected theta_total = {expected_theta}")
print("simulated ancilla state:", ancilla_state)
print("expected ancilla state :", expected_state)
assert_allclose_ignorephase(ancilla_state, expected_state)
print("Passed: ancilla state matches expected axis rotation up to global phase.")
A positive rotation can also be synthesized by the RotationPhaseGradient struct using a phase-gradient register prepared before initialization. It applies X to the target before and after the controlled Gidney adder. This makes addition happen on the target’s original \(|0\rangle\) branch, so the phase-gradient eigenphase \(\exp(-2\pi i x / 2^d)\) appears on \(|0\rangle\) rather than \(|1\rangle\). Up to global phase, this is a positive rotation with half-turn angle theta = 2 * x / 2**d for the standard-convention integer \(x\) encoded by the same little-endian bits.
phase_gradient_prep = phase_gradient(
n_phase_qubits,
convention=Convention.Standard,
)
@guppy
@no_type_check
def phase_gradient_main() -> None:
idx = comptime(bits)
phase_qreg = qarray(comptime(n_phase_qubits))
phase_gradient_state = qarray(comptime(n_phase_qubits))
rotation_target = qubit()
for bit in range(comptime(n_phase_qubits)):
if idx[bit]:
x(phase_qreg[bit])
phase_gradient_prep(phase_gradient_state)
rotation = RotationPhaseGradient(phase_gradient_state, RotationAxisY())
rotation.compose(phase_qreg, rotation_target)
state_result("phase_gradient_ancilla", rotation_target)
discard_array(phase_qreg)
discard_array(rotation.phase_gradient)
discard(rotation_target)
phase_gradient_res = phase_gradient_main.emulator(n_qubits=(3 * n_phase_qubits) + 1).run()
phase_gradient_states = Quest.extract_states_dict(phase_gradient_res.results[0].entries)
phase_gradient_ancilla_state = phase_gradient_states["phase_gradient_ancilla"].get_single_state()
encoded_integer = sum((2**i) for i, bit in enumerate(bits) if bit)
expected_phase_gradient_theta = 2.0 * encoded_integer / (2**n_phase_qubits)
phase_gradient_phase = np.pi * expected_phase_gradient_theta / 2.0
expected_phase_gradient_state = np.array(
[np.cos(phase_gradient_phase), np.sin(phase_gradient_phase)],
dtype=np.complex128,
)
print(f"phase-gradient theta_total = {expected_phase_gradient_theta}")
print("phase-gradient ancilla state:", phase_gradient_ancilla_state)
print("expected ancilla state :", expected_phase_gradient_state)
assert_allclose_ignorephase(
phase_gradient_ancilla_state,
expected_phase_gradient_state,
)
print("Passed: phase-gradient kickback matches the positive axis rotation convention.")