Path: blob/main/quantum-applications-to-finance/solutions/03_qchop_solutions.ipynb
1163 views
Quantum Portfolio Optimization
Overview
This notebook is the third in the "Quantum Applications to Finance" series. In this notebook you will perform a fundamental financial task - portfolio optimization. You will learn how to setup the optimization problem, solve it using two different quantum methods, add constraints, and explore methods for scaling up such approaches. This notebook will also explore the state-of-the-art Q-CHOP algorithm developed by Infleqtion (also contributors to this notebook) and JPMorgan Chase demonstrating how the fundamental examples you code up with CUDA-Q can be extended towards useful real-world applications.
What you'll do:
Setup a QUBO portfolio optimization problem
Map the problem to a quantum Hamiltonian
Solve the problem with a QAOA implementation in CUDA-Q
Solve the problem with an adiabatic implementation with CUDA-Q dynamics
Add constraints to the optimization problem
Learn how Q-CHOP can improve quantum portfolio optimization
Explore how multiple QPUs can help scale up portfolio sampling
CUDA-Q Syntax you'll use:
quantum kernel function decoration:
@cudaq.kernelqubit initialization:
cudaq.qvectorandcudaq.qubitquantum gates:
x,h,t,_.ctrl,cudaq.register_operationCUDA-Q spin operators:
spinexecute a kernel:
sample,get_state,observeAnalyze sample results
.countRun
dynamicsbackend withevolveRun
mqpubackend withsample_async
Pre-requisites: Learners should have familiarity with Jupyter notebooks and programming in Python and CUDA-Q. It is assumed the reader has some familiarity already with quantum computation and is comfortable with braket notation and the concepts of qubits, quantum circuits, measurement, Hamiltonians, and circuit sampling. The CUDA-Q Academic course entitled "Quick Start to Quantum Computing with CUDA-Q" provides a walkthrough of this prerequisite knowledge if the reader is new to quantum computing and CUDA-Q or needs refreshing. While not required, the notebook titled "Lab 1 - Overview: Max cut with QAOA on a small graph" provides additional context for the Quantum Approximate Optimization Algorithm (QAOA) covered here.
First run the cell below to install the correct packages. You may need to restart your kernel.
Introduction
Portfolio optimization is foundational for modern finance. The aim of portfolio optimization is to select the best combination of assets to maximize returns while minimizing risk. Qualitatively, this looks like the portfolio in the leftmost point of the "universe" of potential portfolios pictured below.

