Skip to content
Construct PsiQDK Algorithms

multiplexing

multiplexing

BinaryTreeMultiplexor

BinaryTreeMultiplexor(**kwargs)

Bases: Qubrick

Optimized multiplexing based on Fig. 7 in "Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity" (arXiv:1805.03662 ⧉).

index_reg instance-attribute

index_reg: Incomplete

multiplex_function instance-attribute

multiplex_function: Incomplete

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 register. Defaults to 0.

0

ConditionallyCleanMultiplexor

ConditionallyCleanMultiplexor(name=None, **kwargs)

Bases: Qubrick

Multiplexor utilizing conditionally clean construction as presented in "Rise of conditionally clean ancillae for efficient quantum circuit constructions" (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 utilising 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 register. Defaults to 0.

0

OneAncMultiplexor

OneAncMultiplexor(**kwargs)

Bases: Qubrick

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

Circuit shown in Fig. (1.a) in "Trading T gates for dirty qubits in state preparation and unitary synthesis" (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 auxiliary qubit.

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 register. Defaults to 0.

0

SawtoothMultiplexor

SawtoothMultiplexor(max_index=None, **kwargs)

Bases: Qubrick

Unoptimized multiplexor from Fig. 5 in "Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity" (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
**kwargs dict[str, Any]

Other arguments to pass to the init.

{}

max_index instance-attribute

max_index: Incomplete = max_index

index_reg instance-attribute

index_reg: Incomplete

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.

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:

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

4b) The uncontrolled version of this routine is slightly cheaper than the controlled case. We actually have additional small sub-cases here: (i) 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. (ii) In all other cases, the uncontrolled case uses one fewer auxiliary qubit than the controlled case (and also one fewer elbow).

4c) 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 register. 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 register. 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.