BlockEncoding.from_lcu#

classmethod BlockEncoding.from_lcu(coeffs: ndarray[tuple[Any, ...], dtype[number]], unitaries: list[Callable[[...], Any]], num_ops: int = 1, is_hermitian: bool = False) BlockEncoding[source]#

Constructs a BlockEncoding using the Linear Combination of Unitaries (LCU) protocol.

For an LCU block encoding, consider a linear combination of unitaries:

\[O = \sum_{i=0}^{M-1} \alpha_i U_i\]

where \(\alpha_i\) are real non-negative coefficients such that \(\sum_i \alpha_i = \alpha\), and \(U_i\) are unitaries acting on the same operand quantum variables.

The block encoding unitary is constructed via the LCU protocol:

\[U = \text{PREP} \cdot \text{SEL} \cdot \text{PREP}^{\dagger}\]

where:

  • SEL (Select, in Qrisp: q_switch) applies each unitary \(U_i\) conditioned on the auxiliary variable state \(\ket{i}_a\):

\[\text{SEL} = \sum_{i=0}^{M-1} \ket{i}\bra{i} \otimes U_i\]
  • PREP (Prepare) prepares the state representing the coefficients:

\[\text{PREP} \ket{0}_a = \sum_{i=0}^{M-1} \sqrt{\frac{\alpha_i}{\alpha}} \ket{i}_a\]
Parameters:
coeffsArrayLike

1-D array of non-negative coefficients \(\alpha_i\).

unitarieslist[Callable]

List of functions, where each U(*operands) applies a unitary transformation in-place to the provided quantum variables. All functions must accept the same signature and operate on the same set of operands.

num_opsint

The number of operand quantum variables. The default is 1.

is_hermitianbool

Indicates whether the block-encoding unitary is Hermitian. The default is False. Set to True, if all provided unitaries are Hermitian.

Returns:
BlockEncoding

A BlockEncoding using LCU.

Raises:
ValueError

If any entry in coeffs is negative, as the LCU protocol only supports positive coefficients.

Notes

  • Normalization: The block-encoding normalization factor is \(\alpha = \sum_i \alpha_i\).

Examples

from qrisp import *
from qrisp.block_encodings import BlockEncoding
def f0(x): x-=1
def f1(x): x+=1
BE = BlockEncoding.from_lcu(np.array([1., 1.]), [f0, f1])

@terminal_sampling
def main():
    return BE.apply_rus(lambda : QuantumFloat(2))()

main()
# {1.0: 0.5, 3.0: 0.5}