Algorithm building blocks for quantum circuits.
Overview¶
| Function | Description |
|---|---|
cost_layer | Apply the cost (phase separation) layer. |
cx_entangling_layer | Apply CX entangling layer with linear connectivity. |
cz_entangling_layer | Apply CZ entangling layer with linear connectivity. |
fqaoa_layers | Apply p layers of cost + mixer. |
fqaoa_state | Generate complete FQAOA state. |
givens_rotation | Apply a single Givens rotation between qubits i and j. |
givens_rotations | Apply a sequence of Givens rotations. |
hopping_gate | Apply the fermionic hopping gate between qubits i and j. |
hubo_ising_cost | Apply the full cost layer including higher-order terms. |
hubo_qaoa_layers | Apply p layers of the HUBO QAOA circuit (cost + mixer). |
hubo_qaoa_state | Generate HUBO QAOA state. |
initial_occupations | Apply X gates to the first num_fermions qubits. |
ising_cost | Apply the Ising cost layer for quadratic interactions. |
mixer_layer | Apply the fermionic mixer layer (even-odd-boundary hopping). |
qaoa_layers | Apply p layers of the QAOA circuit (cost + mixer). |
qaoa_state | Generate QAOA State for Ising model. |
rx_layer | Apply RX rotation to each qubit. |
ry_layer | Apply RY rotation to each qubit. |
rz_layer | Apply RZ rotation to each qubit. |
superposition_vector | Create a uniform superposition state by applying Hadamard to all qubits. |
trotterized_time_evolution | Apply Suzuki-Trotter time evolution exp(-i gamma H) to q. |
x_mixer | Apply the X-mixer layer. |
Functions¶
cost_layer [source]¶
def cost_layer(
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply the cost (phase separation) layer.
cx_entangling_layer [source]¶
def cx_entangling_layer(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]Apply CX entangling layer with linear connectivity.
Applies CX gates between consecutive qubits: (0,1), (1,2), ..., (n-2,n-1).
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Qubit vector after entanglement
cz_entangling_layer [source]¶
def cz_entangling_layer(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]Apply CZ entangling layer with linear connectivity.
Applies CZ gates between consecutive qubits: (0,1), (1,2), ..., (n-2,n-1).
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after entanglement
fqaoa_layers [source]¶
def fqaoa_layers(
q: qmc.Vector[qmc.Qubit],
betas: qmc.Vector[qmc.Float],
gammas: qmc.Vector[qmc.Float],
p: qmc.UInt,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
hopping: qmc.Float,
num_qubits: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply p layers of cost + mixer.
fqaoa_state [source]¶
def fqaoa_state(
p: qmc.UInt,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
num_qubits: qmc.UInt,
num_fermions: qmc.UInt,
givens_ij: qmc.Matrix[qmc.UInt],
givens_theta: qmc.Vector[qmc.Float],
hopping: qmc.Float,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate complete FQAOA state.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of FQAOA layers. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of Ising model. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of Ising model. |
num_qubits | qmc.UInt | Number of qubits. |
num_fermions | qmc.UInt | Number of fermions for initial state. |
givens_ij | qmc.Matrix[qmc.UInt] | Matrix of shape (N, 2) with qubit index pairs for Givens rotations. |
givens_theta | qmc.Vector[qmc.Float] | Vector of length N with Givens rotation angles. |
hopping | qmc.Float | Hopping integral for the mixer. |
gammas | qmc.Vector[qmc.Float] | Vector of gamma parameters. |
betas | qmc.Vector[qmc.Float] | Vector of beta parameters. |
Returns:
qmc.Vector[qmc.Qubit] — FQAOA state vector.
givens_rotation [source]¶
def givens_rotation(
q: qmc.Vector[qmc.Qubit],
i: qmc.UInt,
j: qmc.UInt,
theta: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply a single Givens rotation between qubits i and j.
The controlled-RY factory is constructed inside the function rather
than at module level so importing fqaoa does not trigger
eager wrapper synthesis (compile/exec + tracing) for every install.
The synthesized wrapper is cached per-callable inside
qmc.controlled, so the only real cost happens on the first
Givens rotation; subsequent calls hit the cache.
givens_rotations [source]¶
def givens_rotations(
q: qmc.Vector[qmc.Qubit],
givens_ij: qmc.Matrix[qmc.UInt],
givens_theta: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply a sequence of Givens rotations.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register. |
givens_ij | qmc.Matrix[qmc.UInt] | Matrix of shape (N, 2) where each row [i, j] contains the qubit indices for one Givens rotation. |
givens_theta | qmc.Vector[qmc.Float] | Vector of length N with the rotation angles. |
hopping_gate [source]¶
def hopping_gate(
q: qmc.Vector[qmc.Qubit],
i: qmc.UInt,
j: qmc.UInt,
beta: qmc.Float,
hopping: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the fermionic hopping gate between qubits i and j.
hubo_ising_cost [source]¶
def hubo_ising_cost(
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the full cost layer including higher-order terms.
Applies the standard quadratic Ising cost circuit, then decomposes each higher-order term into phase gadgets.
Parameters:
| Name | Type | Description |
|---|---|---|
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients J_{ij} of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients h_i of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gamma | qmc.Float | Variational parameter for the cost layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
hubo_qaoa_layers [source]¶
def hubo_qaoa_layers(
p_val: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
q: qmc.Vector[qmc.Qubit],
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply p layers of the HUBO QAOA circuit (cost + mixer).
Each layer applies the HUBO cost circuit (quadratic + higher-order terms) followed by the X-mixer circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p_val | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register after all layers.
hubo_qaoa_state [source]¶
def hubo_qaoa_state(
p_val: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
n: qmc.UInt,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate HUBO QAOA state.
Creates a uniform superposition and applies p layers of the HUBO QAOA circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p_val | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
n | qmc.UInt | Number of qubits. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: HUBO QAOA state vector.
initial_occupations [source]¶
def initial_occupations(q: qmc.Vector[qmc.Qubit], num_fermions: qmc.UInt) -> qmc.Vector[qmc.Qubit]Apply X gates to the first num_fermions qubits.
ising_cost [source]¶
def ising_cost(
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the Ising cost layer for quadratic interactions.
Applies RZZ gates for quadratic terms and RZ gates for linear terms, each scaled by the variational parameter gamma.
Parameters:
| Name | Type | Description |
|---|---|---|
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients J_{ij} of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients h_i of the Ising model. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gamma | qmc.Float | Variational parameter for the cost layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
mixer_layer [source]¶
def mixer_layer(
q: qmc.Vector[qmc.Qubit],
beta: qmc.Float,
hopping: qmc.Float,
num_qubits: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply the fermionic mixer layer (even-odd-boundary hopping).
qaoa_layers [source]¶
def qaoa_layers(
p: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
q: qmc.Vector[qmc.Qubit],
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply p layers of the QAOA circuit (cost + mixer).
Each layer applies the Ising cost circuit followed by the X-mixer circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register after all layers.
qaoa_state [source]¶
def qaoa_state(
p: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
n: qmc.UInt,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate QAOA State for Ising model.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
n | qmc.UInt | Number of qubits. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: QAOA state vector.
rx_layer [source]¶
def rx_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RX rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
ry_layer [source]¶
def ry_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RY rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
rz_layer [source]¶
def rz_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RZ rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
superposition_vector [source]¶
def superposition_vector(n: qmc.UInt) -> qmc.Vector[qmc.Qubit]Create a uniform superposition state by applying Hadamard to all qubits.
Parameters:
| Name | Type | Description |
|---|---|---|
n | qmc.UInt | Number of qubits. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Qubit register in the |+>^n state.
trotterized_time_evolution [source]¶
def trotterized_time_evolution(
q: qmc.Vector[qmc.Qubit],
hamiltonian: qmc.Vector[qmc.Observable] | Sequence[Hamiltonian],
order: int | qmc.UInt,
gamma: float | qmc.Float,
step: int | qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply Suzuki-Trotter time evolution exp(-i gamma H) to q.
H = sum_k hamiltonian[k]. The evolution is split into step
Trotter slices of size dt = gamma / step; each slice applies
the order-th Suzuki-Trotter formula via :func:_trotter_evolve.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register handle. |
hamiltonian | qmc.Vector[qmc.Observable] | Sequence[Hamiltonian] | qmc.Vector[qmc.Observable] when called from a @qkernel (the handle type for a vector of Hamiltonians), or a Python list of qamomile.observable.Hamiltonian objects. At least two sub-Hamiltonian terms are required. |
order | int | qmc.UInt | Approximation order — 1 or a positive even integer (2, 4, 6, …). Must be a compile-time constant. |
gamma | float | qmc.Float | Total evolution time. |
step | int | qmc.UInt | Number of Trotter steps. |
Returns:
qmc.Vector[qmc.Qubit] — The evolved qubit register.
Raises:
ValueError— Ifhamiltonianhas fewer than two terms, iforderis abool, or iforderis not1or a positive even integer. When these arguments are still symbolic (e.g. the enclosing kernel has not been re-traced with bindings yet) validation silently defers.
x_mixer [source]¶
def x_mixer(q: qmc.Vector[qmc.Qubit], beta: qmc.Float) -> qmc.Vector[qmc.Qubit]Apply the X-mixer layer.
Applies RX(2*beta) to every qubit in the register.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register. |
beta | qmc.Float | Variational parameter for the mixer layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
qamomile.circuit.algorithm.basic¶
Basic building blocks for variational quantum circuits.
This module provides fundamental rotation layers and entanglement layers that can be composed to build variational ansatze.
Overview¶
| Function | Description |
|---|---|
cx_entangling_layer | Apply CX entangling layer with linear connectivity. |
cz_entangling_layer | Apply CZ entangling layer with linear connectivity. |
phase_gadget | Apply exp(-i * angle/2 * Z_{i0} Z_{i1} ... Z_{ik-1}). |
rx_layer | Apply RX rotation to each qubit. |
ry_layer | Apply RY rotation to each qubit. |
rz_layer | Apply RZ rotation to each qubit. |
superposition_vector | Create a uniform superposition state by applying Hadamard to all qubits. |
Functions¶
cx_entangling_layer [source]¶
def cx_entangling_layer(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]Apply CX entangling layer with linear connectivity.
Applies CX gates between consecutive qubits: (0,1), (1,2), ..., (n-2,n-1).
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Qubit vector after entanglement
cz_entangling_layer [source]¶
def cz_entangling_layer(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]Apply CZ entangling layer with linear connectivity.
Applies CZ gates between consecutive qubits: (0,1), (1,2), ..., (n-2,n-1).
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after entanglement
phase_gadget [source]¶
def phase_gadget(
q: qmc.Vector[qmc.Qubit],
indices: qmc.Vector[qmc.UInt],
angle: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply exp(-i * angle/2 * Z_{i0} Z_{i1} ... Z_{ik-1}).
Decomposes a k-body Z-rotation into CX + RZ primitives.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register. |
indices | qmc.Vector[qmc.UInt] | Qubit indices for the interaction term. Must be non-empty. |
angle | qmc.Float | Rotation angle in radians. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
rx_layer [source]¶
def rx_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RX rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
ry_layer [source]¶
def ry_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RY rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
rz_layer [source]¶
def rz_layer(
q: qmc.Vector[qmc.Qubit],
thetas: qmc.Vector[qmc.Float],
offset: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply RZ rotation to each qubit.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit vector |
thetas | qmc.Vector[qmc.Float] | Parameter vector |
offset | qmc.UInt | Starting index in thetas (consumes q.shape[0] parameters) |
Returns:
qmc.Vector[qmc.Qubit] — Qubit vector after rotations
superposition_vector [source]¶
def superposition_vector(n: qmc.UInt) -> qmc.Vector[qmc.Qubit]Create a uniform superposition state by applying Hadamard to all qubits.
Parameters:
| Name | Type | Description |
|---|---|---|
n | qmc.UInt | Number of qubits. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Qubit register in the |+>^n state.
qamomile.circuit.algorithm.fqaoa¶
FQAOA (Fermionic QAOA) circuit building blocks.
This module provides the quantum circuit components for the Fermionic Quantum Approximate Optimization Algorithm (FQAOA), including Givens rotations for initial state preparation, hopping gates for the fermionic mixer, and cost layer construction.
All functions are decorated with @qm_c.qkernel and use Handle-typed
parameters so they can be composed inside other @qkernel functions.
Overview¶
| Function | Description |
|---|---|
cost_layer | Apply the cost (phase separation) layer. |
fqaoa_layers | Apply p layers of cost + mixer. |
fqaoa_state | Generate complete FQAOA state. |
givens_rotation | Apply a single Givens rotation between qubits i and j. |
givens_rotations | Apply a sequence of Givens rotations. |
hopping_gate | Apply the fermionic hopping gate between qubits i and j. |
initial_occupations | Apply X gates to the first num_fermions qubits. |
mixer_layer | Apply the fermionic mixer layer (even-odd-boundary hopping). |
Functions¶
cost_layer [source]¶
def cost_layer(
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply the cost (phase separation) layer.
fqaoa_layers [source]¶
def fqaoa_layers(
q: qmc.Vector[qmc.Qubit],
betas: qmc.Vector[qmc.Float],
gammas: qmc.Vector[qmc.Float],
p: qmc.UInt,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
hopping: qmc.Float,
num_qubits: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply p layers of cost + mixer.
fqaoa_state [source]¶
def fqaoa_state(
p: qmc.UInt,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
num_qubits: qmc.UInt,
num_fermions: qmc.UInt,
givens_ij: qmc.Matrix[qmc.UInt],
givens_theta: qmc.Vector[qmc.Float],
hopping: qmc.Float,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate complete FQAOA state.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of FQAOA layers. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of Ising model. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of Ising model. |
num_qubits | qmc.UInt | Number of qubits. |
num_fermions | qmc.UInt | Number of fermions for initial state. |
givens_ij | qmc.Matrix[qmc.UInt] | Matrix of shape (N, 2) with qubit index pairs for Givens rotations. |
givens_theta | qmc.Vector[qmc.Float] | Vector of length N with Givens rotation angles. |
hopping | qmc.Float | Hopping integral for the mixer. |
gammas | qmc.Vector[qmc.Float] | Vector of gamma parameters. |
betas | qmc.Vector[qmc.Float] | Vector of beta parameters. |
Returns:
qmc.Vector[qmc.Qubit] — FQAOA state vector.
givens_rotation [source]¶
def givens_rotation(
q: qmc.Vector[qmc.Qubit],
i: qmc.UInt,
j: qmc.UInt,
theta: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply a single Givens rotation between qubits i and j.
The controlled-RY factory is constructed inside the function rather
than at module level so importing fqaoa does not trigger
eager wrapper synthesis (compile/exec + tracing) for every install.
The synthesized wrapper is cached per-callable inside
qmc.controlled, so the only real cost happens on the first
Givens rotation; subsequent calls hit the cache.
givens_rotations [source]¶
def givens_rotations(
q: qmc.Vector[qmc.Qubit],
givens_ij: qmc.Matrix[qmc.UInt],
givens_theta: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply a sequence of Givens rotations.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register. |
givens_ij | qmc.Matrix[qmc.UInt] | Matrix of shape (N, 2) where each row [i, j] contains the qubit indices for one Givens rotation. |
givens_theta | qmc.Vector[qmc.Float] | Vector of length N with the rotation angles. |
hopping_gate [source]¶
def hopping_gate(
q: qmc.Vector[qmc.Qubit],
i: qmc.UInt,
j: qmc.UInt,
beta: qmc.Float,
hopping: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the fermionic hopping gate between qubits i and j.
initial_occupations [source]¶
def initial_occupations(q: qmc.Vector[qmc.Qubit], num_fermions: qmc.UInt) -> qmc.Vector[qmc.Qubit]Apply X gates to the first num_fermions qubits.
mixer_layer [source]¶
def mixer_layer(
q: qmc.Vector[qmc.Qubit],
beta: qmc.Float,
hopping: qmc.Float,
num_qubits: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply the fermionic mixer layer (even-odd-boundary hopping).
qamomile.circuit.algorithm.qaoa¶
Overview¶
| Function | Description |
|---|---|
hubo_ising_cost | Apply the full cost layer including higher-order terms. |
hubo_qaoa_layers | Apply p layers of the HUBO QAOA circuit (cost + mixer). |
hubo_qaoa_state | Generate HUBO QAOA state. |
ising_cost | Apply the Ising cost layer for quadratic interactions. |
qaoa_layers | Apply p layers of the QAOA circuit (cost + mixer). |
qaoa_state | Generate QAOA State for Ising model. |
x_mixer | Apply the X-mixer layer. |
Functions¶
hubo_ising_cost [source]¶
def hubo_ising_cost(
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the full cost layer including higher-order terms.
Applies the standard quadratic Ising cost circuit, then decomposes each higher-order term into phase gadgets.
Parameters:
| Name | Type | Description |
|---|---|---|
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients J_{ij} of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients h_i of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gamma | qmc.Float | Variational parameter for the cost layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
hubo_qaoa_layers [source]¶
def hubo_qaoa_layers(
p_val: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
q: qmc.Vector[qmc.Qubit],
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply p layers of the HUBO QAOA circuit (cost + mixer).
Each layer applies the HUBO cost circuit (quadratic + higher-order terms) followed by the X-mixer circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p_val | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register after all layers.
hubo_qaoa_state [source]¶
def hubo_qaoa_state(
p_val: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
higher: qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float],
n: qmc.UInt,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate HUBO QAOA state.
Creates a uniform superposition and applies p layers of the HUBO QAOA circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p_val | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
higher | qmc.Dict[qmc.Vector[qmc.UInt], qmc.Float] | Higher-order coefficients keyed by index vectors. |
n | qmc.UInt | Number of qubits. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: HUBO QAOA state vector.
ising_cost [source]¶
def ising_cost(
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
q: qmc.Vector[qmc.Qubit],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]Apply the Ising cost layer for quadratic interactions.
Applies RZZ gates for quadratic terms and RZ gates for linear terms, each scaled by the variational parameter gamma.
Parameters:
| Name | Type | Description |
|---|---|---|
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients J_{ij} of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients h_i of the Ising model. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gamma | qmc.Float | Variational parameter for the cost layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
qaoa_layers [source]¶
def qaoa_layers(
p: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
q: qmc.Vector[qmc.Qubit],
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Apply p layers of the QAOA circuit (cost + mixer).
Each layer applies the Ising cost circuit followed by the X-mixer circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
q | qmc.Vector[qmc.Qubit] | Qubit register. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register after all layers.
qaoa_state [source]¶
def qaoa_state(
p: qmc.UInt,
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
linear: qmc.Dict[qmc.UInt, qmc.Float],
n: qmc.UInt,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate QAOA State for Ising model.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of QAOA layers. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of the Ising model. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of the Ising model. |
n | qmc.UInt | Number of qubits. |
gammas | qmc.Vector[qmc.Float] | Cost-layer parameters, one per layer. |
betas | qmc.Vector[qmc.Float] | Mixer-layer parameters, one per layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: QAOA state vector.
x_mixer [source]¶
def x_mixer(q: qmc.Vector[qmc.Qubit], beta: qmc.Float) -> qmc.Vector[qmc.Qubit]Apply the X-mixer layer.
Applies RX(2*beta) to every qubit in the register.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register. |
beta | qmc.Float | Variational parameter for the mixer layer. |
Returns:
qmc.Vector[qmc.Qubit] — qmc.Vector[qmc.Qubit]: Updated qubit register.
qamomile.circuit.algorithm.state_preparation¶
State-preparation building blocks.
Available routines:
:class:
MottonenAmplitudeEncoding/ :func:amplitude_encoding: prepare an arbitrary real- or complex-amplitude state from using Möttönen’s Ry (and, for complex inputs, Rz) decomposition. The amplitudes are concrete and the angle computation runs at compile time (lazily — see the class docstring for the deferred-evaluation contract).:func:
amplitude_encoding_from_angles: the parametric companion. Accepts pre-computed Ry (and optional Rz) angles as either concrete sequences orVector[Float]kernel parameters, so the same compiled circuit can be re-bound to different amplitude vectors at runtime (e.g. inside a hybrid optimisation loop).
The classical Möttönen angle precomputation
(compute_mottonen_amplitude_encoding_ry_angles /
compute_mottonen_amplitude_encoding_rz_angles) lives in
:mod:qamomile.linalg. Import them from there directly when you need
to feed pre-computed angles into amplitude_encoding_from_angles::
from qamomile.linalg import (
compute_mottonen_amplitude_encoding_ry_angles,
compute_mottonen_amplitude_encoding_rz_angles,
)Overview¶
| Function | Description |
|---|---|
amplitude_encoding | Apply Möttönen amplitude encoding to qubits in place. |
amplitude_encoding_from_angles | Apply Möttönen amplitude encoding from pre-computed Ry / Rz angles. |
| Class | Description |
|---|---|
MottonenAmplitudeEncoding | Möttönen amplitude encoding for normalised real or complex vectors. |
Functions¶
amplitude_encoding [source]¶
def amplitude_encoding(
qubits: Vector[Qubit],
amplitudes: Sequence[float] | Sequence[complex] | np.ndarray | Vector[Float],
) -> Vector[Qubit]Apply Möttönen amplitude encoding to qubits in place.
Convenience wrapper around :class:MottonenAmplitudeEncoding that
accepts a Vector handle and writes the gated qubits back into
the same vector. Real and complex amplitudes are both supported;
see the class docstring for the gate-count tradeoff between the two
paths.
.. important::
**Pre-condition: ``qubits`` must currently be in the all-zero
state** $|0\rangle^{\otimes n}$. Möttönen encodes the
unitary that takes $|0\rangle^{\otimes n}$ to the
normalised target; applied to any other state it produces a
different (in general meaningless) output. In practice call
this immediately after ``qmc.qubit_array(n, ...)`` inside a
kernel.amplitudes may be supplied as one of three forms:
A concrete Python
Sequence[float]/Sequence[complex]/np.ndarray. Use this when the amplitudes are known where you build the kernel (closed over from the surrounding Python scope). This is the only form that supports complex amplitude vectors.A
Vector[Float]handle obtained from a kernel parameter that is bound at compile time viatranspiler.transpile(kernel, bindings={"amps": [...]}). The handle’s bound concrete values are extracted at trace time and flow through the same angle-computation path. This makesbindings={"amps": ...}ergonomic without forcing the user to pre-compute Möttönen angles.Vector[Float]carries real numbers only; for complex amplitudes via a kernel parameter, either pass a concretenp.ndarraydirectly (closure form), or use :func:amplitude_encoding_from_angleswith separatery_anglesandrz_anglesparameters.Not a
Vector[Float]left symbolic byparameters=["amps"]— the angle computation requires concrete values at trace time. Use :func:amplitude_encoding_from_angleswithparameters=["ry_angles", "rz_angles"]for the runtime-parametric case.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | Vector[Qubit] | Vector of n qubit handles, expected to start in . |
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Vector[Float] | Amplitude vector of length 2**n. Concrete sequences and np.ndarray accept both real and complex inputs and are normalised automatically. Vector[Float] is accepted only when (a) its concrete values are available at trace time (i.e., it came from a bindings={...} entry, not from parameters=[...]) and (b) the values are real; complex amplitudes via a kernel parameter must instead go through :func:amplitude_encoding_from_angles. |
Returns:
Vector[Qubit] — Vector[Qubit]: The same qubits vector, with each element
updated to the post-encoding qubit handle.
Raises:
ValueError— If qubits has a symbolic shape (no compile-time known size — e.g., it is a sub-kernel parameter traced standalone before its shape is resolved), the amplitude length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), all amplitudes are zero, the qubit count does not matchlog2(len(amplitudes)), or amplitudes is aVector[Float]handle whose concrete values are not available at trace time (use :func:amplitude_encoding_from_angleswithparameters=[...]for runtime-parametric angles).
Example::
# Concrete Python amplitudes
@qmc.qkernel
def prepare() -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding(q, [1.0, 0.0, 0.0, 1.0])
return qmc.measure(q)
# Bound Vector[Float] kernel parameter
@qmc.qkernel
def prepare(amps: qmc.Vector[qmc.Float]) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding(q, amps)
return qmc.measure(q)
transpiler.transpile(prepare, bindings={"amps": [1.0, 0.0, 0.0, 1.0]})amplitude_encoding_from_angles [source]¶
def amplitude_encoding_from_angles(
qubits: Vector[Qubit],
ry_angles: Sequence[float] | np.ndarray | Vector[Float],
rz_angles: Sequence[float] | np.ndarray | Vector[Float] | None = None,
) -> Vector[Qubit]Apply Möttönen amplitude encoding from pre-computed Ry / Rz angles.
.. important::
**Pre-condition: ``qubits`` must currently be in the all-zero
state** $|0\rangle^{\otimes n}$. The Möttönen Gray-walk
emission produced by these angle vectors only encodes the
intended state when starting from $|0\rangle^{\otimes n}$;
applied to any other input it produces ``U |\phi\rangle`` for
the same ``U`` and a different ``|\phi\rangle``, which is in
general not the target amplitude vector.Companion to :func:amplitude_encoding for the parametric use
case: the user pre-computes the Gray-walk Ry (and optionally Rz)
angles classically with
:func:qamomile.linalg.compute_mottonen_amplitude_encoding_ry_angles
and
:func:qamomile.linalg.compute_mottonen_amplitude_encoding_rz_angles,
then passes them in as either concrete sequences or as
Vector[Float] handles obtained from kernel parameters. In the
latter case the angles can be left as runtime parameters
(transpiler.transpile(kernel, parameters=["ry_angles", ...]))
so the same compiled circuit can be re-bound to different
amplitude vectors without recompilation — useful inside hybrid
optimisation loops.
Unlike :func:amplitude_encoding, this function does NOT wrap the
emission in a :class:MottonenAmplitudeEncoding CompositeGate
box on the IR side; the Ry / Rz / CNOT Gray-walk gates are emitted
directly into the surrounding kernel. Resource estimation /
visualization will therefore see the elementary gates rather than
a single high-level op.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | Vector[Qubit] | Vector of n qubit handles, expected to start in . |
ry_angles | Sequence[float] | np.ndarray | Vector[Float] | Gray-walk Ry angles for the magnitude stage. Must have length 2**n - 1. |
rz_angles | Sequence[float] | np.ndarray | Vector[Float] | None | Gray-walk Rz angles for the phase stage. Pass None (default) to skip the Rz stage entirely (real-amplitude path); otherwise must have length 2**n - 1 as well. |
Returns:
Vector[Qubit] — Vector[Qubit]: The same qubits vector, with each element
updated to the post-encoding qubit handle.
Raises:
ValueError— Ifry_angles/rz_anglesis a concrete sequence whose length does not match2**n - 1, or if thequbitsvector has an unresolved symbolic shape thatget_sizecannot reduce to a concrete integer. When the angle argument is aVector[Float]handle the length check is skipped (the shape may be symbolic at trace time); a runtime mismatch then surfaces as a backend bind-time error instead.
Example::
from qamomile.linalg import (
compute_mottonen_amplitude_encoding_ry_angles,
compute_mottonen_amplitude_encoding_rz_angles,
)
# Pre-compute classically (outside the kernel)
ry = compute_mottonen_amplitude_encoding_ry_angles(amps)
rz = compute_mottonen_amplitude_encoding_rz_angles(amps)
@qmc.qkernel
def prepare(
ry_a: qmc.Vector[qmc.Float],
rz_a: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding_from_angles(q, ry_a, rz_a)
return qmc.measure(q)
exe = transpiler.transpile(prepare, parameters=["ry_a", "rz_a"])
# Same compiled circuit, re-bound at "runtime":
exe.run(transpiler.executor(),
bindings={"ry_a": ry.tolist(), "rz_a": rz.tolist()})Classes¶
MottonenAmplitudeEncoding [source]¶
class MottonenAmplitudeEncoding(CompositeGate)Möttönen amplitude encoding for normalised real or complex vectors.
Prepares the state from
using uniformly controlled Y (and,
for genuinely complex inputs, Z) rotations decomposed into RY /
RZ and CNOT gates with Gray-code ordering.
.. important::
**Pre-condition: the input qubits must be in the all-zero state**
$|0\rangle^{\otimes n}$. The gate emits the unitary
that maps $|0\rangle^{\otimes n}$ to the target
$|\psi\rangle$. Applied to any other input it
produces ``U |\phi\rangle`` for that ``|\phi\rangle`` —
which is *not* the target amplitude vector. Qamomile does
not track qubit states at runtime, so this is the caller's
responsibility.Notes:
Input amplitudes are normalised automatically.
Real inputs (negative entries allowed) use a single RY stage: the sign of
arctan2at the leaf preserves negative signs natively, so no RZ overhead is incurred.Complex inputs with non-zero imaginary part use the full two-stage construction (RY for magnitudes, RZ for phases).
Complex inputs whose imaginary part is identically zero (e.g.
[1+0j, -1+0j]) are silently coerced to real and follow the single-stage path.__init__runs the cheap pass eagerly (O(2^n)): shape / length validation, normalisation, and complex detection, so input errors (wrong shape, length not a power of two, all-zero amplitudes) surface at construction time. The expensive pass — full per-level angle precomputation,O(n * 2^n)— is deferred until the first :meth:_decomposecall and cached afterwards. :meth:_resourcesreads only the cached complex flag and never triggers the angle pass.
Example::
# Real amplitudes (signed allowed)
gate = MottonenAmplitudeEncoding([1.0, 0.0, 0.0, 1.0])
q0, q1 = gate(q0, q1)
# Complex amplitudes
gate = MottonenAmplitudeEncoding([1+0j, 1j, -1+0j, -1j])
q0, q1 = gate(q0, q1)Constructor¶
def __init__(self, amplitudes: Sequence[float] | Sequence[complex] | np.ndarray)Initialise the gate with a concrete amplitude vector.
Runs the full normalisation + complex-detection pass eagerly
(O(2^n)) so that input errors — wrong shape, length not a
power of two, all-zero amplitudes — surface at construction
time rather than at the first _resources() /
_decompose() call. Angle precomputation
(O(n * 2^n)) stays deferred: it runs lazily on the first
_decompose() call and is cached afterwards.
This keeps the dominant cost of Möttönen out of
kernel-build time when the gate is later only used for
resource estimation. In practice the
CompositeGate.__call__ framework eagerly invokes
_decompose() to build the implementation block when the
gate is invoked inside a @qkernel, so the lazy aspect
currently bites only for code that constructs
MottonenAmplitudeEncoding standalone — but the laziness
still matters there, and is the right shape for a future
framework refactor that defers _decompose() until emit
time.
Parameters:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude vector of length 2**n. Real or complex; it is automatically normalised. Complex inputs with zero imaginary part are coerced to real (single-stage RY path). |
Raises:
ValueError— If the input is not a 1-D vector, the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
Attributes¶
custom_namegate_typenum_target_qubits: int Number of qubits the gate acts on.
qamomile.circuit.algorithm.state_preparation.mottonen_amplitude_encoding¶
State preparation via Möttönen’s uniformly-controlled-rotation construction.
Prepares the n-qubit state
from for a normalised amplitude vector
a. The construction follows Möttönen et al., “Transformation of
quantum states using uniformly controlled rotations”
(arXiv:quant-ph/0407010). Both the RY-only “amplitude distribution”
stage and the RZ “phase restoration” stage are supported, so general
complex amplitudes work end-to-end.
.. important::
**Pre-condition: the input register must be in the all-zero state**
$|0\rangle^{\otimes n}$. The Möttönen construction
decomposes the unitary that takes $|0\rangle^{\otimes n}$
to the target $|\psi\rangle$; applying it to any other
initial state yields a *different* output (the same unitary
applied to a different input), not the target amplitude vector.
There is no runtime guard for this — Qamomile does not track
qubit states — so it is the caller's responsibility to ensure the
register has not yet been mutated when ``amplitude_encoding`` /
``amplitude_encoding_from_angles`` is invoked. In practice, call
these helpers immediately after ``qmc.qubit_array(n, ...)``
inside a kernel.This module hosts only the gate-emission side of the algorithm:
the MottonenAmplitudeEncoding CompositeGate, the function
wrappers amplitude_encoding / amplitude_encoding_from_angles,
and the Gray-walk emitter _emit_mottonen_gates. The classical
angle precomputation lives in :mod:qamomile.linalg.mottonen so that
hybrid loops can call compute_mottonen_amplitude_encoding_*_angles
outside any kernel and feed the result back through
amplitude_encoding_from_angles with parameters=[...].
Pipeline¶
Validate and normalise the input — see :func:
qamomile.linalg.mottonen.validate_and_normalize_amplitudes(length must be a power of two, all-zero rejected).Determine whether the input is genuinely complex (has a non-zero imaginary part). Real inputs (including complex with zero imag) keep the original signed-RY path — negative real amplitudes flow through the sign of
arctan2(a_1, a_0)naturally, with no RZ overhead. Complex inputs use the iterative disentangling construction.For real inputs, compute the per-level RY rotation angles by splitting each chunk into upper / lower halves and using
arctan2of the two sub-block norms (or signedarctan2at the leaf). See :func:qamomile.linalg.mottonen.compute_all_ry_angles_per_level.For complex inputs, iteratively disentangle the target amplitude vector qubit-by-qubit from LSB to MSB. See :func:
qamomile.linalg.mottonen.compute_disentangling_angles_per_level.At each level
k >= 1apply a uniformly controlled rotation over the previously preparedkqubits. We use the standard Gray-code RY / CNOT decomposition for the magnitude stage and the same structure with RZ for the phase stage. The emitted gate order is “all RY layers, then all RZ layers”. Pairwise[U_y^(k), U_z^(k')]does NOT commute in general (includingk != k'cases, because earlier RY targets can be controls of later RZ multiplexers), but the FULL sweep product equals the per-level interleaved product(U_z^(0) U_y^(0)) ... (U_z^(n-1) U_y^(n-1))as unitaries — this is a structural identity verified by :class:tests.circuit.algorithm.state_preparation.test_mottonen_amplitude_encoding.TestRyRzOrderingwith arbitrary (non-disentangling) per-level angles. Within each level the order RY-before-RZ is preserved in both schemes.
Lazy angle precomputation¶
MottonenAmplitudeEncoding.__init__ runs the cheap normalisation
pass eagerly (O(2^n)) so input errors — wrong shape, length not a
power of two, all-zero amplitudes — surface at construction time. The
expensive angle precomputation (O(n * 2^n)) is deferred: it runs
lazily on the first _decompose() call and is cached afterwards.
_resources() reads is_complex directly from the cached
__init__ state and never triggers angle precomputation, so
standalone resource estimation on a MottonenAmplitudeEncoding
instance pays only the normalisation cost.
In practice the surrounding CompositeGate.__call__ framework
eagerly invokes _decompose() when the gate is invoked inside a
@qkernel (to populate the operation’s implementation_block),
so kernel-side estimate_resources() does still pay the angle
cost today. The lazy aspect is the right shape for a future
framework refactor that defers _build_decomposition_block until
emit time, and is verified standalone by
tests.circuit.algorithm.state_preparation.test_mottonen_amplitude_encoding.TestLazyConstruction.
Overview¶
| Function | Description |
|---|---|
amplitude_encoding | Apply Möttönen amplitude encoding to qubits in place. |
amplitude_encoding_from_angles | Apply Möttönen amplitude encoding from pre-computed Ry / Rz angles. |
compute_all_ry_angles_per_level | Pre-compute every level’s Ry rotation angle vector (magnitude stage). |
compute_disentangling_angles_per_level | Iteratively disentangle to obtain both Ry and Rz angles per level. |
cx | CNOT (Controlled-X) gate. |
get_size | Return the size of a Vector handle as a Python integer. |
ry | Rotation around Y-axis: RY(angle) = exp(-i * angle/2 * Y). |
rz | Rotation around Z-axis: RZ(angle) = exp(-i * angle/2 * Z). |
validate_and_normalize_amplitudes | Validate an amplitude vector and return its normalised form. |
| Class | Description |
|---|---|
CompositeGate | Base class for user-facing composite gate definitions. |
CompositeGateType | Registry of known composite gate types. |
MottonenAmplitudeEncoding | Möttönen amplitude encoding for normalised real or complex vectors. |
ResourceMetadata | Resource estimation metadata for composite gates. |
Functions¶
amplitude_encoding [source]¶
def amplitude_encoding(
qubits: Vector[Qubit],
amplitudes: Sequence[float] | Sequence[complex] | np.ndarray | Vector[Float],
) -> Vector[Qubit]Apply Möttönen amplitude encoding to qubits in place.
Convenience wrapper around :class:MottonenAmplitudeEncoding that
accepts a Vector handle and writes the gated qubits back into
the same vector. Real and complex amplitudes are both supported;
see the class docstring for the gate-count tradeoff between the two
paths.
.. important::
**Pre-condition: ``qubits`` must currently be in the all-zero
state** $|0\rangle^{\otimes n}$. Möttönen encodes the
unitary that takes $|0\rangle^{\otimes n}$ to the
normalised target; applied to any other state it produces a
different (in general meaningless) output. In practice call
this immediately after ``qmc.qubit_array(n, ...)`` inside a
kernel.amplitudes may be supplied as one of three forms:
A concrete Python
Sequence[float]/Sequence[complex]/np.ndarray. Use this when the amplitudes are known where you build the kernel (closed over from the surrounding Python scope). This is the only form that supports complex amplitude vectors.A
Vector[Float]handle obtained from a kernel parameter that is bound at compile time viatranspiler.transpile(kernel, bindings={"amps": [...]}). The handle’s bound concrete values are extracted at trace time and flow through the same angle-computation path. This makesbindings={"amps": ...}ergonomic without forcing the user to pre-compute Möttönen angles.Vector[Float]carries real numbers only; for complex amplitudes via a kernel parameter, either pass a concretenp.ndarraydirectly (closure form), or use :func:amplitude_encoding_from_angleswith separatery_anglesandrz_anglesparameters.Not a
Vector[Float]left symbolic byparameters=["amps"]— the angle computation requires concrete values at trace time. Use :func:amplitude_encoding_from_angleswithparameters=["ry_angles", "rz_angles"]for the runtime-parametric case.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | Vector[Qubit] | Vector of n qubit handles, expected to start in . |
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Vector[Float] | Amplitude vector of length 2**n. Concrete sequences and np.ndarray accept both real and complex inputs and are normalised automatically. Vector[Float] is accepted only when (a) its concrete values are available at trace time (i.e., it came from a bindings={...} entry, not from parameters=[...]) and (b) the values are real; complex amplitudes via a kernel parameter must instead go through :func:amplitude_encoding_from_angles. |
Returns:
Vector[Qubit] — Vector[Qubit]: The same qubits vector, with each element
updated to the post-encoding qubit handle.
Raises:
ValueError— If qubits has a symbolic shape (no compile-time known size — e.g., it is a sub-kernel parameter traced standalone before its shape is resolved), the amplitude length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), all amplitudes are zero, the qubit count does not matchlog2(len(amplitudes)), or amplitudes is aVector[Float]handle whose concrete values are not available at trace time (use :func:amplitude_encoding_from_angleswithparameters=[...]for runtime-parametric angles).
Example::
# Concrete Python amplitudes
@qmc.qkernel
def prepare() -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding(q, [1.0, 0.0, 0.0, 1.0])
return qmc.measure(q)
# Bound Vector[Float] kernel parameter
@qmc.qkernel
def prepare(amps: qmc.Vector[qmc.Float]) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding(q, amps)
return qmc.measure(q)
transpiler.transpile(prepare, bindings={"amps": [1.0, 0.0, 0.0, 1.0]})amplitude_encoding_from_angles [source]¶
def amplitude_encoding_from_angles(
qubits: Vector[Qubit],
ry_angles: Sequence[float] | np.ndarray | Vector[Float],
rz_angles: Sequence[float] | np.ndarray | Vector[Float] | None = None,
) -> Vector[Qubit]Apply Möttönen amplitude encoding from pre-computed Ry / Rz angles.
.. important::
**Pre-condition: ``qubits`` must currently be in the all-zero
state** $|0\rangle^{\otimes n}$. The Möttönen Gray-walk
emission produced by these angle vectors only encodes the
intended state when starting from $|0\rangle^{\otimes n}$;
applied to any other input it produces ``U |\phi\rangle`` for
the same ``U`` and a different ``|\phi\rangle``, which is in
general not the target amplitude vector.Companion to :func:amplitude_encoding for the parametric use
case: the user pre-computes the Gray-walk Ry (and optionally Rz)
angles classically with
:func:qamomile.linalg.compute_mottonen_amplitude_encoding_ry_angles
and
:func:qamomile.linalg.compute_mottonen_amplitude_encoding_rz_angles,
then passes them in as either concrete sequences or as
Vector[Float] handles obtained from kernel parameters. In the
latter case the angles can be left as runtime parameters
(transpiler.transpile(kernel, parameters=["ry_angles", ...]))
so the same compiled circuit can be re-bound to different
amplitude vectors without recompilation — useful inside hybrid
optimisation loops.
Unlike :func:amplitude_encoding, this function does NOT wrap the
emission in a :class:MottonenAmplitudeEncoding CompositeGate
box on the IR side; the Ry / Rz / CNOT Gray-walk gates are emitted
directly into the surrounding kernel. Resource estimation /
visualization will therefore see the elementary gates rather than
a single high-level op.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | Vector[Qubit] | Vector of n qubit handles, expected to start in . |
ry_angles | Sequence[float] | np.ndarray | Vector[Float] | Gray-walk Ry angles for the magnitude stage. Must have length 2**n - 1. |
rz_angles | Sequence[float] | np.ndarray | Vector[Float] | None | Gray-walk Rz angles for the phase stage. Pass None (default) to skip the Rz stage entirely (real-amplitude path); otherwise must have length 2**n - 1 as well. |
Returns:
Vector[Qubit] — Vector[Qubit]: The same qubits vector, with each element
updated to the post-encoding qubit handle.
Raises:
ValueError— Ifry_angles/rz_anglesis a concrete sequence whose length does not match2**n - 1, or if thequbitsvector has an unresolved symbolic shape thatget_sizecannot reduce to a concrete integer. When the angle argument is aVector[Float]handle the length check is skipped (the shape may be symbolic at trace time); a runtime mismatch then surfaces as a backend bind-time error instead.
Example::
from qamomile.linalg import (
compute_mottonen_amplitude_encoding_ry_angles,
compute_mottonen_amplitude_encoding_rz_angles,
)
# Pre-compute classically (outside the kernel)
ry = compute_mottonen_amplitude_encoding_ry_angles(amps)
rz = compute_mottonen_amplitude_encoding_rz_angles(amps)
@qmc.qkernel
def prepare(
ry_a: qmc.Vector[qmc.Float],
rz_a: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = amplitude_encoding_from_angles(q, ry_a, rz_a)
return qmc.measure(q)
exe = transpiler.transpile(prepare, parameters=["ry_a", "rz_a"])
# Same compiled circuit, re-bound at "runtime":
exe.run(transpiler.executor(),
bindings={"ry_a": ry.tolist(), "rz_a": rz.tolist()})compute_all_ry_angles_per_level [source]¶
def compute_all_ry_angles_per_level(amplitudes: np.ndarray, num_qubits: int) -> list[np.ndarray]Pre-compute every level’s Ry rotation angle vector (magnitude stage).
Implements the recursive magnitude-stage angle formula given in
Möttönen et al., arXiv:quant-ph/0407010, Section III, Eq. (8)
(with the leaf case matching the unnumbered angle
2 \arcsin(|a_{2j}| / \sqrt{|a_{2j-1}|^2 + |a_{2j}|^2})
just before Eq. (6), equivalent to 2 atan2(|a_1|, |a_0|)
on the leaf pair). The pre-condition is the all-zero state
; the formulas below describe the
angles needed to reach a real amplitudes from there.
For each level k (0 <= k < num_qubits) the amplitude
vector is split into 2**k equal chunks. Each chunk yields one
per-control-state angle α (Eq. (8)):
Intermediate levels (
chunk_size > 2): the angle rotates the target qubit so its|1>weight matches the lower-half block norm. Usingarctan2(norm_lower, norm_upper)keeps the formula well defined when the upper half has zero norm.Leaf level (
chunk_size == 2):α = 2 * arctan2(a_1, a_0)directly (signed, so negative amplitudes are preserved without a phase stage).
For k >= 1 the per-control-state angles are then transformed
to the Gray-walk basis via :func:_to_gray_walk_basis (paper
Eq. (3)).
Parameters:
| Name | Type | Description |
|---|---|---|
amplitudes | np.ndarray | Unit-norm real amplitude vector of length 2**num_qubits. |
num_qubits | int | Number of qubits in the target register. |
Returns:
list[np.ndarray] — list[np.ndarray]: A list of num_qubits arrays; the k-th
entry has length 2**k and holds the Gray-walk Ry
angles for that level.
compute_disentangling_angles_per_level [source]¶
def compute_disentangling_angles_per_level(
amplitudes: np.ndarray,
num_qubits: int,
) -> tuple[list[np.ndarray], list[np.ndarray]]Iteratively disentangle to obtain both Ry and Rz angles per level.
Implements the Möttönen disentangling sweep for general (complex)
amplitudes from Möttönen et al., arXiv:quant-ph/0407010,
Section III, Eqs. (4)-(8): the phase-equalisation rotation
Ξ_z (Eq. (4)) followed by the magnitude rotation, applied
pair-by-pair from LSB to MSB. Pre-condition is the all-zero
state ; the angles below are
those needed to reach the input amplitudes from there
(computed via the inverse sweep that disentangles the input).
At each step the amplitude vector is halved by pairing adjacent
entries; for the pair (a_0, a_1) we read off
Ry angle = 2 * arctan2(|a_1|, |a_0|)(magnitude split, paper Eq. (8) restricted to the leaf level — equivalently the unnumbered2 arcsin(...)form just before Eq. (6)),Rz angle = arg(a_1 / a_0)(phase difference, paper Eq. (5)),
and replace the pair with the single complex amplitude that
survives the implicit Rz^{-1} Ry^{-1} disentangling step
(paper Eq. (6) zeros out one of the pair entries; the
surviving amplitude is
which carries the averaged phase needed by the next outer step
of the sweep — the paper does not write β in this exact
closed form but the relation falls out of combining
Eqs. (5)-(7)). The full sweep is paper Eq. (7).
Zero-magnitude halves are handled gracefully by leaving the
corresponding angle at 0 (the underlying state has no support
there so the angle is immaterial). The returned per-level arrays
are already bit-reversed and gray-walk-transformed (paper
Eq. (3) applied per level via :func:_to_gray_walk_basis),
ready for the Möttönen Gray-walk emission.
Parameters:
| Name | Type | Description |
|---|---|---|
amplitudes | np.ndarray | Unit-norm complex amplitude vector of length 2**num_qubits. |
num_qubits | int | Number of qubits in the target register. |
Returns:
tuple[list[np.ndarray], list[np.ndarray]] — tuple[list[np.ndarray], list[np.ndarray]]:
(ry_angles_per_level, rz_angles_per_level) in
Gray-walk ordering. Each list has num_qubits entries;
the k-th entry holds 2**k angles for level k of
the forward emission.
cx [source]¶
def cx(control: Qubit, target: Qubit) -> tuple[Qubit, Qubit]CNOT (Controlled-X) gate.
get_size [source]¶
def get_size(arr: Vector[_H]) -> intReturn the size of a Vector handle as a Python integer.
Resolves the leading axis of arr.shape through two forms a
Vector shape entry can take:
A plain Python
int(built-in bound shape; this is what you get fromqmc.qubit_array(N, ...)for literalN).A
UInthandle whose underlyingValuecarries a compile-time constant (set byuint(literal),_create_bound_input, or partial evaluation).
A UInt handle whose underlying Value is not a constant is
treated as an unresolved symbolic dimension and raises
ValueError even when the handle has the dataclass-default
init_value=0. Falling back to init_value for that case
would silently turn a runtime-symbolic Vector[Float] parameter
into a “size 0” array, hiding programming errors. Callers that
need to handle symbolic shapes (e.g., to gracefully no-op when the
size is unknown) must catch the ValueError themselves.
Parameters:
| Name | Type | Description |
|---|---|---|
arr | Vector[Handle] | Vector handle whose first axis size is requested. |
Returns:
int — The first-axis size as a plain Python int.
Raises:
ValueError— If the shape cannot be resolved to a concrete integer — e.g., the Vector is a runtime-parametric handle without compile-time bindings, or carries aUIntdimension whose underlyingValuehas not been promoted to a constant.
ry [source]¶
def ry(
target: Union[Qubit, Vector[Qubit]],
angle: float | Float,
) -> Union[Qubit, Vector[Qubit]]Rotation around Y-axis: RY(angle) = exp(-i * angle/2 * Y).
Broadcasts the same angle over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
angle | float | Float | Rotation angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
rz [source]¶
def rz(
target: Union[Qubit, Vector[Qubit]],
angle: float | Float,
) -> Union[Qubit, Vector[Qubit]]Rotation around Z-axis: RZ(angle) = exp(-i * angle/2 * Z).
Broadcasts the same angle over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
angle | float | Float | Rotation angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
validate_and_normalize_amplitudes [source]¶
def validate_and_normalize_amplitudes(
amplitudes: Sequence[float] | Sequence[complex] | np.ndarray,
) -> tuple[np.ndarray, int, bool]Validate an amplitude vector and return its normalised form.
The Möttönen construction (arXiv:quant-ph/0407010, Section III) works on a unit-norm amplitude vector indexed by computational basis states; this helper enforces that pre-condition so the downstream angle computation can assume a well-formed input.
Inputs may be real or complex. A complex input whose imaginary
part is identically zero (within np.allclose tolerance) is
coerced to a real array; this preserves the cheaper signed-RY fast
path for vectors that happen to arrive boxed as complex.
Parameters:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude vector. Must be a 1-D sequence whose length is a power of two and at least 2, with at least one non-zero entry. |
Returns:
tuple[np.ndarray, int, bool] — tuple[np.ndarray, int, bool]:
(normalized, num_qubits, is_complex) where
normalized is a unit-norm np.ndarray,
num_qubits is log2(len(amplitudes)), and
is_complex is True iff the input has a non-zero
imaginary component (and therefore needs the
phase-restoration stage downstream). When is_complex
is False, normalized.dtype is float; otherwise
it is complex.
Raises:
ValueError— If the input is not a 1-D vector (e.g., a nested sequence or a 2-Dnp.ndarray), the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
Classes¶
CompositeGate [source]¶
class CompositeGate(abc.ABC)Base class for user-facing composite gate definitions.
Subclasses can define composite gates in two ways:
Using _decompose() (recommended for users): Define the gate decomposition using frontend syntax (same as
qkernel[source]).class QFT(CompositeGate): def __init__(self, num_qubits: int): self._num_qubits = num_qubits @property def num_target_qubits(self) -> int: return self._num_qubits def _decompose(self, qubits: Vector[Qubit]) -> Vector[Qubit]: # Use frontend syntax: qm.h(), qm.cp(), qm.range(), etc. n = self._num_qubits for j in qmc.range(n - 1, -1, -1): qubits[j] = qmc.h(qubits[j]) for k in qmc.range(j - 1, -1, -1): angle = math.pi / (2 ** (j - k)) qubits[j], qubits[k] = qmc.cp(qubits[j], qubits[k], angle) return qubits def _resources(self) -> ResourceMetadata: return ResourceMetadata(t_gates=0)Using get_implementation() (advanced): Return a pre-built Block directly.
Example usage:
# Factory function pattern
def qft(qubits: Vector[Qubit]) -> Vector[Qubit]:
n = _get_size(qubits)
return QFT(n)(qubits)
# Direct class usage
result = QFT(3)(*qubit_list)Attributes¶
custom_name: strgate_type: CompositeGateTypenum_control_qubits: int Number of control qubits (default: 0).num_target_qubits: int Number of target qubits this gate operates on.
Methods¶
build_decomposition¶
def build_decomposition(self, *qubits: Qubit = (), **params: Any = {}) -> Block | NoneBuild the decomposition circuit dynamically.
Override this method to provide a decomposition that depends on runtime arguments (e.g., QPE needs the unitary Block).
This method is called by InlinePass when inlining composite gates that have dynamic implementations.
Parameters:
| Name | Type | Description |
|---|---|---|
*qubits | Qubit | The qubits passed to the gate |
**params | Any | Additional parameters (e.g., unitary for QPE) |
Returns:
Block | None — Block containing the decomposition, or None if not available.
Example:
class QPE(CompositeGate):
def build_decomposition(self, *qubits, **params):
unitary = params.get("unitary")
# Build QPE circuit using the unitary
return self._build_qpe_impl(qubits, unitary)get_implementation¶
def get_implementation(self) -> Block | NoneGet the implementation Block, if any.
Return None for stub gates (used in resource estimation). Override in subclasses to provide implementation.
Note: If _decompose() is defined, it takes precedence over this method.
get_resource_metadata¶
def get_resource_metadata(self) -> ResourceMetadata | NoneGet resource estimation metadata.
Returns _resources() if defined, otherwise None. Override _resources() to provide resource hints.
get_resources_for_strategy¶
def get_resources_for_strategy(self, strategy_name: str | None = None) -> ResourceMetadata | NoneGet resource metadata for a specific strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
strategy_name | str | None | Strategy to query, or None for default |
Returns:
ResourceMetadata | None — ResourceMetadata for the strategy, or None if not available
get_strategy¶
@classmethod
def get_strategy(cls, name: str | None = None) -> 'DecompositionStrategy | None'Get a registered decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | None | Strategy name, or None for default strategy |
Returns:
'DecompositionStrategy | None' — DecompositionStrategy instance, or None if not found
list_strategies¶
@classmethod
def list_strategies(cls) -> list[str]List all registered strategy names.
Returns:
list[str] — List of strategy names
register_strategy¶
@classmethod
def register_strategy(cls, name: str, strategy: 'DecompositionStrategy') -> NoneRegister a decomposition strategy for this gate type.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy identifier (e.g., “standard”, “approximate”) |
strategy | 'DecompositionStrategy' | DecompositionStrategy instance |
Example:
QFT.register_strategy("approximate", ApproximateQFTStrategy(k=3))set_default_strategy¶
@classmethod
def set_default_strategy(cls, name: str) -> NoneSet the default decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy name to use as default |
Raises:
ValueError— If strategy is not registered
CompositeGateType [source]¶
class CompositeGateType(enum.Enum)Registry of known composite gate types.
Attributes¶
CUSTOMIQFTQFTQPE
MottonenAmplitudeEncoding [source]¶
class MottonenAmplitudeEncoding(CompositeGate)Möttönen amplitude encoding for normalised real or complex vectors.
Prepares the state from
using uniformly controlled Y (and,
for genuinely complex inputs, Z) rotations decomposed into RY /
RZ and CNOT gates with Gray-code ordering.
.. important::
**Pre-condition: the input qubits must be in the all-zero state**
$|0\rangle^{\otimes n}$. The gate emits the unitary
that maps $|0\rangle^{\otimes n}$ to the target
$|\psi\rangle$. Applied to any other input it
produces ``U |\phi\rangle`` for that ``|\phi\rangle`` —
which is *not* the target amplitude vector. Qamomile does
not track qubit states at runtime, so this is the caller's
responsibility.Notes:
Input amplitudes are normalised automatically.
Real inputs (negative entries allowed) use a single RY stage: the sign of
arctan2at the leaf preserves negative signs natively, so no RZ overhead is incurred.Complex inputs with non-zero imaginary part use the full two-stage construction (RY for magnitudes, RZ for phases).
Complex inputs whose imaginary part is identically zero (e.g.
[1+0j, -1+0j]) are silently coerced to real and follow the single-stage path.__init__runs the cheap pass eagerly (O(2^n)): shape / length validation, normalisation, and complex detection, so input errors (wrong shape, length not a power of two, all-zero amplitudes) surface at construction time. The expensive pass — full per-level angle precomputation,O(n * 2^n)— is deferred until the first :meth:_decomposecall and cached afterwards. :meth:_resourcesreads only the cached complex flag and never triggers the angle pass.
Example::
# Real amplitudes (signed allowed)
gate = MottonenAmplitudeEncoding([1.0, 0.0, 0.0, 1.0])
q0, q1 = gate(q0, q1)
# Complex amplitudes
gate = MottonenAmplitudeEncoding([1+0j, 1j, -1+0j, -1j])
q0, q1 = gate(q0, q1)Constructor¶
def __init__(self, amplitudes: Sequence[float] | Sequence[complex] | np.ndarray)Initialise the gate with a concrete amplitude vector.
Runs the full normalisation + complex-detection pass eagerly
(O(2^n)) so that input errors — wrong shape, length not a
power of two, all-zero amplitudes — surface at construction
time rather than at the first _resources() /
_decompose() call. Angle precomputation
(O(n * 2^n)) stays deferred: it runs lazily on the first
_decompose() call and is cached afterwards.
This keeps the dominant cost of Möttönen out of
kernel-build time when the gate is later only used for
resource estimation. In practice the
CompositeGate.__call__ framework eagerly invokes
_decompose() to build the implementation block when the
gate is invoked inside a @qkernel, so the lazy aspect
currently bites only for code that constructs
MottonenAmplitudeEncoding standalone — but the laziness
still matters there, and is the right shape for a future
framework refactor that defers _decompose() until emit
time.
Parameters:
| Name | Type | Description |
|---|---|---|
amplitudes | Sequence[float] | Sequence[complex] | np.ndarray | Amplitude vector of length 2**n. Real or complex; it is automatically normalised. Complex inputs with zero imaginary part are coerced to real (single-stage RY path). |
Raises:
ValueError— If the input is not a 1-D vector, the length is not a power of two (or is less than 2, i.e., would map to a zero-qubit register), or all amplitudes are zero.
Attributes¶
custom_namegate_typenum_target_qubits: int Number of qubits the gate acts on.
ResourceMetadata [source]¶
class ResourceMetadataResource estimation metadata for composite gates.
Gate count fields mirror GateCount categories.
None semantics:
Fields left as None mean “unknown/unspecified”. During extraction, gate_counter treats None as 0, which may undercount resources if the true value is nonzero. To ensure accurate resource estimates, set all relevant fields explicitly.
When total_gates is set but some of single_qubit_gates, two_qubit_gates, or multi_qubit_gates are None, the extractor emits a UserWarning if the known sub-total is less than total_gates, indicating potentially missing gate category data.
Constructor¶
def __init__(
self,
query_complexity: int | None = None,
t_gates: int | None = None,
ancilla_qubits: int = 0,
total_gates: int | None = None,
single_qubit_gates: int | None = None,
two_qubit_gates: int | None = None,
multi_qubit_gates: int | None = None,
clifford_gates: int | None = None,
rotation_gates: int | None = None,
custom_metadata: dict[str, Any] = dict(),
) -> NoneAttributes¶
ancilla_qubits: intclifford_gates: int | Nonecustom_metadata: dict[str, Any]multi_qubit_gates: int | Nonequery_complexity: int | Nonerotation_gates: int | Nonesingle_qubit_gates: int | Nonet_gates: int | Nonetotal_gates: int | Nonetwo_qubit_gates: int | None
qamomile.circuit.algorithm.trotter¶
Suzuki-Trotter time evolution as a self-recursive @qkernel.
The public :func:trotterized_time_evolution wrapper validates the
hamiltonian length and order and then delegates to the
@qkernel :func:_trotter_evolve, which slices the evolution into
step Trotter steps and applies a single-step operator from
:func:_suzuki_trotter_step. That step kernel branches on order:
order == 1— Lie-Trotter forward sweep (base case).order == 2— Strang splitting with the palindrome center merged (base case).order >= 4(even) — Suzuki’s fractal recursionThe recursion calls back into :func:
_suzuki_trotter_stepwithorder - 2. Qamomile’s transpiler resolves the self-call by iterating inline + partial-eval under the concreteorderbinding, so the emitted circuit is flat regardless of recursion depth.
order must be bound to a compile-time constant at transpile time —
without it the base-case if never folds and the unroll loop has
nothing to terminate on. Only order == 1 or even orders
2, 4, 6, ... are accepted; other values raise ValueError at
the call site. hamiltonian must contain at least two terms.
Example::
import qamomile.circuit as qmc
import qamomile.observable as qm_o
from qamomile.circuit.algorithm.trotter import (
trotterized_time_evolution,
)
@qmc.qkernel
def my_circuit(
Hs: qmc.Vector[qmc.Observable],
gamma: qmc.Float,
order: qmc.UInt,
step: qmc.UInt,
) -> qmc.Vector[qmc.Qubit]:
q = qmc.qubit_array(1, name="q")
q = trotterized_time_evolution(q, Hs, order, gamma, step)
return q
Hs = [qm_o.Z(0), qm_o.X(0)] # list of qamomile.observable.HamiltonianOverview¶
| Function | Description |
|---|---|
trotterized_time_evolution | Apply Suzuki-Trotter time evolution exp(-i gamma H) to q. |
Functions¶
trotterized_time_evolution [source]¶
def trotterized_time_evolution(
q: qmc.Vector[qmc.Qubit],
hamiltonian: qmc.Vector[qmc.Observable] | Sequence[Hamiltonian],
order: int | qmc.UInt,
gamma: float | qmc.Float,
step: int | qmc.UInt,
) -> qmc.Vector[qmc.Qubit]Apply Suzuki-Trotter time evolution exp(-i gamma H) to q.
H = sum_k hamiltonian[k]. The evolution is split into step
Trotter slices of size dt = gamma / step; each slice applies
the order-th Suzuki-Trotter formula via :func:_trotter_evolve.
Parameters:
| Name | Type | Description |
|---|---|---|
q | qmc.Vector[qmc.Qubit] | Qubit register handle. |
hamiltonian | qmc.Vector[qmc.Observable] | Sequence[Hamiltonian] | qmc.Vector[qmc.Observable] when called from a @qkernel (the handle type for a vector of Hamiltonians), or a Python list of qamomile.observable.Hamiltonian objects. At least two sub-Hamiltonian terms are required. |
order | int | qmc.UInt | Approximation order — 1 or a positive even integer (2, 4, 6, …). Must be a compile-time constant. |
gamma | float | qmc.Float | Total evolution time. |
step | int | qmc.UInt | Number of Trotter steps. |
Returns:
qmc.Vector[qmc.Qubit] — The evolved qubit register.
Raises:
ValueError— Ifhamiltonianhas fewer than two terms, iforderis abool, or iforderis not1or a positive even integer. When these arguments are still symbolic (e.g. the enclosing kernel has not been re-traced with bindings yet) validation silently defers.