Below we briefly present the problem at hand, as well as its mathematical formulationModern portfolio optimization [1] is the process of allocating a portfolio of financial assets optimally, according to some predetermined goal. Usually, the goal is to maximize the potential return while minimizing the financial risk of the portfolio. In similar to many other real-world problems, one can express the portfolio optimization problem as a set of linear equations. In this demo, we show how a quantum linear solver, the HHL algorithm [2], or its hybrid variance [3], can be used to solve this problem. We demonstrate how using functional modeling can be leveraged for constructing different models and obtaining different quantum programs for a given set of constraints.*In this demo we focus on the case of continuous variable for the allocation vector.The scenario of discrete allocation falls into the domain of Combinatorial Optimization Problems, for which one can employ the QAOA (Quantum Approximate Optimization Ansatz) approach.*
1.1 Portfolio optimization problem as a linear equation
As a first step, we have to model the problem mathematically:
A portfolio is built from a pool of N financial assets.
The assets’ return values are random variables, whose statistical properties are represented by the expected return vector μ∈RN and a covariance matrix Σ∈RN×RN.
The prices of the assets today are represented by the prices vector p∈RN.
The portfolio has an allocation vector w∈RN, representing the weight of each asset in the portfolio —
this is the variable we would like to find.
Finally, the portfolio problem has two constants: a desired total return R, and a total budget B.
The task is to find a solution for the allocation vector w which minimizes the riskwminwT⋅Σ⋅w,under the constraints of\begin{eqnarray}
\text{getting the desired total return: } R&=\vec{\mu}^T\cdot\vec{w},\\
\text{satisfying the total known budget: } B&=\vec{p}^T\cdot\vec{w}
\end{eqnarray}Writing this problem as a constrained minimization problem in a continuous space, we get a set of linear equations:00μ00pμTpTΣν1ν2w=RB0,(1)where ν1,2 are Lagrange multipliers.comment: one can rescale the problem and set the total budget to B=1.Equation (1) is of the form of a linear equation:A⋅x=b(2)
The HHL algorithm [2] (after Harrow, Hassidim, and Lloyd) is a quantum linear solver, treating problems which can be defined by Eq. (1).The basic implementation assumes that the b is a normalized vector ∣b∣=1 of size 2n, and that A is an Hermitian matrix of size 2n×2n, whose eigenvalues are in the interval [0,1).For the mathematical introduction we will follow these assumptions, whereas in the following sections we show how to tweek the quantum model in order to relax them.The basic HHL algorithm contains 4 functional blocks:
State Preparation for the vector b: Let us assume that b is of size 2n, then:
ֿ∣0⟩nSPi=0∑2n−1bi∣i⟩n.(3)
Quantum Phase Estimation (QPE) for the unitary e2πiA: This quantum block approximates the eigenvalues of the matrix A, {λi}i=12n, into a register of size m. If we write the vector b in the basis of eigenvectors of A, ∑i=02n−1bi∣i⟩n=∑j=02n−1βj∣ψj⟩n, then the QPE stage gives
∣0⟩mj=0∑2n−1βj∣ψj⟩nQPE(e2πiA)j=0∑2n−1βj∣λ~j⟩m∣ψj⟩n,(4)where λ~=2m1∑k=02m−1λ~(k)2k with λ~(k) being the state of the k-th qubit. Thus, m is the precision of the binary representation of λ~, which in turn is an approximation of the actual eigenvalue of A, λ.
3. Eigenvalue Inversion: This quantum block adds an extra qubit∣0⟩j=0∑2n−1βj∣λ~j⟩m∣ψj⟩nEig.Inversion∣1⟩(j=0∑2n−1λ~jCβj∣λ~j⟩m∣ψj⟩n)+∣0⟩(j=0∑2n−11−λ~j2C2βj∣λ~j⟩m∣ψj⟩n),(5)where C is a normalization factor which gurrentees that the amplitudes are not larger than4. Inverese QPE for the unitary e2πiA: This operation acts on the QPE register of size m and aims to return the phase registers to zero.The final state is:∣0⟩m∣1⟩(j=0∑2n−1λ~jCβj∣ψj⟩n)+∣0⟩m∣0⟩(j=0∑2n−11−λ~j2C2βj∣ψj⟩n).(6)
Looking at Eq. (6) we can see that the result vector x is coupled to the indicator state ∣1⟩ (up to normalization with the known constant C):j=0∑2n−1λjCβjψj=Cx.(7)In order to obtain the solution from the quantum circuit we must measure the complete statevector, however, this exponential readout procedure halts quantum advantage. In the framework of portfolio optimization, it was suggested that one can use a swap-test [4] to compare the result to some guess of the solution.Let us write the quantum state at the final stage of the HHL algorithm, Eq. (6), as C∣x∣∣1⟩∣x~^⟩n+α∣0⟩∣ζ⟩n, where we neglect the QPE register, and α∣ζ⟩ is some non-normalized state.For the swap-test we add an extra test qubit ∣0⟩test and we need to prepare a quantum register with the guessed solution ∣y^⟩.Applying the swap-test gives the final state:2C∣x∣∣1⟩[∣0⟩test(∣x~^,y^⟩2n+∣y^,x~^⟩2n)+∣1⟩test(∣x~^,y^⟩2n−∣y^,x~^⟩2n)]+non-interesting state(8)From the above equation we find that the probability of measuring 1 on the HHL indicator qubit and 0 on the test qubit isP(test=0,indictor=1)=2C2∣x∣2(1+∣⟨x~^∣y^⟩∣2)=2P(indictor=1)(1+∣⟨x~^∣y^⟩∣2).(9)From this equation one can determine the desired overlap ∣⟨x~^∣y^⟩∣2.
The hybrid approach [3] comes to facilitate the Eigenvalue Inversion function which is not easy to implement.This quantum block can be obtained using multi-controlled RY rotations:∣0⟩∣λ⟩mmulti−controlled−RY(2arcsin(C/λ))λC∣1⟩∣λ⟩m+1−λ2C2∣0⟩∣λ⟩m.(10)However, to apply this procedure we should have a quantum block which caclulates arcsinC/λ, and such general arithmetics requires many qubits.Another approach is to use a generic amplitude loading for the function f(x)=C/x, but this procedure contains exponential number, 2m, of multi-controlled rotations (the gray-code approch might reduce the gate count by some factor, but the total gate count is still O(2m)).The hybrid approach containes three steps:
QPE of e2πiA on the State Preparation of b: This is simply the first two steps of the HHL algorithm, corresponding to the final state ∑j=02n−1βj∣λ~j⟩m∣ψj⟩n.
Classical post-process: We analyze the results in the context of the following two facts: (i) The matrix A has 2n eigenvalues, whereas the QPE gives approximation of these over 2m different values. (ii) Not all the eigenvalues of A are relevant to the solution, but only eigenvalues such that βj/λj>ϵ, with ϵ being some user-defined tolarance.
Based on these two points we can try to find a set of relevant eigenvalues {λ~s}s=1r to our problem.
A full HHL routine: We now apply a usual HHL algorithm, where the Eigenvalue Inversion block is implemented by hard-coded multi-controlled-RY rotation according to the specific values found in the previous step. Moreover, we can represent the relevant eigenvalues with a smaller binary represantation of size m′<m.
Then in the HHL we use a QPE with this smaller size, and apply the controlled rotations with angles which are calculated with higher precision.
We define two eigenvalue inversion functions, one which rotates over all eigenvalues, and another rotating on a reduced set of these. In addition, we take into account an affine transformation of the matrix, and its resulting transformation on the eigenvalues.The affine transformation is necessary as the QPE is applied on a matrix whose eigenvalues are in [0,1). We can define a transformation A=γAqpe+δ with a rescaling factor γ and a shift value δ.The eigenvalues of the original matrix are transformed in a similar way. In the HHL algorithm, we use QPE to store the eigenvalues of the transformed matrix Aqpe, but perform eigenvalue inversion for the original matrix A. Thus, inversion of an eigenvalue corresponds to the following operation:∣0⟩∣λ⟩meig−inversionγλ+δC∣1⟩∣λ⟩m+1−(γλ+δC)2∣0⟩∣λ⟩m.The normalization parameter C gurentees that the amplitudes’ norm is ≤1.
First, we define a quantum function that performs a simple eigenvalue inversion with Classiq built-in amplitude loading function, assign_amplitude_table.
Partial eigenvalue inversion, with prescribed values
Here, we apply hard-coded controlled rotations, as described in Sec.
Moreover, we take into account a situation where the rotation angles are given with higher precision than the eigenvalues stored in the phase variable returning from the QPE.
from classiq.qmod.symbolic import asin, floor@qfuncdef reduced_hardcoded_eig_inversion( gamma: CReal, delta: CReal, c_param: CReal, eigs: CArray[CReal], # list of eigenvalues to rotate phase: QNum, indicator: Output[QBit],) -> None: allocate(1, indicator) integer_phase = QNum("integer_phase", phase.size, False, 0) bind(phase, integer_phase) repeat( eigs.len, lambda index: control( integer_phase == floor( eigs[index] * 2**phase.size ), # currently control only supports int lambda: RY(2 * asin(c_param / (gamma * eigs[index] + delta)), indicator), ), ) bind(integer_phase, phase)
We consider a specific portfolio optimization example for the demonstration. We focus on a simple case of 2 assets, and define the corresponding set of linear equations (a 2×2 problem is represented by n_unitary=2 qubits, for more complex examples see Sec. 8):
We shall normalize the vector b before loading it into a quantum register. In addition, the user can control the functional error of this function block.
As we will see below, the HHL algorithm assumes that we have some bound on the maximal eigenvalue (in absolute value) of the matrix.For the sake of demonstration, we will perform a full classical eigenvalue decomposition.This also serves us for verifying the QPE in Step
4.3 Preparing the unitary from the matrix A for the Quantum Phase Estimation
The QPE function gets a phase quantum variable, whose size refers to the precision of the eigenphases estimations. We set it with to
(note that this number corresponds to the condition number of the matrix κ≡λmax/λmin).
QPE_SIZE = 4
As explained in Sec. 3, we have to classically transform our matrix to have eigenvalues in [0,1), and apply an inverse transformation when performing the eigenvalue inversion block. We take the following matrix transformation:Aqpe=(1−2qpe−size)(A+λmax)/2λmax.
import scipymat_shift = w_max # assures only positive eivenvaluesmat_rescaling = (1 - 1 / 2**QPE_SIZE) / ( 2 * w_max) # assures eigenvalues in [0,1-1/2^QPE_SIZE]a_mat_qpe = (a_mat + np.identity(n_unitary) * mat_shift) * mat_rescaling# rescaled and shifted matrixw_min = w_max / ( 2**QPE_SIZE - 1) # this is the minimal eigenvalue which can be resolved by the QPEprint("the maximal eigenvalue found:", w_max)print("the minimal resolved eigenvalue with qpe of size", QPE_SIZE, ":", w_min)
Output:
the maximal eigenvalue found: 1.547651922176089 the minimal resolved eigenvalue with qpe of size 4 : 0.10317679481173926
The transformation to Aqpe, whose eigenvalues are in [0,1), implies the inverse transformation for the eigenvalue inversion with: γ=1/mat_rescaling, δ=−mat_shift, and C=w_min respectively.
For the demonstration we use the exact unitary, namely, we calculate classically e2πiAqpe. We then use the built-in quantum function unitary for decomposing the unitary into quantum gates.Note that this procedure is not scalable, as the decomposition is exponential in the number of qubits. In addition, the classical calculation is equivalent to solving the linear equation. Yet, this approach is good for small-scale problems.One relevant way to implement the unitary is with Hamiltonian simulation, see Sec. (8)—
Step 2: Post-Processing the results to get data for the eigenvalue inversion
To analyze the results we generate the following 4 lists:
qpe_eigs: the values of the QPE eigenvalues λqpe,
original_eigs: the values of the original matrix eigenvalues,
eigs_prob: the probability of each eigenvalue, this corresponds to βj2 in Eq. (4).
projected_x: the effect of each phase on the solution x, given by βj/λj.
qpe_eigs = np.array([sample.state["phase_result"] for sample in res_qpe.parsed_counts])original_eigs = np.array([mat_rescaling ** (-1) * eig - mat_shift for eig in qpe_eigs])eigs_prob = np.array([sample.shots / NUM_SHOTS for sample in res_qpe.parsed_counts])projected_x = np.sqrt(eigs_prob) / original_eigs
Let us examine the measured phases and their contribution to the solution vector x.
import matplotlib.pyplot as pltfig, axs = plt.subplots(2)fig.suptitle("Distribution of eigenvalues")axs[0].plot(original_eigs, eigs_prob, color="red", marker="o", linestyle="None")axs[0].set_ylabel("$P(\\lambda)$")axs[1].plot( original_eigs, np.abs(projected_x), color="blue", marker="o", linestyle="None")axs[1].set_ylabel("$\\sqrt{P(\\lambda)}/|\\lambda|$")axs[1].set_xlabel("$\\lambda$")
Output:
Text(0.5, 0, '$\\lambda$')
What can we learn from the data?We are dealing with a 4×4 matrix, thus it has only 4 eigenvalues.The QPE tries to approximate this with binary representation of size QPE_SIZE=4, resulting in 24=16 values. We can take the two most significant eigenvalues for which we will apply the eigenvalue inversion.First, find the the 4 eigenvalues.From the upper graph we can see 4 local maxima, which can be attributed for the four eigenvalues in our problem.
Then, from the lower graph we can choose, out of the 4 eigenvalues, the ones with the highest projection on the solution.Take the two most significant ones:
Comment: the effect of trimming the LSB cannot be observed from the plot as it is 0 in our case. To see the effect one should look at the distribution of eigenvalues obtained from a QPE of size 3.
Synthesizing the quantum model and executing the resulting quantum program
We can pass different constraints and preferences to the synthesis engine.For the demonstration let us assume that we are interested in a quantum circuit with up to max_width=12 qubits, and ask for optimization over depth.
Below we post-process the results to find the overlap between the HHL solution and the classical one.
We are interested in the probability of that the test qubit being at state 0, under the condition that the indicator is measured at state
Quantum program link: https://platform.classiq.io/circuit/36pbg8VpPK9AThkVk6BpgjfDlEj Fidelity between basic HHL and classical solutions: 0.951661902861773
import pandas as pdnames = ["hyb. HHL C (depth)", "hyb. HHL C (width)", "basic HHL"]df = pd.DataFrame( list(zip(widths, depths, cx_counts, fidelities)), columns=["num. qubits", "depth", "cx_count", "fidelity"], index=names,)df
num. qubits
depth
cx_count
fidelity
hyb. HHL C (depth)
12
416
260
0.909921
hyb. HHL C (width)
9
443
268
0.909921
basic HHL
10
477
304
0.951662
Already at this small scale problem we can see reduced properties of the hybrid HHL compared to the basic implementation.This should be more pronounced in larger problems (the properties of the QPE in step 1 of the hybrid approach are not taken into account here).
As mentioned above, the correct way to implement the unitary on which we apply the QPE is Hamiltonian simulation.One way to apply this quantum function is with the built-in exponentiation_with_depth_constraint function.This includes another level of approximation to our algorithm.The exponentiation is implemented with Trotter-Suzuki product.Estimating the functional error of such implementation is a hard problem. An indirect way to manage the error is by increasing the depth of Exponentiation block in order to allow the engine to explore higher order Trotter-Suzuki products.Another option is to use suzuki_trotter functions directly.Both functions are then should be plugged it into qpe or qpe_flexible function, instead of plugging the built-in unitary; see HHL notebook.
In Ref.[5] it was shown how one can use an hybrid quantum-classical routine in order to increase the precision in the eigenvalue resolution in step 1 of the hybrid HHL.The algorithm is based on running this step with various evolution coefficients γ in eiγ2πAqpe, and find the optimal one (in a sense that we increase the resolution in the eigenvalue estimation, while using the same qpe_size ).Note that while this algorithm increases eigenvalue precision it increases the evolution coeffecient, as typically γ>1.This in turn requires more resources for performing well-approximated Exponentiation.
In Ref.[5] it was shown how one can use an iterative-QPE approach to perform step 1 in the hybrid HHL routine.This allows to get an estimate of the eigenvalues with only one qubit, instead of qpe_size qubits.
In the problem above we have only 2 assets, which results in a problem with matrix size 2+2=4. In general, a case of N assets will result in a matrix of size N+2 (see Eq. (1)). In case that log(N+2) is not an integer we can complete the matrix dimension to the closest 2n with an identity matrix.The vector b will be completed with zeros.(A00I)(b0)=(x0).This procedure shall not affect the swap-test part, all need to be done is to load a guessed solution as (y,0).