Skip to content

workbench_algorithms.subroutines.multiplexing

Submodule for multiplexing.

BinaryTreeMultiplexor

BinaryTreeMultiplexor(**kwargs)

Bases: Qubrick

Optimized multiplexing based on Fig. 7 in arXiv:1805.03662.

compute

compute(
    index_reg: Qubits,
    multiplex_function: Callable,
    used_indices: list[int] | None = None,
    ctrl: Qubits | int = 0,
)

Compute the binary tree multiplexing circuit.

We account for various cases:

  1. There is no data to load (exit the routine).
  2. Loading a single item does not require this hefty machinery.
  3. Loading two items without a control is similarly cheap to the single-item-case.
  4. Loading n > 2 items without a control.
  5. Loading n > 1 items with a control.

The bottom two cases are handled by calling workhorse methods in this class.

Parameters:

Name Type Description Default
index_reg Qubits

Qubit register storing the values over which the multiplexing is performed.

required
multiplex_function Callable

A function which takes an index and then index register and performs the operation associated with that index.

required
used_indices list

List of indices corresponding to terms where the operators are actually being applied.

None
ctrl (int, Qubits)

Control for \(\text{SELECT}\). Defaults to 0.

0

ConditionallyCleanMultiplexor

Bases: Qubrick

Multiplexor utilizing conditionally clean construction as presented in arxiv:2407.17966.

See Figure 9 in reference. This qubrick works by constructing the first relevant index as aggregated conditions and then applies the corresponding data conditioned on this. Then it sequentially moves through the relevant indices by finding the MSB difference, undoing the structure to this point, and then redo-ing with the new condition utiliing the partial_compute() function.

Notes

This should be called with the filter '>>hermitian-window-filter>>' to take advantage of the cancellations that occur when you replace one condition with the next. This does the minimal work before calling the filter without hardcoding the cancellations with a maximum filter window required that is linear in the number of qubits in index register. Hardcoding like in the BinaryTreeMultiplexor requires more care as gates that begin each layer in the conditionally clean structure include adjacent bits such that the gate that switches branch for bit i, will need to be aware of the higher bit (i-1) to apply the correct cancellation.

compute

compute(
    index_reg: Qubits,
    multiplex_function: Callable,
    used_indices: list[int] | None = None,
    ctrl: Qubits | int = 0,
)

Compute the serial multiplexing circuit.

Parameters:

Name Type Description Default
index_reg Qubits

Qubit register storing the values over which the multiplexing is performed.

required
multiplex_function callable

A function which takes an index and then index register and performs the operation associated with that index.

required
used_indices list

List of indices corresponding to terms where the operators are actually being applied.

None
ctrl (int, Qubits)

Control for \(\text{SELECT}\). Defaults to 0.

0

OneAncMultiplexor

OneAncMultiplexor(**kwargs)

Bases: Qubrick

\(\text{SELECT}\) operator using a single, clean ancilla.

Circuit shown in Fig. (1.a) in arXiv:1812.00954.

compute

compute(
    index_reg: Qubits,
    multiplex_function: Callable,
    used_indices: list[int] | None = None,
    ctrl: Qubits | int = 0,
)

Compute a multiplexing circuit using a single, clean ancilla.

Parameters:

Name Type Description Default
index_reg Qubits

Qubit register storing the values over which the multiplexing is performed.

required
multiplex_function callable

A function which takes an index and then index register and performs the operation associated with that index.

required
used_indices list

List of indices corresponding to terms where the operators are actually being applied.

None
ctrl (int, Qubits)

Control for \(\text{SELECT}\). Defaults to 0.

0

GeneralMultiplexedRotationNaive

GeneralMultiplexedRotationNaive(
    rot_qbk: RotationInterface
    | GivensRotation[float] = None,
    **kwargs,
)

Bases: Qubrick

Routine for implementing multiplexed rotations, allowing for various tradeoffs.

Attributes:

Name Type Description
rot_qbk RotationInterface