Seminal to this field is Harry Markowitz's Modern Portfolio Theory (MPT), introduced in the 1950s. Markowitz's groundbreaking work established the framework for diversification, demonstrating that the risk of a portfolio is not merely the sum of the risks of its individual assets. Instead, by carefully selecting a mix of assets with varying correlations, investors can reduce the overall portfolio risk.
Even with such insights, identifying good portfolios is extremely challenging when there are seemingly endless choices for investment selections. Moreover, fund managers can add additional complexity by changing the weights of each portfolio or considering schemes where stock inclusion is not binary, but can include a number of different strategies.
Regardless of the setup, the biggest challenge for portfolio optimization is the combinatorial scaling of the universe of potential portfolios. It is well within the bounds of practicality to consider, for example, a portfolio constructed from 20 Fortune 100 stocks which is selected from 100 choose 20 or quintillions () of potential portfolios. This is where quantum computing might be able to help.
The idea is to encode the problem into a quantum state consisting of qubits. A clever algorithm could leverage the properties of superposition, entanglement, and interference to evolve this state towards a final state from which quality portfolios can be sampled with a high probability. This avoids the need for brute force search of all portfolios and potentially provides a much more efficient means to sample optimal portfolios.
The simplest formulation of a portfolio optimization is an equal weighted quadratic unconstrained binary optimization (QUBO). The QUBO formulation is presented in the following equation.
The vector is binary (1 or 0 entries) and is of length (total possible stocks) where the portfolio is indicated by the subset of 1 entries. The vector and matrix contain information about the return of each stock and the covariance between pairs of stocks. This information is generally obtained from historical data which is an helpful but imperfect indicator of future behavior. Finally, the and terms are parameters that weigh the importance of risk and reward.
Exercise 1:
Given the , , and values below, write code in the cell below to produce the QUBO matrix . Note, in practice, the matrix is usually made upper diagonal by doubling the upper diagonal entries and zeroing out the lower diagonal entries. This saves space rather than having two terms correspond to the same correlation. Then, in the following cell, write code to generate all possible portfolios and, via brute force, determine the optimal portfolio.
Now, write a code that generates all of the possible portfolios, evaluates the portfolio quality by manually computing , and returns the best portfolio.
Now explore the widget below that allows you to change and . Try the following scenarios.
and
and
Can you rationalize the optimal portfolios produced by these settings? Hint: study and .
From QUBO to Spin Operator Hamiltonian
The QUBO is a useful format because it is well understood how to map a QUBO to a quantum Hamiltonian whose ground state corresponds to the QUBO solution. In the following sections you will explore two ways to find the ground state, first using the quantum approximate optimization algorithm (QAOA), and the second using an adiabatic approach and CUDA-Q dyanmics.
The first step to running either of these methods is to derive the Ising Hamiltonian, , which corresponds to the QUBO problem. Starting with the QUBO definition , substitute the binary variables with spin variables so . This results in
Grouping the terms results in a constant that can be dropped as it has no impact on the optimization.
We can further simplify things by grouping the single variable terms and the interacting terms: where are the coefficients of the single variable terms and are the coefficients of the interacting terms:
Exercise 2:
Using this derivation, write two functions to 1) build an Ising Hamiltonian and 2) produce a CUDA-Q spin operator Hamiltonian. The first function should take an upper triangular QUBO matrix as a numpy array and return a list of coefficients for the single variable terms, a flattened list of pairs of indices for the double variable terms, and a list of coefficients for the double variable terms. Returning each as a separate list in this way makes them easy to input to CUDA-Q kernels later.
Write a second function that uses these inputs and constructs the Hamiltonian as a CUDA-Q spin operator object. Print the Hamiltonian to confirm it is correct.
Gate-Based Approach - QAOA
The Quantum Approximate Optimization Algorithm (QAOA) is a hybrid quantum-classical approach to solving combinatorial optimization problems. To the state, it applies alternating layers of two types of unitary operations — one driven by a cost Hamiltonian (what your defined in the previous exercise) and one driven by a mixer Hamiltonian, each controlled by adjustable parameters. Your cost Hamiltonian is written as:
The goal is to produce a state, that when sampled, produces bitstrings at or close to the ground state of the Hamiltonian (i.e., optimal portfolios). The QAOA state after iterations (layers) is:
where:
(cost unitary),
(mixer unitary, often chosen as a transverse-field Hamiltonian),
is the uniform superposition over all computational basis states,
and are the variational parameters over the layers.
A classical optimizer iteratively updates these parameters to minimize the following expectation value:
Exercise 3:
Code a QAOA kernel in CUDA-Q. Note how inputs are provided as lists in accordance with your earlier functions. The figure below shows the gates applied to two qubits and one layer of the QAOA circuit. Specifically, the elements of the cost function are applied as gates parameterized with a for each layer, times for the single variable terms. The two variable terms are applied as a CNOT gate between the two corresponding qubits (controlled by the first), an gate parameterized with the same for each layer, times , followed by a repeat of the previous CNOT gate. The mixer terms are simple gates applied to each qubit parameterized with a for each layer.

