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
, orA function
unitaries(i, operand)
performing some in-place operation onoperand
depending on a nonnegative integer indexi
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 whenunitaries
is a callable.
See also
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}\).