The rotation protocol compliant Qubrick used for rotation operations.

Parameters:

Name Type Description Default
rot_qbk RotationInterface

The rotation protocol compliant Qubrick used for rotation operations.

None
**kwargs dict[str, Any]

Additional keyword arguments.

{}

compute

compute(
    index_reg: Qubits,
    target_reg: Qubits,
    rotation_specs: list[RotationSpec] | None = None,
    bits_of_precision: int | None = None,
    *,
    ctrl: Qubits | int = 0,
    **kwargs,
)

Compute circuit for a naive multiplexed rotation.

Implements the naive approach to multiplexed rotations by applying each rotation conditionally based on the index register matching the rotation's mux_idx. This implementation has no constraints on target qubits or rotation specifications, offering maximum flexibility at the cost of circuit efficiency.

Parameters:

Name Type Description Default
index_reg Qubits

Index register that selects which rotation to apply.

required
target_reg Qubits

Target register containing all qubits where rotations may be applied.

required
rotation_specs list[RotationSpec] | None

List of specifications for each rotation to be multiplexed.

None
bits_of_precision int | None

Precision for the rotation angles.

None
ctrl Qubits | int

Additional control for the entire operation (keyword-only parameter).

0
**kwargs dict[str, Any]

Additional keyword arguments.

{}

Raises:

Type Description
ValueError

If rotation_specs is not provided, or if a rotation spec doesn't have a rotation Qubrick specified and no default was provided.

GeneralMultiplexedRotationViaQROM

GeneralMultiplexedRotationViaQROM(
    qrom: QROM,
    rot_qbk: RotationInterface
    | GivensRotation[Qubits] = None,
    adder: Adder = PhaseGradientAdder(),
    is_unary: bool | None = None,
    **kwargs,
)

Bases: Qubrick

Optimized implementation of multiplexed rotations using QROM for angle loading.

This Qubrick implements multiplexed rotations using a QROM-based approach for efficient angle loading, significantly reducing the T-count compared to naive implementations when there are many rotations.

The implementation has specific constraints: within each multiplexer group: 1. All rotations must use the same rotation Qubrick 2. All rotations must target the same qubit indices

However, different multiplexer groups can have different target qubits and rotation Qubricks. This allows for efficient batching while maintaining some flexibility.

Attributes:

Name Type Description
qrom Incomplete

The Qubrick used for quantum ROM operations to load rotation angles.

rot_qbk Incomplete

Default rotation protocol compliant Qubrick used for all rotations unless overridden.

adder Incomplete

Adder Qubrick used in the rotation implementation.

bin_to_unary Incomplete

Converter for binary to unary encoding when required by the QROM.

Parameters:

Name Type Description Default
qrom QROM

The Qubrick instance used for quantum ROM operations.

required
rot_qbk RotationInterface | GivensRotation[Qubits]

Default rotation protocol compliant Qubrick to use if not specified in individual rotation specs. If None, each RotationSpec must provide its own.

None
adder Adder

Adder Qubrick used in the rotation implementation. Defaults to PhaseGradientAdder().

PhaseGradientAdder()
is_unary bool | None

Whether to use unary encoding for the index register. If None, auto-detects from the QROM implementation.

None
**kwargs dict[str, Any]

Additional keyword arguments passed to the Qubrick constructor.

{}

compute_rotations

compute_rotations(
    target_indices: list[list[int]],
    angle_reg: Qubits,
    target_reg: Qubits,
    rot_qbks_for_batch: list[RotationInterface],
    b_of_p: int,
) -> None

Apply rotation operations for each mux group using loaded angles.

Parameters:

Name Type Description Default
target_indices list[list[int]]

List of target qubit indices for each rotation.

required
angle_reg Qubits

Register containing loaded angle values.

required
target_reg Qubits

Target register containing qubits where rotations are applied.

required
rot_qbks_for_batch list[RotationInterface]

List of rotation Qubricks to use for each rotation.

required
b_of_p int