Test your kernel with the code below which computes an expectation value from the state produced by the circuit and feeds the result into a classical optimizer which optimizes the 's and the 's. Then, the shots variable saves the results from sampling the circuit. Was the optimal portfolio the most probable?
The QAOA result may not produce the optimal portfolio most frequently due to the approximate nature of QAOA. However, the resulting QAOA state should sample the optimal portfolio with much higher probability than randomly sampling all bitstrings. Run the script below on the sample results and confirm that the optimal portfolio is at least in the top 5 results sampled from the QAOA procedure.
This variability can make it challenging to assess a particular optimization method. For example, what if you run QAOA and produce a state that only samples the second best portfolio? If there are quintillions of portfolios, that is not too bad! Result quality for simulated quantum algorithms is often quantified with a so called approximation ratio. This ratio can be defined a few ways, but the idea is to see how close the result is to the optimal portfolio.
A pragmatic way to do this is take the best result from the sample and compare it to the optimal value. For a small problem like you completed, that will probably be trivially 1 as you sample the optimal state many times. A better estimate to assess the algorithm performance is the average sample result divided by the optimal value. This approach can also help prove that the QAOA procedure really did help produce a state that samples better portfolios and that you did not just get lucky sampling the ground state.
To better visualize the performance of your QAOA function, use the plot_samples_histogram function to plot a sample from the QAOA kernel with your initial parameters (a uniform superposition as all variational parameters are 0) and the final state you produced. We've labeled "Good Portfolios" as those results that correspond to a negative value. Did the algorithm work?
Note, plot_samples_histogram takes the initial and final CUDA-Q sample results as inputs, as well as the list of all solutions output by the evaluate_all_bitstrings function you wrote previously.
Assume you are a portfolio manager and you need to be confident that you are suggesting a portfolio that has more reward than risk (a negative cost function value in this case). From the histogram, estimate the probability of choosing a good portfolio from the initial solution and your QAOA solution?
Though the example in this lab is small, it can be easily scaled up. In fact, the same approach used in "Divide and Conquer MaxCut QAOA" can potentially be used here to divide the problem into sub QAOA problems which can be solved in parallel and then knit together to obtain the final results. However, this depends on the sparsity of the QUBO and can only be used if certain stocks have negligible covariance.
Another possible aporoach is to use AI. Rather than performing the QAOA optimization to produce the circuit corresponding to the ground state, techniques like QAOA-GPT can use a generative model to sample QAOA circuits. Such an approach has promise, but is highly dependent on quality training data such that generated circuits are good approximations for the ground state.
Adiabatic Approach
An alternative to gate-based quantum computation is annealing. Annealing can be used to solve optimization problems encoded into a QUBO Hamiltonian like you have already done. The difference is that annealing takes advantage of a special Hamiltonian of the form
The adiabatic theorem states that such a Hamiltonian will remain in its ground state if the system evolves slowly enough. Consider why this is advantageous here. At , the system Hamiltonian is exactly . In practice, is selected to have a very easy to prepare ground state. Avoiding costly state preparation routines enables such an approach to more easily run on quantum hardware.

Though easier said than done, if an appropriate annealing schedule is chosen, will evolve from to a mixture of and to a final state corresponding to the ground state of . This state is the exact same as what QAOA is trying to approximate.
CUDA-Q can simulate such an evolution using its dynamics solvers which solve the differential equations that govern such an evolution. The code below will use annealing to find an optimal portfolio. The is selected to be Pauli- operations on all qubits which corresponds to the easily prepared state. The problem Hamiltonian, , is just the cost Hamiltonian that you used for QAOA.
Using the cudaq.evolve API, the simulation is run and produces the state below. This state can be input into a kernel and sampled similarly to the final state from the QAOA result. Note, the dynamics backend needs to be set in order to run evolve, but the backend must switch back to nvidia to build and sample the resulting state.
Exercise 4:
Confirm the adiabatic approach works. Notice how the variable controls how fast the evolution occurs. Try rerunning with . What happens to the quality of the result?
💻 Just a heads-up: The remainder of this notebook is designed to be run on an environment with a GPU. If you don't have access to a GPU, feel free to read through the cells and explore the content without executing them. Enjoy learning! ⭐
Adding Constraints
In practice, it is often helpful to constrain a portfolio optimization problem. Maybe a portfolio manager wants to only select a portfolio consisting of stocks rather than all possible combinations considered thus far. This means that the there is now some range of feasible solutions where exactly stocks are selected.
The constraint can be easily added to the QUBO by adding a quadratic constraint term of the form to produce
Expanding the constraint term results in:
Because is binary, we can replace with . So our expressions becomes:
Generally, the constraint has a factor which is a tuneable parameter for the strength of the constraint. After working out the example, there is a factor added to the diagonal terms, a term added to each off diagonal (the 2 comes from the upper diagonal QUBO form we are using), and a constant term of , which is often omitted since it has no impact on the result.
Exercise 5:
Write a new function below which now adds constraints to restrict the portfolio solutions to stocks. Using all of the functions created earlier, rerun an adiabatic simulation of this constrained result. Does the histogram produced below indicate that the constraint worked?
Improving Adiabatic Convergence with Q-CHOP
Adding a constraint means that you no longer care about the entire universe of portfolios, but only a subset of feasible solutions. Though the standard adiabatic approach always ends with a feasible ground state solution of the total Hamiltonian, it is not guaranteed to remain within the feasible space of solutions during the evolution. If somehow, the evolution process could remain within the feasible solution space, convergence could be much faster and potentially reach higher quality solutions.
The Q-CHOP Algorithm developed by Infleqtion and JPMorgan Chase does just that and provides a clever way to modify the evolution process to remain within the feasible solution space. Q-CHOP takes advantage of the fact that it is sometimes easy to prepare the worst feasible solution. The Q-CHOP evolution starts with the Hamiltonian whose ground state is equal to the worst feasible state and slowly rotates it (within the subspace of feasible solutions) to the problem Hamiltonian, resulting in a ground state of the problem Hamiltonian. The advantage of this is a procedure that can both converge faster and produce higher quality results by ensuring the evolution remains within the feasible space of solutions.

