-
Notifications
You must be signed in to change notification settings - Fork 0
/
fig2work.html
256 lines (217 loc) · 9.45 KB
/
fig2work.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
<title>Figure 2</title>
<link rel="stylesheet" href="css/Serif/cmun-serif.css"/>
<link rel="stylesheet" href="css/grover-figures.css"/>
<link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<style>
body { font-family: "Computer Modern Serif"; }
</style>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
showMathMenu: false,
"HTML-CSS": {
scale: 90
},
TeX: {
Macros: {
braket: ['{\\langle #1 \\rangle}', 1],
Abs: ['\\left\\lvert #2 \\right\\rvert_{\\text{#1}}', 2, ""],
bra: ['{\\left\\langle #1\\right|}',1],
ket: ['{\\left| #1\\right\\rangle}',1],
}}
});
</script>
<script type="text/javascript" async src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<!-- Global D3 variables etc -->
<script type="text/javascript">
var n_bits = 4; // Number of qubits
var needle_idx = 7; // Haven't chosen a needle yet
function myLine(svgHandle, startCoord, endCoord) {
return svgHandle.append("line")
.attr("x1", startCoord[0]).attr("y1", startCoord[1])
.attr("x2", endCoord[0]).attr("y2", endCoord[1]);
}
function drawMyAxis(w, origin, svgHandle, endHeight) {
var group = svgHandle.append("g").attr("id", "axis");
myLine(group, [0.5, origin], [w - 0.5, origin]);
myLine(group, [0.5, origin + endHeight], [0.5, origin - endHeight]);
myLine(group, [w - 0.5, origin + endHeight], [w - 0.5, origin - endHeight]);
}
</script>
</head>
<body>
<h1>Figure 2</h1>
<input type="button" value="Ground State" onclick="doGroundState()" />
<input type="button" value="Hadamard" onclick="doHadamard()" />
<input type="button" value="Oracle" onclick="doOracle()" />
<input type="button" value="Mean" onclick="doMean()" />
<div id="prob-div" style="opacity: 0;">\(P(x_0) \approx \) <span id="prob-val"></span></div>
<script type="text/javascript">
var h_algplot = 200; // SVG canvas size
var w_algplot = 580;
var origin_algplot = h_algplot / 2.0; // x-axis position
var noiseOn = true; // Whether to include noise
var noiseSigma = 0.025; // Strength of noise
var n_bars = Math.pow(2, n_bits);
var bar_heights = new Array(n_bars).fill(1.0 / Math.sqrt(n_bars)); // Initialize bars to equal superposition
// Create a scale for taking amplitudes to graphable values
var bar_scale = d3.scale.linear()
.domain([-1.0, 1.0])
.range([-100, 100]);
var bar_padding = 12; // Padding between bars
var svg = d3.select("body").append("svg").attr("width", w_algplot).attr("height", h_algplot).attr("id", "fig2"); // Create SVG object
var noise = function() { return noiseOn ? d3.random.normal(0.0, noiseSigma)() : 0 }; // Noise function gives normally distributed noise if turned on
bar_heights = bar_heights.map(function(clean) { return clean + noise(); }); // Apply noise to the amplitudes
var rects = svg.append("g").attr("id", "rects") // Add the bars (but don't draw them)
.selectAll("rect")
.data(bar_heights)
.enter()
.append("rect");
drawMyAxis(w_algplot, origin_algplot, svg, 5); // Draw x-axis
d3.select("#axis").selectAll("line").style("opacity", 0.65).style("stroke", "gray");
/* Helper function for dealing with negative values
*
* SVG uses the top left corner of rectangles and doesn't allow negative heights.
* So, we use |height| but set the y coordinate to either 0 or the "usual" value depending
* on whether the height is negative or positive, respectively.
*
*/
function makeBar(h) {
return {
"y" : origin_algplot - ((h < 0) ? bar_scale(0) : Math.abs(bar_scale(h))),
"height" : Math.abs(bar_scale(h))
};
}
var needle_idx = 15;
// Gets probability of measuring the needle to 2 dp
function probMeasure() {
return Math.min(1.00, Math.pow(bar_heights[needle_idx], 2).toFixed(2));
}
var rect_width = (w_algplot / n_bars) - bar_padding; // Caulculate width of each bar
rects.attr("width", rect_width) // "Draw" bars but with zero height now
.each(function(h) { d3.select(this).attr(makeBar(0)); })
.attr("x", bar_padding / 2.0)
.classed({"graph-rect" : true,
"needle-rect" : function(h,i) { return (i == needle_idx); } }); // Add a class to the needle bar
// First step of the algorithm: \ket{0}^{\otimes n}
function doGroundState() {
rects.each(function (h) {
d3.select(this)
.transition("groundstate")
.duration(400)
.ease("quad")
.attr(makeBar(h)); // Give the bars height
});
}
// Second step of the algorithm: equal superposition (I know I said I already did that, but now we can see it)
function doHadamard() {
rects.transition("hadamard")
.duration(350)
.ease("quad")
.attr("x", function(h, i) { // Space the bars out correctly
return i * (w_algplot / n_bars) + 0.5*bar_padding;
});
d3.select("#prob-val").html(probMeasure()) // Update the probability
d3.select("#prob-div").transition()
.duration(150)
.style("opacity", 1);
}
// Draw a red dot to symbolize the action of the oracle
var r_dot = 3;
var oracleDot = svg.append("circle")
.attr("cx", -1.5*r_dot)
.attr("cy", origin_algplot)
.attr("r", r_dot)
.style("fill", "red");
// First part of grover iteration: flip the phase of the needle
function doOracle() {
var totalTime = 1300; // How long the oracle pass takes
var scaleFactor = 1.35; // How big to make the needle when we pulse it
var dotOffset = 0.5 * w_algplot / n_bars // Starting position of the dot
oracleDot.attr("cx", dotOffset);
oracleDot.transition() // Move the dot
.duration(totalTime)
.ease("sin-in-out")
.attr("cx", w_algplot + r_dot);
rects.transition("oracle") // This gets complex. Flash along the bars.
.duration(totalTime / n_bars)
.delay(function(h,i) { // Stagger flashes
var easy = d3.ease("sin-out-in"); // Take the easing function, don't fully understand why it needs to be out-in rather than in-out
return easy(i / (n_bars - 1)) * totalTime;
})
.style("fill", "gray") // The flash colour
.each("start", function(h) { // Called at the start of each flash
thisRec = d3.select(this);
thisNoise = 0.75*noise(); // Oracle-induced noise is weaker, I've decided
thisRec.transition() // Draw the effect of the noise
.duration(100)
.attr(makeBar(h + thisNoise));
thisRec.datum(h + thisNoise); // Update the internal data
if (thisRec.attr("class").indexOf("needle-rect") == -1) { // If it's not the needle then unflash it
thisRec
.transition()
.delay(120)
.duration(200)
.style("fill", "black");
} else { // If it is the needle...
thisRec
.transition("pulse") // Pulse it (inflation phase)
.duration(250)
.ease("exp")
.attr("width", scaleFactor*rect_width)
.attr("x", needle_idx * (w_algplot / n_bars) + 0.5*bar_padding + 0.5*rect_width*(1 - scaleFactor))
.attr(makeBar(scaleFactor*(h + thisNoise)))
.transition("pulse")
.ease("linear") // Pulse it (relaxation phase)
.delay(250 + 20)
.duration(250)
.attr("width", rect_width)
.attr("x", needle_idx * (w_algplot / n_bars) + 0.5*bar_padding)
.attr(makeBar(h + thisNoise))
.transition("flip") // Flip it down to 0
.delay(250 + 20 + 250 + 300)
.duration(300)
.ease("cubic-in")
.attr(makeBar(0))
.transition() // And then past zero
.duration(300)
.ease("cubic-out")
.attr(makeBar(-(h + thisNoise)));
bar_heights[needle_idx] *= -1; // Update internal data re flip
rects.data(bar_heights);
}
});
}
// Second part of Grover iteration: inversion about the mean
function doMean() {
var mean_height = d3.mean(bar_heights); // Calculate mean amplitude
var mean_y = makeBar(mean_height)["y"]; // And mean coordinate
var meanLine = myLine(svg, [-1, mean_y], [-1, mean_y]); // Start off the mean marking line
meanLine.attr("class", "mean-line");
meanLine.transition("meanline") // Draw the line across the graph
.duration(750)
.ease("quad")
.attr("x2", w_algplot + 1);
bar_heights = bar_heights.map(function(old) { // Perform the actual inversion about the mean
return 2*mean_height - old;
});
rects.data(bar_heights); // Update internal data
rects.transition("meanInversion") // Draw the inversion
.duration(1200)
.delay(750 + 300)
.attr("y", function(h) { return makeBar(h)["y"]; })
.attr("height", function(h) { return makeBar(h)["height"]; });
setTimeout(function(){ d3.select("#prob-val").html(probMeasure()); }, 750+300+1200); // Update the measurement probability
meanLine.transition("disappearLine") // Remove the mean marker
.duration(750)
.delay(750 + 300 + 1200 + 200)
.ease("quad")
.attr("x1", w_algplot + 1);
}
</script>
</body>
</html>