Bits of precision per rotation angle.

required

Raises:

Type Description
ValueError

If no rotation Qubrick is available for a rotation.

compute

compute(
    index_reg: Qubits,
    target_reg: Qubits,
    rotation_specs: list[RotationSpec] | None = None,
    mux_data: GeneralMultiplexedRotationViaQROMConfig
    | None = None,
    *,
    ctrl: Qubits | int = 0,
)

Compute circuit for multiplexed rotation using QROM-based implementation.

This method implements multiplexed rotations using QROM for efficient angle loading. It processes rotation specifications in batches, loads batched angle data from QROM, and applies rotations to target qubits according to the specifications.

The implementation requires that within each multiplexer group: 1. All rotations must use the same rotation Qubrick 2. All rotations must target the same qubit indices

Different multiplexer groups can have different target qubits and rotation Qubricks, allowing for flexibility while maintaining efficiency.

Note

This Qubrick supports Unary QROMs as well as Binary QROMs. If the QROM is unary it must have a is_unary attribute or you must set the is_unary flag in the init

Parameters:

Name Type Description Default
index_reg Qubits

The index register that selects which rotation to apply.

required
target_reg Qubits

The register where rotations are applied.

required
rotation_specs list[RotationSpec] | None

Specifications for each rotation.

None
mux_data GeneralMultiplexedRotationViaQROMConfig | None

Configuration data for the multiplexed rotation, including: - bits_of_precision: Precision bits per rotation angle - batches: Optional custom batching information. Ex. if you have three mux_groups ordered 0,1,2 then you can send in [[0],[1],[2]] which batches none of them together, or [[0,1],[2]] to just batch 0 and 1 together. [[0,1,2]] Would batch all of them together. Defaults to zero batching, which is [[0],[1],[2]] in the example. - lambda_val: Optional parameter for QROM optimization - ignore_last_batch_qrom_cleanup: Flag to skip final QROM cleanup

None
ctrl Qubits | int

Optional control for the entire operation.

0

Raises:

Type Description
ValueError

If mux_data is not provided or contains invalid specifications.

GeneralMultiplexedRotationViaQROMConfig dataclass

GeneralMultiplexedRotationViaQROMConfig(
    bits_of_precision: int,
    lambda_val: int | None = None,
    batches: list[list[int]] | None = None,
    ignore_last_batch_qrom_cleanup: bool = False,
)

Encapsulates configuration data for QROM-based multiplexed rotations.

This dataclass holds all the parameters needed to configure a QROM-based multiplexed rotation: precision settings, batching strategy, and other optimization parameters.

Attributes:

Name Type Description
bits_of_precision int

Number of bits used to represent each rotation angle.

lambda_val int | None

Optional lambda value parameter for the QROM implementation.

batches list[list[int]] | None

Optional custom batching of rotations by group indices. If None, default batching will be used based on group indices.

ignore_last_batch_qrom_cleanup bool

If True, skips the QROM cleanup step after the last batch for optimization purposes. Default is False.

GivensPPRs

GivensPPRs(**kwargs)

Bases: Qubrick

Applies the Givens rotation using Pauli Product Rotations.

This method implements the unitary:

\[ G(\theta) = e^{-i\frac{\theta}{2} (\mathbf{Y} \otimes \mathbf{X} - \mathbf{X} \otimes \mathbf{Y})} \]

which is equivalent to applying two single-qubit rotations, one with \( +\theta \) and the other with \( -\theta \).

compute

compute(
    rotation_encoding: float,
    target_reg: Qubits,
    ctrl: Qubits | int = 0,
) -> None

Apply the Givens rotation using Pauli Product Rotations.

Parameters:

Name Type Description Default
rotation_encoding float

Rotation angle in degrees.

required
target_reg Qubits

Two qubits to apply the rotation to.

required
ctrl Qubits | int

Control qubit (default: 0).

0

GivensRotationFusedAdder

GivensRotationFusedAdder(
    adder: Adder | None = None, **kwargs
)

