Source code for qarp.blocks._primitives.order_finding_block
"""Measurement-free quantum order-finding circuit."""
from math import gcd
from typing import List, Optional
from .._block import CompositeBlockBase, ControlledBlock, SimpleBlock
from .hn_block import HnBlock
from .modular_multiplication_block import ModularMultiplicationBlock
from .qft_block import QFTBlock
[docs]
class OrderFindingBlock(CompositeBlockBase):
r"""Reference circuit for finding the multiplicative order of a base.
The LSB-indexed register layout is ``counting`` followed by ``work``. The
circuit prepares the work register in ``|1>``, computes ``a**x mod N`` by
controlled modular multiplications, and applies an inverse QFT to the
counting register. Measurement is deliberately left to a sampler.
Args:
base: Integer satisfying ``1 < base < modulus`` and coprime to it.
modulus: Integer modulus greater than one.
n_counting_qubits: Counting-register width. Defaults to twice the work
width and cannot be smaller than that value.
target_qubits: Qubits occupied when embedded in a parent block.
name: Block name.
Note:
Modular arithmetic is synthesized by
:class:`ModularMultiplicationBlock` and therefore has the same
six-work-qubit reference limit and exponential gate cost.
"""
def __init__(
self,
base: int,
modulus: int,
n_counting_qubits: Optional[int] = None,
target_qubits: Optional[List[int]] = None,
name: str = "OrderFinding",
) -> None:
if isinstance(modulus, bool) or not isinstance(modulus, int):
raise TypeError("modulus must be an integer")
if modulus <= 1:
raise ValueError("modulus must be greater than one")
if isinstance(base, bool) or not isinstance(base, int):
raise TypeError("base must be an integer")
if not 1 < base < modulus:
raise ValueError("base must satisfy 1 < base < modulus")
if gcd(base, modulus) != 1:
raise ValueError("base and modulus must be coprime")
n_work_qubits = (modulus - 1).bit_length()
if n_work_qubits > ModularMultiplicationBlock.MAX_REFERENCE_WORK_QUBITS:
raise ValueError(
"OrderFindingBlock's exact reference arithmetic supports at most "
f"{ModularMultiplicationBlock.MAX_REFERENCE_WORK_QUBITS} work qubits; "
f"modulus={modulus} requires {n_work_qubits}."
)
if n_counting_qubits is None:
n_counting_qubits = 2 * n_work_qubits
elif isinstance(n_counting_qubits, bool) or not isinstance(n_counting_qubits, int):
raise TypeError("n_counting_qubits must be an integer")
if n_counting_qubits < 2 * n_work_qubits:
raise ValueError(
"n_counting_qubits must be at least twice the work-register width "
f"({2 * n_work_qubits})"
)
self.base = base
self.modulus = modulus
self.n_counting_qubits = n_counting_qubits
self.n_work_qubits = n_work_qubits
super().__init__(
n_qubits=n_counting_qubits + n_work_qubits,
target_qubits=target_qubits,
name=name,
)
@property
def counting_qubits(self) -> list[int]:
"""Local counting-register qubits, ordered least-significant first."""
return list(range(self.n_counting_qubits))
@property
def work_qubits(self) -> list[int]:
"""Local work-register qubits, ordered least-significant first."""
return list(range(self.n_counting_qubits, self.n_qubits))
[docs]
def build_vanilla(self) -> None:
self.add_wired_child(
HnBlock(
self.n_counting_qubits,
target_qubits=self.counting_qubits,
name="CountingH",
)
)
prepare_one = SimpleBlock(
self.n_work_qubits,
target_qubits=self.work_qubits,
name="PrepareWorkOne",
)
prepare_one.x(0)
self.add_wired_child(prepare_one)
self.add_child(self._modular_exponentiation_block())
inverse_qft = QFTBlock(self.n_counting_qubits).dagger().build()
inverse_qft.target_qubits = self.counting_qubits
self.add_child(inverse_qft)
def _modular_exponentiation_block(self) -> CompositeBlockBase:
"""Build ``|x>|w> -> |x>|a**x*w mod N>`` in the local register frame."""
modular_exponentiation = CompositeBlockBase(
self.n_qubits,
name="ModularExponentiation",
)
for exponent_bit, control_qubit in enumerate(self.counting_qubits):
multiplier = pow(self.base, 2**exponent_bit, self.modulus)
if multiplier == 1:
continue
multiplication = ModularMultiplicationBlock(multiplier, self.modulus).build()
controlled = ControlledBlock(
multiplication,
num_controls=1,
ctrl_state=[True],
name=f"C-M({multiplier})@q{control_qubit}",
).build()
controlled.target_qubits = [control_qubit, *self.work_qubits]
modular_exponentiation.add_child(controlled)
return modular_exponentiation.build()