Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

📚 The CoCalc Library - books, templates and other resources

132928 views
License: OTHER
1
#!/usr/bin/env python
2
#
3
# Copyright 2019 the original author or authors.
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
# http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
#
17
import pygame
18
import numpy as np
19
from vqe_playground.utils.colors import *
20
from vqe_playground.utils.navigation import *
21
from vqe_playground.utils.resources import *
22
from vqe_playground.model.circuit_grid_model import CircuitGridNode
23
from vqe_playground.model import circuit_node_types as node_types
24
25
GRID_WIDTH = 60
26
GRID_HEIGHT = 60
27
28
GATE_TILE_WIDTH = 43
29
GATE_TILE_HEIGHT = 45
30
31
LINE_WIDTH = 1
32
33
34
class CircuitGrid(pygame.sprite.RenderPlain):
35
"""Enables interaction with circuit"""
36
def __init__(self, xpos, ypos, circuit_grid_model):
37
self.xpos = xpos
38
self.ypos = ypos
39
self.circuit_grid_model = circuit_grid_model
40
self.selected_wire = 0
41
self.selected_column = 0
42
self.circuit_grid_background = CircuitGridBackground(circuit_grid_model)
43
self.circuit_grid_cursor = CircuitGridCursor()
44
self.gate_tiles = np.empty((circuit_grid_model.max_wires,
45
circuit_grid_model.max_columns),
46
dtype = CircuitGridGate)
47
48
for row_idx in range(self.circuit_grid_model.max_wires):
49
for col_idx in range(self.circuit_grid_model.max_columns):
50
self.gate_tiles[row_idx][col_idx] = \
51
CircuitGridGate(circuit_grid_model, row_idx, col_idx)
52
53
pygame.sprite.RenderPlain.__init__(self, self.circuit_grid_background,
54
self.gate_tiles,
55
self.circuit_grid_cursor)
56
self.update()
57
58
def update(self, *args):
59
# print("in CircuitGrid#update()")
60
61
sprite_list = self.sprites()
62
for sprite in sprite_list:
63
sprite.update()
64
65
self.circuit_grid_background.rect.left = self.xpos
66
self.circuit_grid_background.rect.top = self.ypos
67
68
for row_idx in range(self.circuit_grid_model.max_wires):
69
for col_idx in range(self.circuit_grid_model.max_columns):
70
self.gate_tiles[row_idx][col_idx].rect.centerx = \
71
self.xpos + GRID_WIDTH * (col_idx + 1.5)
72
self.gate_tiles[row_idx][col_idx].rect.centery = \
73
self.ypos + GRID_HEIGHT * (row_idx + 1.0)
74
75
self.highlight_selected_node(self.selected_wire, self.selected_column)
76
77
def highlight_selected_node(self, wire_num, column_num):
78
self.selected_wire = wire_num
79
self.selected_column = column_num
80
self.circuit_grid_cursor.rect.left = self.xpos + GRID_WIDTH * (self.selected_column + 1)
81
self.circuit_grid_cursor.rect.top = self.ypos + GRID_HEIGHT * (self.selected_wire + 0.5)
82
83
def display_exceptional_condition(self):
84
# TODO: Make cursor appearance indicate condition such as unable to place a gate
85
return
86
87
def move_to_adjacent_node(self, direction):
88
if direction == MOVE_LEFT and self.selected_column > 0:
89
self.selected_column -= 1
90
elif direction == MOVE_RIGHT and self.selected_column < self.circuit_grid_model.max_columns - 1:
91
self.selected_column += 1
92
elif direction == MOVE_UP and self.selected_wire > 0:
93
self.selected_wire -= 1
94
elif direction == MOVE_DOWN and self.selected_wire < self.circuit_grid_model.max_wires - 1:
95
self.selected_wire += 1
96
97
self.highlight_selected_node(self.selected_wire, self.selected_column)
98
99
def get_selected_node_gate_part(self):
100
return self.circuit_grid_model.get_node_gate_part(self.selected_wire, self.selected_column)
101
102
def handle_input_x(self):
103
selected_node_gate_part = self.get_selected_node_gate_part()
104
if selected_node_gate_part == node_types.EMPTY:
105
circuit_grid_node = CircuitGridNode(node_types.X)
106
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
107
self.update()
108
109
def handle_input_y(self):
110
selected_node_gate_part = self.get_selected_node_gate_part()
111
if selected_node_gate_part == node_types.EMPTY:
112
circuit_grid_node = CircuitGridNode(node_types.Y)
113
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
114
self.update()
115
116
def handle_input_z(self):
117
selected_node_gate_part = self.get_selected_node_gate_part()
118
if selected_node_gate_part == node_types.EMPTY:
119
circuit_grid_node = CircuitGridNode(node_types.Z)
120
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
121
self.update()
122
123
def handle_input_h(self):
124
selected_node_gate_part = self.get_selected_node_gate_part()
125
if selected_node_gate_part == node_types.EMPTY:
126
circuit_grid_node = CircuitGridNode(node_types.H)
127
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
128
self.update()
129
130
def handle_input_delete(self):
131
selected_node_gate_part = self.get_selected_node_gate_part()
132
# print('In handle_input_delete, node_type in selected node is: ', selected_node_gate_part)
133
if selected_node_gate_part == node_types.X or \
134
selected_node_gate_part == node_types.Y or \
135
selected_node_gate_part == node_types.Z or \
136
selected_node_gate_part == node_types.H:
137
self.delete_controls_for_gate(self.selected_wire, self.selected_column)
138
139
if selected_node_gate_part == node_types.CTRL:
140
gate_wire_num = \
141
self.circuit_grid_model.get_gate_wire_for_control_node(self.selected_wire,
142
self.selected_column)
143
if gate_wire_num >= 0:
144
self.delete_controls_for_gate(gate_wire_num, self.selected_column) # for wire_idx in range(min(self.selected_wire, gate_wire_num),
145
# max(self.selected_wire, gate_wire_num) + 1):
146
# print("Replacing wire ", wire_idx, " in column ", self.selected_column)
147
# self.circuit_grid_model.set_node(wire_idx, self.selected_column, node_types.IDEN)
148
elif selected_node_gate_part != node_types.SWAP and \
149
selected_node_gate_part != node_types.CTRL and \
150
selected_node_gate_part != node_types.TRACE:
151
circuit_grid_node = CircuitGridNode(node_types.EMPTY)
152
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
153
154
self.update()
155
156
def handle_input_ctrl(self):
157
# TODO: Handle Toffoli gates. For now, control qubit is assumed to be in ctrl_a variable
158
# with ctrl_b variable reserved for Toffoli gates
159
selected_node_gate_part = self.get_selected_node_gate_part()
160
if selected_node_gate_part == node_types.X or \
161
selected_node_gate_part == node_types.Y or \
162
selected_node_gate_part == node_types.Z or \
163
selected_node_gate_part == node_types.H:
164
circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)
165
if circuit_grid_node.ctrl_a >= 0:
166
# Gate already has a control qubit so remove it
167
orig_ctrl_a = circuit_grid_node.ctrl_a
168
circuit_grid_node.ctrl_a = -1
169
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
170
171
# Remove TRACE nodes
172
for wire_num in range(min(self.selected_wire, orig_ctrl_a) + 1,
173
max(self.selected_wire, orig_ctrl_a)):
174
if self.circuit_grid_model.get_node_gate_part(wire_num,
175
self.selected_column) == node_types.TRACE:
176
self.circuit_grid_model.set_node(wire_num, self.selected_column,
177
CircuitGridNode(node_types.EMPTY))
178
self.update()
179
elif circuit_grid_node.radians == 0:
180
# Attempt to place a control qubit beginning with the wire above
181
# TODO: Handle crz correctly
182
if self.selected_wire >= 0:
183
if self.place_ctrl_qubit(self.selected_wire, self.selected_wire - 1) == -1:
184
if self.selected_wire < self.circuit_grid_model.max_wires:
185
if self.place_ctrl_qubit(self.selected_wire, self.selected_wire + 1) == -1:
186
print("Can't place control qubit")
187
self.display_exceptional_condition()
188
189
def handle_input_move_ctrl(self, direction):
190
# TODO: Handle Toffoli gates. For now, control qubit is assumed to be in ctrl_a variable
191
# with ctrl_b variable reserved for Toffoli gates
192
# TODO: Simplify the logic in this method, including considering not actually ever
193
# placing a TRACE, but rather always dynamically calculating if a TRACE s/b displayed
194
selected_node_gate_part = self.get_selected_node_gate_part()
195
if selected_node_gate_part == node_types.X or \
196
selected_node_gate_part == node_types.Y or \
197
selected_node_gate_part == node_types.Z or \
198
selected_node_gate_part == node_types.H:
199
circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)
200
if 0 <= circuit_grid_node.ctrl_a < self.circuit_grid_model.max_wires:
201
# Gate already has a control qubit so try to move it
202
if direction == MOVE_UP:
203
candidate_wire_num = circuit_grid_node.ctrl_a - 1
204
if candidate_wire_num == self.selected_wire:
205
candidate_wire_num -= 1
206
else:
207
candidate_wire_num = circuit_grid_node.ctrl_a + 1
208
if candidate_wire_num == self.selected_wire:
209
candidate_wire_num += 1
210
if 0 <= candidate_wire_num < self.circuit_grid_model.max_wires:
211
if self.place_ctrl_qubit(self.selected_wire, candidate_wire_num) == candidate_wire_num:
212
print("control qubit successfully placed on wire ", candidate_wire_num)
213
if direction == MOVE_UP and candidate_wire_num < self.selected_wire:
214
if self.circuit_grid_model.get_node_gate_part(candidate_wire_num + 1,
215
self.selected_column) == node_types.EMPTY:
216
self.circuit_grid_model.set_node(candidate_wire_num + 1, self.selected_column,
217
CircuitGridNode(node_types.TRACE))
218
elif direction == MOVE_DOWN and candidate_wire_num > self.selected_wire:
219
if self.circuit_grid_model.get_node_gate_part(candidate_wire_num - 1,
220
self.selected_column) == node_types.EMPTY:
221
self.circuit_grid_model.set_node(candidate_wire_num - 1, self.selected_column,
222
CircuitGridNode(node_types.TRACE))
223
self.update()
224
else:
225
print("control qubit could not be placed on wire ", candidate_wire_num)
226
227
def handle_input_rotate(self, radians):
228
selected_node_gate_part = self.get_selected_node_gate_part()
229
if selected_node_gate_part == node_types.X or \
230
selected_node_gate_part == node_types.Y or \
231
selected_node_gate_part == node_types.Z:
232
circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)
233
234
# Don't allow rotation of controlled X or Y gates
235
if circuit_grid_node.ctrl_a == -1:
236
circuit_grid_node.radians = (circuit_grid_node.radians + radians) % (2 * np.pi)
237
self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)
238
# TODO: Handle crz correctly
239
# elif selected_node_gate_part == node_types.Z:
240
241
self.update()
242
243
def rotate_gate_absolute(self, gate_node, radians):
244
if gate_node.node_type == node_types.X or \
245
gate_node.node_type == node_types.Y or \
246
gate_node.node_type == node_types.Z:
247
circuit_grid_node = self.circuit_grid_model.get_node(gate_node.wire_num, gate_node.column_num)
248
249
# Don't allow rotation of controlled X or Y gates
250
if circuit_grid_node.ctrl_a == -1:
251
circuit_grid_node.radians = radians
252
self.circuit_grid_model.set_node(gate_node.wire_num, gate_node.column_num, circuit_grid_node)
253
# TODO: Handle crz correctly
254
# elif selected_node_gate_part == node_types.Z:
255
256
self.update()
257
258
def place_ctrl_qubit(self, gate_wire_num, candidate_ctrl_wire_num):
259
"""Attempt to place a control qubit on a wire.
260
If successful, return the wire number. If not, return -1
261
"""
262
if candidate_ctrl_wire_num < 0 or candidate_ctrl_wire_num >= self.circuit_grid_model.max_wires:
263
return -1
264
candidate_wire_gate_part = \
265
self.circuit_grid_model.get_node_gate_part(candidate_ctrl_wire_num,
266
self.selected_column)
267
if candidate_wire_gate_part == node_types.EMPTY or \
268
candidate_wire_gate_part == node_types.TRACE:
269
circuit_grid_node = self.circuit_grid_model.get_node(gate_wire_num, self.selected_column)
270
circuit_grid_node.ctrl_a = candidate_ctrl_wire_num
271
self.circuit_grid_model.set_node(gate_wire_num, self.selected_column, circuit_grid_node)
272
self.circuit_grid_model.set_node(candidate_ctrl_wire_num, self.selected_column,
273
CircuitGridNode(node_types.EMPTY))
274
self.update()
275
return candidate_ctrl_wire_num
276
else:
277
print("Can't place control qubit on wire: ", candidate_ctrl_wire_num)
278
return -1
279
280
def delete_controls_for_gate(self, gate_wire_num, column_num):
281
control_a_wire_num = self.circuit_grid_model.get_node(gate_wire_num, column_num).ctrl_a
282
control_b_wire_num = self.circuit_grid_model.get_node(gate_wire_num, column_num).ctrl_b
283
284
# Choose the control wire (if any exist) furthest away from the gate wire
285
control_a_wire_distance = 0
286
control_b_wire_distance = 0
287
if control_a_wire_num >= 0:
288
control_a_wire_distance = abs(control_a_wire_num - gate_wire_num)
289
if control_b_wire_num >= 0:
290
control_b_wire_distance = abs(control_b_wire_num - gate_wire_num)
291
292
control_wire_num = -1
293
if control_a_wire_distance > control_b_wire_distance:
294
control_wire_num = control_a_wire_num
295
elif control_a_wire_distance < control_b_wire_distance:
296
control_wire_num = control_b_wire_num
297
298
if control_wire_num >= 0:
299
# TODO: If this is a controlled gate, remove the connecting TRACE parts between the gate and the control
300
# ALSO: Refactor with similar code in this method
301
for wire_idx in range(min(gate_wire_num, control_wire_num),
302
max(gate_wire_num, control_wire_num) + 1):
303
print("Replacing wire ", wire_idx, " in column ", column_num)
304
circuit_grid_node = CircuitGridNode(node_types.EMPTY)
305
self.circuit_grid_model.set_node(wire_idx, column_num, circuit_grid_node)
306
307
308
class CircuitGridBackground(pygame.sprite.Sprite):
309
"""Background for circuit grid"""
310
def __init__(self, circuit_grid_model):
311
pygame.sprite.Sprite.__init__(self)
312
313
self.image = pygame.Surface([GRID_WIDTH * (circuit_grid_model.max_columns + 2),
314
GRID_HEIGHT * (circuit_grid_model.max_wires + 1)])
315
self.image.convert()
316
self.image.fill(WHITE)
317
self.rect = self.image.get_rect()
318
pygame.draw.rect(self.image, BLACK, self.rect, LINE_WIDTH)
319
320
for wire_num in range(circuit_grid_model.max_wires):
321
pygame.draw.line(self.image, BLACK,
322
(GRID_WIDTH * 0.5, (wire_num + 1) * GRID_HEIGHT),
323
(self.rect.width - (GRID_WIDTH * 0.5), (wire_num + 1) * GRID_HEIGHT),
324
LINE_WIDTH)
325
326
327
class CircuitGridGate(pygame.sprite.Sprite):
328
"""Images for nodes"""
329
def __init__(self, circuit_grid_model, wire_num, column_num):
330
pygame.sprite.Sprite.__init__(self)
331
self.circuit_grid_model = circuit_grid_model
332
self.wire_num = wire_num
333
self.column_num = column_num
334
335
self.update()
336
337
def update(self):
338
node_type = self.circuit_grid_model.get_node_gate_part(self.wire_num, self.column_num)
339
340
if node_type == node_types.H:
341
self.image, self.rect = load_image('gate_images/h_gate.png', -1)
342
elif node_type == node_types.X:
343
node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)
344
if node.ctrl_a >= 0 or node.ctrl_b >= 0:
345
# This is a control-X gate or Toffoli gate
346
# TODO: Handle Toffoli gates more completely
347
if self.wire_num > max(node.ctrl_a, node.ctrl_b):
348
self.image, self.rect = load_image('gate_images/not_gate_below_ctrl.png', -1)
349
else:
350
self.image, self.rect = load_image('gate_images/not_gate_above_ctrl.png', -1)
351
elif node.radians != 0:
352
self.image, self.rect = load_image('gate_images/rx_gate.png', -1)
353
self.rect = self.image.get_rect()
354
pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)
355
pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)
356
else:
357
self.image, self.rect = load_image('gate_images/x_gate.png', -1)
358
elif node_type == node_types.Y:
359
node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)
360
if node.radians != 0:
361
self.image, self.rect = load_image('gate_images/ry_gate.png', -1)
362
self.rect = self.image.get_rect()
363
pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)
364
pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)
365
else:
366
self.image, self.rect = load_image('gate_images/y_gate.png', -1)
367
elif node_type == node_types.Z:
368
node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)
369
if node.radians != 0:
370
self.image, self.rect = load_image('gate_images/rz_gate.png', -1)
371
self.rect = self.image.get_rect()
372
pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)
373
pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)
374
else:
375
self.image, self.rect = load_image('gate_images/z_gate.png', -1)
376
elif node_type == node_types.S:
377
self.image, self.rect = load_image('gate_images/s_gate.png', -1)
378
elif node_type == node_types.SDG:
379
self.image, self.rect = load_image('gate_images/sdg_gate.png', -1)
380
elif node_type == node_types.T:
381
self.image, self.rect = load_image('gate_images/t_gate.png', -1)
382
elif node_type == node_types.TDG:
383
self.image, self.rect = load_image('gate_images/tdg_gate.png', -1)
384
elif node_type == node_types.IDEN:
385
self.image, self.rect = load_image('gate_images/iden_gate.png', -1)
386
elif node_type == node_types.CTRL:
387
# TODO: Handle Toffoli gates correctly
388
if self.wire_num > \
389
self.circuit_grid_model.get_gate_wire_for_control_node(self.wire_num, self.column_num):
390
self.image, self.rect = load_image('gate_images/ctrl_gate_bottom_wire.png', -1)
391
else:
392
self.image, self.rect = load_image('gate_images/ctrl_gate_top_wire.png', -1)
393
elif node_type == node_types.TRACE:
394
self.image, self.rect = load_image('gate_images/trace_gate.png', -1)
395
elif node_type == node_types.SWAP:
396
self.image, self.rect = load_image('gate_images/swap_gate.png', -1)
397
else:
398
self.image = pygame.Surface([GATE_TILE_WIDTH, GATE_TILE_HEIGHT])
399
self.image.set_alpha(0)
400
self.rect = self.image.get_rect()
401
402
self.image.convert()
403
404
405
class CircuitGridCursor(pygame.sprite.Sprite):
406
"""Cursor to highlight current grid node"""
407
def __init__(self):
408
pygame.sprite.Sprite.__init__(self)
409
self.image, self.rect = load_image('images/circuit-grid-cursor-60px.png', -1)
410
self.image.convert_alpha()
411
412
413
414