Bases: Qubrick

Implements a Givens rotation using a fused quantum adder.

This method optimizes the standard two-adder approach by using a single fused adder, reducing qubit usage and circuit depth. Instead of requiring a full carry bit, it compresses the operation into a more compact form.

Note
  • This implementation requires only \( b - 1 \) bits for a \( b \)-bit approximation of \( \theta \).
  • The reduced bit count results from the fused adder structure, which avoids additional carry propagation.
  • However, current implementations may have basis states reversed, which requires correction.

Parameters:

Name Type Description Default
adder GidneyAdd or NaiveAdd

The quantum adder used to update phase values. Defaults to GidneyAdd(), but can be replaced with NaiveAdd() for alternative implementations.

None
**kwargs dict[str, Any]

Additional arguments passed to Qubrick.

{}

compute

compute(
    rotation_encoding: Qubits,
    target_reg: Qubits,
    ctrl: Qubits | int = 0,
) -> None

Apply the Givens rotation using phase gradient addition with a single fused adder.

Parameters:

Name Type Description Default
rotation_encoding Qubits

Rotation angle in degrees.

required
target_reg Qubits

Two qubits to apply the rotation to.

required
ctrl Qubits | int

Control qubit (default: 0).

0

GivensRotationTwoAdders

GivensRotationTwoAdders(
    adder: Adder | None = None, **kwargs
)

Bases: Qubrick

Implements a Givens rotation using two quantum adders.

This method applies a Givens rotation by using a phase gradient register and two quantum adders (GidneyAdd or NaiveAdd). It is particularly useful for multiplexed rotations, allowing for efficient conditional operations on quantum states.

Note
  • This implementation requires \( b + 1 \) bits for a \( b \)-bit approximation of \( \theta \).
  • The extra bit accounts for the additional carry bit required in the controlled addition.
  • If \( b \) bits are used, precision loss can occur due to truncation.

Parameters:

Name Type Description Default
adder GidneyAdd or NaiveAdd

The quantum adder used to update phase values. Defaults to GidneyAdd(), but can be replaced with NaiveAdd() for alternative implementations.

None
**kwargs dict[str, Any]

Additional arguments passed to Qubrick.

{}

compute

compute(
    rotation_encoding: Qubits,
    target_reg: Qubits,
    ctrl: Qubits | int = 0,
) -> None

Apply the Givens rotation using phase gradient addition with two adders.

Parameters:

Name Type Description Default
rotation_encoding Qubits

Rotation angle in degrees.

required
target_reg Qubits

Two qubits to apply the rotation to.

required
ctrl Qubits | int

Control qubit (default: 0).

0

GivensRZs

GivensRZs(**kwargs)

Bases: Qubrick

Implements a Givens rotation using RZ gates.

This class applies a Givens rotation using Pauli Z rotations (RZ) combined with Clifford gates. The operation targets two qubits and implements the unitary:

\[ G(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\theta) & \sin(\theta) & 0 \\ 0 & -\sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \]

This transformation can be decomposed into single-qubit RZ gates with Clifford conjugation:

  1. Apply a Clifford pre-rotation to transform the computational basis.
  2. Perform controlled RZ gates to introduce the phase shift.
  3. Apply a Clifford post-rotation to revert to the original basis.

This method is useful in multiplexed rotations and block encoding techniques.

Parameters:

Name Type Description Default
**kwargs dict[str, Any]

Additional arguments passed to Qubrick.

{}

compute

compute(
    rotation_encoding: float,
    target_reg: Qubits,
    ctrl: Qubits | int = 0,
) -> None

Apply the Givens rotation using Rz gates and Clifford basis change rotations.

Parameters:

Name Type Description Default
rotation_encoding float

Rotation angle in degrees.

required
target_reg Qubits

Two qubits to apply the rotation to.

required
ctrl Qubits | int

Control qubit (default: 0).

0

MultiplexedRotationInterface

Bases: Protocol