An astute reader might rightfully question the fact that it is easier to prepare the worst feasible state for portfolio optimization problems. This observation is correct: finding the worth feasible state is just as hard as finding the best possible state! The Q-CHOP developers circumvent this with a clever two-stage approach. First, they run Q-CHOP from an arbitrary feasible state. This is trivial to prepare as you can select any state that satisfies the constraint.
Then, they use a modified objective function of the form . This will drive the evolution towards either the best or worst feasible state. If the result is the best state, the problem is solved. If it is the worst, a second Q-CHOP run can be initialized from it. Remarkably, even if Q-CHOP requires both stages, it can still converge faster and to higher quality solutions than the standard adiabatic approach.
The Q-CHOP code is proprietary, so it cannot be interacted with directly. However, the widget below will allow you to compare a Q-CHOP run against a standard adiabatic approach. The left panel is a case where Q-CHOP requires only a single run since it lands on the best feasible state while the second panel is the two-stage situation in which the worst feasible state is found in the initial run.
Infleqtion used CUDA-Q dynamics to perform portfolio optimization on real financial data which is detailed in Spotlight: Infleqtion Optimizes Portfolios Using Q-CHOP and NVIDIA CUDA-Q Dynamics. For instances of selecting portfolios of size 7 or 8 from 15 possible stocks, Q-CHOP on average could produce a solution within 99.5% of the optimal solution with just 70 samples. This is three orders of magnitude fewer samples than is required to search the 12,870 potential portfolios by random sampling and provides great promise for future larger scale runs on physical QPUs.
Scaling Up Sampling
It is worth a brief mention of the fact that the QAOA and adiabatic approaches both require sampling a final state after the algorithm completes. When sampling on a phsyical QPU, this means a circuit must prepare the target state and measure it for every single shot. For simulation this is easy, especially for small problem sizes, but this can be very time consuming and limit the sampling that can be done on a QPU. For larger portfolio optimization problems, there is a tradeoff between the number of samples taken and the best solution sampled.
Exercise 6:
Using the code below, generate random and for stocks. Use your previous functions to run a QAOA. Run four different samples of the final state with 50, 100, 500, and 1000 shots and check how many times the optimal portfolio is sampled. For this rather modest problem size, is there risk it is missed?
Notice when a small number of samples is run, it is quite possible for the optimal solution to be missed. Our example of is rather modest, and this problem likey gets worse with larger problem sizes. So, every shot matters to improve the quality of the portfolios generated from such an application. One way to handle situations like this in the future will be to parallelize sampling across multiple QPUs.
CUDA-Q is designed with the capabilities for running jobs in such a multi-QPU data center. Using the mqpu backend, you can sample the kernel you produced from the 15 qubit QAOA run above across multiple simulated QPUs. If you have access to multiple GPUs, try running the code below.
This feature can help CUDA-Q users run simulations faster (if the original task is onerous enough) and lays the groundwork for parallelizing tasks like sampling to improve algorithm results at scale.
The Next Step: Quantum Stochastic Walks
Looking ahead, the field of quantum finance is continually evolving, with new algorithms emerging that offer different perspectives on optimization. One such exciting area is Quantum Stochastic Walks (QSW).
Unlike the methods you've learned, which translate the portfolio problem into a QUBO, QSW approaches portfolio optimization as a dynamic process on a financial network. This model can be understood through the following analogy:
The Landscape: Each stock is a node on a graph, and your investment capital is personified as a “walker” exploring this landscape.
The Journey: The walker's movement between nodes represents rebalancing the portfolio. This journey is guided by a hybrid engine: coherent quantum evolution uses superposition to find diversified pathways, while classical diffusion penalizes moves between highly correlated assets.
The Destination: The final portfolio weights are determined by the walker's long-term behavior. The probability of finding the walker at any given node becomes that stock's final allocation (for instance, a 10% probability translates to a 10% portfolio weight).
This approach has been shown to produce robust, highly diversified portfolios with significantly lower turnover and transaction costs (Chang et al). For those interested in the intersection of network science, quantum dynamics, and finance, QSW offers an area for further exploration. It represents a conceptual shift from static optimization to a dynamic, network-based simulation, highlighting the diverse ways quantum mechanics can be applied to financial challenges.