qrisp.inner_LCU#

inner_LCU(operand_prep, state_prep, unitaries, num_unitaries=None, oaa_iter=0)[source]#

Core implementation of the Linear Combination of Unitaries (LCU) protocol without Repeat-Until-Success (RUS) protocol. The LCU method is a foundational quantum algorithmic primitive that enables the application of a non-unitary operator \(A\), expressed as a weighted sum of unitaries \(U_i\) as \(A=\sum_i\alpha_i U_i\), to a quantum state, by embedding \(A\) into a larger unitary circuit. This is central to quantum algorithms for Hamiltonian simulation, Linear Combination of Hamiltonian Simulation (LCHS), Quantum Linear Systems (e.g. HHL algorithm), Quantum Signal Processing (QSP), and Quantum Singular Value Transformation (QSVT).

This function implements the prepare-select-unprepare structure, also known as block encoding:

\[\mathrm{LCU} = \mathrm{PREPARE}^\dagger \cdot \mathrm{SELECT} \cdot \mathrm{PREPARE}\]
  • PREPARE: Prepares an ancilla quantum variable in a superposition encoding the normalized coefficients \(\alpha_i\geq0\) of the target operator

\[\mathrm{PREPARE}|0\rangle=\sum_i\sqrt{\frac{\alpha_i}{\lambda}}|i\rangle\]
  • SELECT: Applies the unitary \(U_i\) to the input state \(\ket{\psi}\), controlled on the ancilla variable being in state \(|i\rangle\).

\[\mathrm{SELECT}|i\rangle|\psi\rangle=|i\rangle U_i|\psi\rangle\]
  • PREPARE\(^\dagger\): Applies the inverse prepartion to the ancilla.

Note

The LCU protocol is deemed successful only if the ancilla variable is measured in the \(\ket{0}\) state, which occurs with a probability proportional to \(\frac{\langle\psi|A^{\dagger}A|\psi\rangle}{\lambda^2}\) where \(\lambda=\sum_i\alpha_i\). This function does not perform the measurement; it returns the ancilla variable and the transformed target variable.

For a complete implementation of LCU with the Repeat-Until-Success protocol, see LCU().

For more details on the LCU protocol, refer to Childs and Wiebe (2012), or related seminars provided by Nathan Wiebe.

Parameters:
operand_prepcallable

A function preparing the input state \(\ket{\psi}\). This function must return a QuantumVariable (the operand).

state_prepcallable

A function preparing the coefficient state from the \(\ket{0}\) state. This function receives a QuantumFloat with \(\lceil\log_2m\rceil\) qubits for \(m\) unitiaries \(U_0,\dotsc,U_{m-1}\) as argument and applies

\[\text{PREPARE}\ket{0} = \sum_i\sqrt{\frac{\alpha_i}{\lambda}}\ket{i}\]
unitarieslist[callable] or callable
Either:
  • A list of functions performing some in-place operation on operand, or

  • A function unitaries(i, operand) performing some in-place operation on operand depending on a nonnegative integer index i specifying the case.

num_unitariesint, optional

Required when unitaries is a callable to specify the number \(m\) of unitaries.

oaa_iterint, optional

The number of iterations of oblivious amplitude amplification to perform. The default is 0.

Returns:
tuple(QuantumFloat, QuantumVariable)
  • case_indicator : Ancilla variable encoding which unitary was selected.

  • operand : Target quantum variable after the LCU operation.

Raises:
TypeError

If unitaries is not a list or a callable.

ValueError

If num_unitaries is not specified when unitaries is a callable.

See also

LCU

Full LCU implementation using the RUS protocol.

view_LCU

Generates the quantum circuit for visualization.

Examples

As a first example, we apply the non-unitary operator \(A\) to the operand \(\ket{\psi}=\ket{1}\) where

\[\begin{split}A = \begin{pmatrix}1 & 1\\ 1 & 1\end{pmatrix} = \begin{pmatrix}1 & 0\\ 0 & 1\end{pmatrix} + \begin{pmatrix}1 & 1\\ 1 & 1\end{pmatrix} = I + X\end{split}\]

That is,

\[A = \alpha_0U_0 + \alpha_1U_1\]

where \(\alpha_0=\alpha_1=1\), and \(U_0=I\), \(U_1=X\).

Accordingly, we define the unitaries

from qrisp import *

def U0(operand):
    pass

def U1(operand):
    x(operand)

unitaries = [U0, U1]

and the state_prep function implementing

\[\text{PREPARE}\ket{0} = \frac{1}{\sqrt{2}}\left(\ket{0}+\ket{1}\right)\]
def state_prep(case):
    h(case)

Next, we define the operand_prep function preparing the state \(\ket{\psi}=\ket{1}\)

def operand_prep():
    operand = QuantumVariable(1)
    x(operand)
    return operand

Finally, we apply inner_LCU

>>> case_indicator, operand = inner_LCU(operand_prep, state_prep, unitaries)
>>> operand.qs.statevector()

As result we obtain the state

\[\frac12((\ket{0}+\ket{1})\ket{0}_{\text{case}} - \frac12(\ket{0}-\ket{1})\ket{1}_{\text{case}}\]

If we now measure the case_indicator in the state \(\ket{0}\), the operand will be in state \(\frac{1}{\sqrt{2}}(\ket{0}+\ket{1})\) implementing (up to rescaling) the non-unitary operator \(A\) acting on the input state \(\ket{\psi}=\ket{1}\).