qrisp.LCU#
- LCU(*trial_args)#
Full implementation of the Linear Combination of Unitaries (LCU) algorithmic primitive using the Repeat-Until-Success (RUS) protocol.
This function constructs and executes the LCU protocol using the provided state preparation function and unitaries. It utilizes the
qrisp.jasp.RUS()
decorator from Jasp to handle the repeat-until-success mechanism, which repeatedly applies the LCU operation until the ancilla register is measured in the \(|0\rangle\) state, indicating a successful implementation. The LCU algorithm enables the implementation of linear combinations of unitary operations on a quantum variable by probabilistically projecting onto the desired transformation. The terminal_sampling decorator is utilized to evaluate the LCU.For more details on the LCU primitive, refer to Childs and Wiebe (2012).
For more information on the inner workings of this LCU implementation, see
inner_LCU()
.- 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:
- QuantumVariable
A variable representing the output state \(A\ket{\psi}\) after successful application of LCU to the input state \(\ket{\psi}\).
- Raises:
- TypeError
If
unitaries
is not a list or 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}\]This 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 LCU
@terminal_sampling def main(): qv = LCU(operand_prep, state_prep, unitaries) return qv
and simulate
>>> main() {0: 0.5, 1: 0.5}
As a second example, we apply the operator
\[\cos(H) = \frac{e^{iH}+e^{-iH}}{2}\]for some Hermitian operator \(H\) to the input state \(\ket{\psi}=\ket{0}\).
First, we define an operator \(H\) and unitaries performing the Hamiltonian evolutions \(e^{iH}\) and \(e^{-iH}\). (In this case, Trotterization will perform Hamiltonian evolution exactly since the individual terms commute.)
from qrisp import * from qrisp.operators import X,Y,Z H = Z(0)*Z(1) + X(0)*X(1) def U0(operand): H.trotterization(forward_evolution=False)(operand) def U1(operand): H.trotterization(forward_evolution=True)(operand) unitaries = [U0, U1]
Next, we define the
state_prep
andoperand_prep
functionsdef state_prep(case): h(case) def operand_prep(): operand = QuantumVariable(2) return operand
Finally, we apply LCU
@terminal_sampling def main(): qv = LCU(operand_prep, state_prep, unitaries) return qv
and simulate
>>> main() {3: 0.85471756539818, 0: 0.14528243460182003}
Let’s compare to the classically calculated result:
>>> A = H.to_array() >>> from scipy.linalg import cosm >>> print(cosm(A)) [[ 0.29192658+0.j 0. +0.j 0. +0.j -0.70807342+0.j] [ 0. +0.j 0.29192658+0.j 0.70807342+0.j 0. +0.j] [ 0. +0.j 0.70807342+0.j 0.29192658+0.j 0. +0.j] [-0.70807342+0.j 0. +0.j 0. +0.j 0.29192658+0.j]]
That is, starting in state \(\ket{\psi}=\ket{0}=(1,0,0,0)\), we obtain
>>> result = cosm(A)@(np.array([1,0,0,0]).transpose()) >>> result = result/np.linalg.norm(result) # normalise >>> result = result**2 # compute measurement probabilities >>> print(result) [0.1452825+0.j 0. +0.j 0. +0.j 0.8547175-0.j]
which are exactly the probabilities we obsered in the quantum simulation!