Protocol defining the interface for multiplexed rotation Qubricks.

MultiplexedRotation Qubricks should implement this protocol to ensure they have a consistent interface for controlling multiple rotations based on an index.

The protocol standardizes on a consistent parameter order with index register and target register as the first two parameters, followed by implementation-specific positional parameters, with the control parameter at the end. This allows for flexibility in how implementations handle their specific parameters.

compute

compute(
    index_reg: Qubits,
    target_reg: Qubits,
    rotation_specs: list[RotationSpec] | None,
    *,
    ctrl: Qubits | int = 0,
) -> None

Compute the multiplexed rotation.

RotationInterface

Bases: Protocol

Protocol defining the interface for rotation Qubricks.

Rotation Qubricks should implement this protocol to ensure they have a consistent interface for use in multiplexed rotations.

The protocol allows for additional custom arguments via **kwargs to accommodate different rotation implementations with specialized parameters.

compute

compute(
    rotation_encoding: Qubits | float,
    target_reg: Qubits,
    *,
    ctrl: Qubits | int = 0,
) -> None

Compute the rotation.

RotationSpec dataclass

RotationSpec(
    group_idx: int,
    mux_idx: int,
    target_qubits: list[int],
    angle: float,
    rot_qbk: RotationInterface | None = None,
)

Specification for a single rotation in a multiplexed rotation circuit.

This dataclass holds all information needed to define a single rotation operation within a multiplexed rotation circuit, including its group, multiplexing index, target qubits, angle, and optional custom rotation Qubrick.

Attributes:

Name Type Description
group_idx int

Index of the group this rotation belongs to. Groups allow batched execution of compatible rotations.

mux_idx int

Multiplexing select index - when the index register equals this value, the rotation will be applied.

target_qubits list[int]

List of qubit indices where the rotation should be applied.

angle float

Rotation angle in radians.

rot_qbk RotationInterface | None

Optional custom rotation Qubrick to use for this specific rotation. If None, the default rotation Qubrick from the parent class will be used.

SawtoothMultiplexor

SawtoothMultiplexor(max_index=None, **kwargs)

Bases: Qubrick

Unoptimized multiplexor from Fig. 5 in arXiv:1805.03662.

For some function, f, which returns an operator associated with an integer index, performs: \(\text{SELECT}|l\rangle|\psi\rangle \rightarrow |l\rangle f(l)|\psi\rangle\).

Parameters:

Name Type Description Default
max_index (Optional, int)

Integer corresponding to the maximum possible index that will be multiplexed over. Defaults to None.

None

Parameters:

Name Type Description Default
max_index (Optional, int)

Integer corresponding to the maximum possible index that will be multiplexed over. Defaults to None.

None
**kwargs dict[str, Any]

Other arguments to pass to the init.

{}

set_max_index

set_max_index(index_val) -> None

Set the max index.

Sets the max_index attribute to the input arg.

Parameters:

Name Type Description Default
index_val int

The max possible index to iterate over, given as an int.

required

compute

compute(
    index_reg: Qubits,
    multiplex_function: Callable,
    used_indices: list[int] | None = None,
    ctrl: Qubits | int = 0,
)

Compute the sawtooth multiplexing circuit.

We account for four cases, and some sub-cases:

  1. There is no data to load (exit the routine).
  2. Loading a single item does not require this hefty machinery.
  3. Loading two items without a control is similarly cheap to the single-item-case.
  4. All other cases will proceed with unary iteration, removing controls on the index register when possible.

The last case above has a couple of sub-cases:

(i). We (rarely, but sometimes) may determine we needn't control on any qubit for a particular index value; consider this a free lunch!

(ii). The uncontrolled version of this routine is slightly cheaper than the controlled case. We actually have additional small sub-cases here: (a). If we determine we only need to control on a single qubit for a particular index, we can directly apply that element and exit the for loop. (b). In all other cases, the uncontrolled case uses one fewer ancilla than the controlled case (and also one fewer elbow).

