QUBO Formulation¶
Overview¶
The QUBO (Quadratic Unconstrained Binary Optimization) is the bridge between financial optimization problems and quantum hardware. qufin provides a flexible QUBO builder with multiple constraint types and encodings.
Markowitz to QUBO¶
Unconstrained Problem¶
The Markowitz objective:
With binary variables \(x_i \in \{0, 1\}\) (asset selected or not):
where:
Cardinality Constraint¶
Select exactly \(K\) out of \(N\) assets. Added as a quadratic penalty:
The penalty weight \(\lambda\) should be large enough to make infeasible solutions suboptimal. qufin's default: budget_penalty = 1e4.
Sector Constraints¶
Limit the number of assets per sector \(s\):
where \(C_s\) is the sector capacity.
Turnover Constraint¶
Penalize deviation from previous allocation \(x^{prev}\):
This makes it cheaper to hold current positions and expensive to change.
Encodings¶
One-Hot Encoding¶
Each asset gets one qubit: \(x_i = 1\) means asset \(i\) is selected.
- Qubits: \(N\) (one per asset)
- Weights: Equal weight \(1/K\) for selected assets
- Best for: Selection problems (which assets to hold)
Binary Encoding¶
Each asset gets \(b\) qubits encoding its weight level:
- Qubits: \(N \times b\)
- Weights: Granularity of \(1/(2^b - 1)\)
- Best for: Weight allocation (how much of each asset)
Qubit Cost Comparison¶
from qufin.portfolio.encodings import qubit_cost_table
table = qubit_cost_table([10, 20, 50, 100])
# Shows qubits needed for one-hot, binary (3-bit), unary (4-level)
| Assets | One-Hot | Binary (3-bit) | Unary (4-level) |
|---|---|---|---|
| 10 | 10 | 30 | 40 |
| 20 | 20 | 60 | 80 |
| 50 | 50 | 150 | 200 |
| 100 | 100 | 300 | 400 |
QUBO to Ising¶
For quantum execution, the QUBO is mapped to an Ising Hamiltonian via \(x_i = (1 - Z_i) / 2\):
This is handled automatically by PortfolioQUBO.build_matrix().
Usage¶
from qufin.portfolio.qubo import PortfolioQUBO
qubo = PortfolioQUBO(
mu=mu, # Expected returns (N,)
cov=cov, # Covariance matrix (N, N)
gamma=1.0, # Risk aversion
cardinality=5, # Select 5 assets
sector_map={0: [0,1,2], 1: [3,4,5], 2: [6,7,8,9]},
sector_caps={0: 2, 1: 2, 2: 2},
turnover_penalty=0.01,
transaction_cost=0.005,
previous_weights=prev_w,
budget_penalty=1e4,
encoding="one_hot",
)
Q = qubo.build_matrix() # (n_qubits, n_qubits)
obj = qubo.evaluate("0110100000") # Evaluate a specific bitstring
w = qubo.decode_weights("0110100000") # Get portfolio weights
feas = qubo.feasibility_check("0110100000") # Check constraints
References¶
- Barkoutsos et al. (2020). "Improving Variational Quantum Optimization using CVaR." Quantum 4, 256
- Mugel et al. (2022). "Dynamic Portfolio Optimization with Real Datasets Using Multi-Period Quantum Annealing." IEEE Access
- Brandhofer et al. (2023). "Benchmarking the Performance of Portfolio Optimization with QAOA." Quantum Sci. Technol. 8