Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
NVIDIA
GitHub Repository: NVIDIA/cuda-q-academic
Path: blob/main/quantum-applications-to-finance/helper.py
1147 views
1
import numpy as np
2
from typing import List, Tuple
3
import matplotlib.pyplot as plt
4
import numpy as np
5
from collections import defaultdict
6
7
8
def top_k_bitstrings(shots, Q: np.ndarray, k: int = 5
9
) -> List[Tuple[str, float, float]]:
10
"""
11
List the k most-probable bit-strings in `shots` and their QUBO values.
12
13
Returns
14
-------
15
[(bitstring, probability, qubo_cost), ...]
16
"""
17
# ------------------------------------------------------------------
18
# 1) extract a counts-dict {str -> int}
19
# ------------------------------------------------------------------
20
if isinstance(shots, dict): # plain counts dict
21
counts_dict = shots
22
elif hasattr(shots, "items"): # cudaq.SampleResult
23
counts_dict = dict(shots.items())
24
elif hasattr(shots, "counts"): # older naming
25
counts_dict = shots.counts
26
else:
27
raise TypeError("Unrecognised shot container type.")
28
29
total_shots = sum(counts_dict.values())
30
if total_shots == 0:
31
raise ValueError("No shots in sample result.")
32
33
# ------------------------------------------------------------------
34
# 2) sort by frequency and keep top-k
35
# ------------------------------------------------------------------
36
top = sorted(counts_dict.items(),
37
key=lambda kv: kv[1],
38
reverse=True)[:k]
39
40
# ------------------------------------------------------------------
41
# 3) compute QUBO value for every string
42
# ------------------------------------------------------------------
43
n = Q.shape[0]
44
results = []
45
for bitstr, cnt in top:
46
# bitstr is already a string like '1010'
47
x = np.fromiter(bitstr, dtype=int, count=n)
48
cost = float(x @ (Q @ x))
49
prob = cnt / total_shots
50
results.append((bitstr, prob, cost))
51
print(f"{bitstr} prob = {prob:.3f} QUBO = {cost:.6f}")
52
53
return results
54
55
def plot_samples_histogram(sample1, sample2, solutions_data, title="Portfolio Comparison"):
56
"""
57
Plot a histogram comparing two CUDAQ sample objects.
58
59
Args:
60
sample1: First CUDAQ sample object
61
sample2: Second CUDAQ sample object
62
solutions_data: List of tuples ((bit0, bit1, ...), objective_value)
63
title: Plot title
64
"""
65
# Sort solutions by objective value
66
sorted_solutions = sorted(solutions_data, key=lambda x: x[1])
67
68
# Create a mapping from bitstring tuples to their string representation
69
bitstring_map = {tuple(bits): ''.join(str(b) for b in bits) for bits, _ in sorted_solutions}
70
71
# Convert samples to dictionaries of counts
72
counts1 = defaultdict(int)
73
counts2 = defaultdict(int)
74
75
# Extract counts from sample1
76
for bitstring, count in sample1.items():
77
bitstring_tuple = tuple(int(b) for b in bitstring)
78
counts1[bitstring_tuple] = count
79
80
# Extract counts from sample2
81
for bitstring, count in sample2.items():
82
bitstring_tuple = tuple(int(b) for b in bitstring)
83
counts2[bitstring_tuple] = count
84
85
# Get all unique bitstrings in order of objective value
86
all_bitstrings = [bits for bits, _ in sorted_solutions]
87
88
# Create x-axis labels with bitstring and objective value
89
x_labels = [f"{bitstring_map[bits]}\n(obj: {val:.2f})" for bits, val in sorted_solutions]
90
91
# Set up plot
92
fig, ax = plt.subplots(figsize=(14, 8))
93
94
# Set positions for bars
95
x = np.arange(len(all_bitstrings))
96
width = 0.35
97
98
# Create bars with NVIDIA colors
99
nvidia_green = '#76B900' # NVIDIA green color
100
counts1_values = [counts1.get(bits, 0) for bits in all_bitstrings]
101
counts2_values = [counts2.get(bits, 0) for bits in all_bitstrings]
102
103
# Plot bars with black and NVIDIA green
104
bar1 = ax.bar(x - width/2, counts1_values, width, label='Initial State', color='black')
105
bar2 = ax.bar(x + width/2, counts2_values, width, label='Final State', color=nvidia_green)
106
107
# Find the transition point between good and bad portfolios
108
good_portfolios = []
109
bad_portfolios = []
110
for i, (_, val) in enumerate(sorted_solutions):
111
if val < 0:
112
good_portfolios.append(i)
113
else:
114
bad_portfolios.append(i)
115
116
# Add annotations for good and bad portfolios
117
max_count = max(max(counts1_values or [0]), max(counts2_values or [0]))
118
if max_count > 0:
119
if good_portfolios:
120
mid_good = good_portfolios[len(good_portfolios)//2]
121
ax.text(mid_good, max_count * 0.95, "Good Portfolios",
122
ha='center', va='center', fontsize=12, fontweight='bold')
123
124
if bad_portfolios:
125
mid_bad = bad_portfolios[len(bad_portfolios)//2]
126
ax.text(mid_bad, max_count * 0.95, "Bad Portfolios",
127
ha='center', va='center', fontsize=12, fontweight='bold')
128
129
# Customize plot
130
ax.set_xlabel('Bitstring Configuration (Objective Value)', fontsize=12)
131
ax.set_ylabel('Sample Count', fontsize=12)
132
ax.set_title(title, fontsize=14, fontweight='bold')
133
ax.set_xticks(x)
134
ax.set_xticklabels(x_labels, rotation=45, ha='right')
135
136
# Improve legend
137
ax.legend(loc='upper right', frameon=True, framealpha=0.9, fontsize=10)
138
139
# Add grid and adjust layout
140
ax.grid(axis='y', linestyle='--', alpha=0.3)
141
plt.tight_layout()
142
143
return fig, ax
144
145