(iii). After accounting for one fewer qubit for the uncontrolled case, the controlled and uncontrolled case than perform the same logic.

Parameters:

Name Type Description Default
index_reg Qubits

Qubit register storing the values over which the multiplexing is performed.

required
multiplex_function callable

A function which takes an index and then index register and performs the operation associated with that index.

required
used_indices list

List of indices corresponding to terms where the operators are actually being applied.

None
ctrl (int, Qubits)

Control for \(\text{SELECT}\). Defaults to 0.

0

ZeroAncMultiplexor

ZeroAncMultiplexor(**kwargs)

Bases: Qubrick

Most naïve version of multiplexing possible.

For some function, f, which returns an operator associated with an integer index performs: \(\text{SELECT}|l\rangle|\psi\rangle \rightarrow |l\rangle f(l)|\psi\rangle\).

compute

compute(
    index_reg: Qubits,
    multiplex_function: Callable,
    used_indices: list[int] | None = None,
    ctrl: Qubits | int = 0,
)

Compute the serial multiplexing circuit.

Parameters:

Name Type Description Default
index_reg Qubits

Qubit register storing the values over which the multiplexing is performed.

required
multiplex_function callable

A function which takes an index and then index register and performs the operation associated with that index.

required
used_indices list

List of indices corresponding to terms where the operators are actually being applied.

None
ctrl (int, Qubits)

Control for \(\text{SELECT}\). Defaults to 0.

0

get_default_multiplex_function

get_default_multiplex_function(target_reg, data)

Factory to generate the default multiplexing function.

This default function assumes two kinds of input data
  • A PauliSum object.
  • A list of integers (to be loaded by, say, a QROM).

This function acts as a factory to return a multiplexing function that applies the element in data at the position given by the index onto the target register controlled on the index qubits being in the binary state representing the index value.

Note

All multiplexing functions must adhere to the contract of having a signature of index (int), index_qubits (Qubits), and ctrl (Qubits or int), such that the multiplexors that call them are promised a certain signature.

Parameters:

Name Type Description Default
target_reg Qubits

The target register to apply the operation on

required
data Union[PauliSum, List, Dict]

The object storing the operation that should be applied at the given index. Typically a PauliSum, List, or Dict. Examples also include a List/Dict of PauliMasks or a List/Dict of bitmasks

required

Returns:

Type Description
Callable

multiplexing function

get_multiplex_function_of_indexed_callables

get_multiplex_function_of_indexed_callables(
    target_reg, data
)

Factory to generate a multiplexing function when data contains a list of callable operators.

This function acts as a factory to return a multiplexing function that takes the callable given by the first element at the position in "data" given by "index" and calls that operation on the target register controlled on the index qubits being in the binary state representing the index value.

Note

All multiplexing functions must adhere to the contract of having a signature of index (int), index_qubits (Qubits), and ctrl (Qubits or int), such that the multiplexors that call them are promised a certain signature.

Parameters:

Name Type Description Default
target_reg Qubits

The target register to apply the operation on

required
data List or Dict

The List or Dict storing the callables (as the first element in the Tuple) and any kwargs needed for that callable (as the second element in the Tuple)

required

Returns:

Type Description
Callable

callable multiplexing function

BinaryToUnaryComputation

BinaryToUnaryComputation(
    **kwargs,
) -> BinaryToUnaryUncomputation

Factory function that creates a BinaryToUnaryUncomputation instance with dagger=True.

This is an alias to make the binary-to-unary conversion more intuitive by avoiding the double negative of "Uncomputation" with dagger=True. This function performs a binary-to-unary conversion by internally using BinaryToUnaryUncomputation with the dagger operation.

Parameters:

Name Type Description Default
**kwargs dict[str, Any]

Additional keyword arguments to pass to the BinaryToUnaryUncomputation constructor. The 'dagger' parameter will be set to True regardless of what is passed.

{}

Returns:

Type Description
BinaryToUnaryUncomputation

An instance configured for binary-to-unary conversion.