📚 The CoCalc Library - books, templates and other resources
License: OTHER
#!/usr/bin/env python1#2# Copyright 2019 the original author or authors.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15#16import pygame17import numpy as np18from vqe_playground.utils.colors import *19from vqe_playground.utils.navigation import *20from vqe_playground.utils.resources import *21from vqe_playground.model.circuit_grid_model import CircuitGridNode22from vqe_playground.model import circuit_node_types as node_types2324GRID_WIDTH = 6025GRID_HEIGHT = 602627GATE_TILE_WIDTH = 4328GATE_TILE_HEIGHT = 452930LINE_WIDTH = 1313233class CircuitGrid(pygame.sprite.RenderPlain):34"""Enables interaction with circuit"""35def __init__(self, xpos, ypos, circuit_grid_model):36self.xpos = xpos37self.ypos = ypos38self.circuit_grid_model = circuit_grid_model39self.selected_wire = 040self.selected_column = 041self.circuit_grid_background = CircuitGridBackground(circuit_grid_model)42self.circuit_grid_cursor = CircuitGridCursor()43self.gate_tiles = np.empty((circuit_grid_model.max_wires,44circuit_grid_model.max_columns),45dtype = CircuitGridGate)4647for row_idx in range(self.circuit_grid_model.max_wires):48for col_idx in range(self.circuit_grid_model.max_columns):49self.gate_tiles[row_idx][col_idx] = \50CircuitGridGate(circuit_grid_model, row_idx, col_idx)5152pygame.sprite.RenderPlain.__init__(self, self.circuit_grid_background,53self.gate_tiles,54self.circuit_grid_cursor)55self.update()5657def update(self, *args):58# print("in CircuitGrid#update()")5960sprite_list = self.sprites()61for sprite in sprite_list:62sprite.update()6364self.circuit_grid_background.rect.left = self.xpos65self.circuit_grid_background.rect.top = self.ypos6667for row_idx in range(self.circuit_grid_model.max_wires):68for col_idx in range(self.circuit_grid_model.max_columns):69self.gate_tiles[row_idx][col_idx].rect.centerx = \70self.xpos + GRID_WIDTH * (col_idx + 1.5)71self.gate_tiles[row_idx][col_idx].rect.centery = \72self.ypos + GRID_HEIGHT * (row_idx + 1.0)7374self.highlight_selected_node(self.selected_wire, self.selected_column)7576def highlight_selected_node(self, wire_num, column_num):77self.selected_wire = wire_num78self.selected_column = column_num79self.circuit_grid_cursor.rect.left = self.xpos + GRID_WIDTH * (self.selected_column + 1)80self.circuit_grid_cursor.rect.top = self.ypos + GRID_HEIGHT * (self.selected_wire + 0.5)8182def display_exceptional_condition(self):83# TODO: Make cursor appearance indicate condition such as unable to place a gate84return8586def move_to_adjacent_node(self, direction):87if direction == MOVE_LEFT and self.selected_column > 0:88self.selected_column -= 189elif direction == MOVE_RIGHT and self.selected_column < self.circuit_grid_model.max_columns - 1:90self.selected_column += 191elif direction == MOVE_UP and self.selected_wire > 0:92self.selected_wire -= 193elif direction == MOVE_DOWN and self.selected_wire < self.circuit_grid_model.max_wires - 1:94self.selected_wire += 19596self.highlight_selected_node(self.selected_wire, self.selected_column)9798def get_selected_node_gate_part(self):99return self.circuit_grid_model.get_node_gate_part(self.selected_wire, self.selected_column)100101def handle_input_x(self):102selected_node_gate_part = self.get_selected_node_gate_part()103if selected_node_gate_part == node_types.EMPTY:104circuit_grid_node = CircuitGridNode(node_types.X)105self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)106self.update()107108def handle_input_y(self):109selected_node_gate_part = self.get_selected_node_gate_part()110if selected_node_gate_part == node_types.EMPTY:111circuit_grid_node = CircuitGridNode(node_types.Y)112self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)113self.update()114115def handle_input_z(self):116selected_node_gate_part = self.get_selected_node_gate_part()117if selected_node_gate_part == node_types.EMPTY:118circuit_grid_node = CircuitGridNode(node_types.Z)119self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)120self.update()121122def handle_input_h(self):123selected_node_gate_part = self.get_selected_node_gate_part()124if selected_node_gate_part == node_types.EMPTY:125circuit_grid_node = CircuitGridNode(node_types.H)126self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)127self.update()128129def handle_input_delete(self):130selected_node_gate_part = self.get_selected_node_gate_part()131# print('In handle_input_delete, node_type in selected node is: ', selected_node_gate_part)132if selected_node_gate_part == node_types.X or \133selected_node_gate_part == node_types.Y or \134selected_node_gate_part == node_types.Z or \135selected_node_gate_part == node_types.H:136self.delete_controls_for_gate(self.selected_wire, self.selected_column)137138if selected_node_gate_part == node_types.CTRL:139gate_wire_num = \140self.circuit_grid_model.get_gate_wire_for_control_node(self.selected_wire,141self.selected_column)142if gate_wire_num >= 0:143self.delete_controls_for_gate(gate_wire_num, self.selected_column) # for wire_idx in range(min(self.selected_wire, gate_wire_num),144# max(self.selected_wire, gate_wire_num) + 1):145# print("Replacing wire ", wire_idx, " in column ", self.selected_column)146# self.circuit_grid_model.set_node(wire_idx, self.selected_column, node_types.IDEN)147elif selected_node_gate_part != node_types.SWAP and \148selected_node_gate_part != node_types.CTRL and \149selected_node_gate_part != node_types.TRACE:150circuit_grid_node = CircuitGridNode(node_types.EMPTY)151self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)152153self.update()154155def handle_input_ctrl(self):156# TODO: Handle Toffoli gates. For now, control qubit is assumed to be in ctrl_a variable157# with ctrl_b variable reserved for Toffoli gates158selected_node_gate_part = self.get_selected_node_gate_part()159if selected_node_gate_part == node_types.X or \160selected_node_gate_part == node_types.Y or \161selected_node_gate_part == node_types.Z or \162selected_node_gate_part == node_types.H:163circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)164if circuit_grid_node.ctrl_a >= 0:165# Gate already has a control qubit so remove it166orig_ctrl_a = circuit_grid_node.ctrl_a167circuit_grid_node.ctrl_a = -1168self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)169170# Remove TRACE nodes171for wire_num in range(min(self.selected_wire, orig_ctrl_a) + 1,172max(self.selected_wire, orig_ctrl_a)):173if self.circuit_grid_model.get_node_gate_part(wire_num,174self.selected_column) == node_types.TRACE:175self.circuit_grid_model.set_node(wire_num, self.selected_column,176CircuitGridNode(node_types.EMPTY))177self.update()178elif circuit_grid_node.radians == 0:179# Attempt to place a control qubit beginning with the wire above180# TODO: Handle crz correctly181if self.selected_wire >= 0:182if self.place_ctrl_qubit(self.selected_wire, self.selected_wire - 1) == -1:183if self.selected_wire < self.circuit_grid_model.max_wires:184if self.place_ctrl_qubit(self.selected_wire, self.selected_wire + 1) == -1:185print("Can't place control qubit")186self.display_exceptional_condition()187188def handle_input_move_ctrl(self, direction):189# TODO: Handle Toffoli gates. For now, control qubit is assumed to be in ctrl_a variable190# with ctrl_b variable reserved for Toffoli gates191# TODO: Simplify the logic in this method, including considering not actually ever192# placing a TRACE, but rather always dynamically calculating if a TRACE s/b displayed193selected_node_gate_part = self.get_selected_node_gate_part()194if selected_node_gate_part == node_types.X or \195selected_node_gate_part == node_types.Y or \196selected_node_gate_part == node_types.Z or \197selected_node_gate_part == node_types.H:198circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)199if 0 <= circuit_grid_node.ctrl_a < self.circuit_grid_model.max_wires:200# Gate already has a control qubit so try to move it201if direction == MOVE_UP:202candidate_wire_num = circuit_grid_node.ctrl_a - 1203if candidate_wire_num == self.selected_wire:204candidate_wire_num -= 1205else:206candidate_wire_num = circuit_grid_node.ctrl_a + 1207if candidate_wire_num == self.selected_wire:208candidate_wire_num += 1209if 0 <= candidate_wire_num < self.circuit_grid_model.max_wires:210if self.place_ctrl_qubit(self.selected_wire, candidate_wire_num) == candidate_wire_num:211print("control qubit successfully placed on wire ", candidate_wire_num)212if direction == MOVE_UP and candidate_wire_num < self.selected_wire:213if self.circuit_grid_model.get_node_gate_part(candidate_wire_num + 1,214self.selected_column) == node_types.EMPTY:215self.circuit_grid_model.set_node(candidate_wire_num + 1, self.selected_column,216CircuitGridNode(node_types.TRACE))217elif direction == MOVE_DOWN and candidate_wire_num > self.selected_wire:218if self.circuit_grid_model.get_node_gate_part(candidate_wire_num - 1,219self.selected_column) == node_types.EMPTY:220self.circuit_grid_model.set_node(candidate_wire_num - 1, self.selected_column,221CircuitGridNode(node_types.TRACE))222self.update()223else:224print("control qubit could not be placed on wire ", candidate_wire_num)225226def handle_input_rotate(self, radians):227selected_node_gate_part = self.get_selected_node_gate_part()228if selected_node_gate_part == node_types.X or \229selected_node_gate_part == node_types.Y or \230selected_node_gate_part == node_types.Z:231circuit_grid_node = self.circuit_grid_model.get_node(self.selected_wire, self.selected_column)232233# Don't allow rotation of controlled X or Y gates234if circuit_grid_node.ctrl_a == -1:235circuit_grid_node.radians = (circuit_grid_node.radians + radians) % (2 * np.pi)236self.circuit_grid_model.set_node(self.selected_wire, self.selected_column, circuit_grid_node)237# TODO: Handle crz correctly238# elif selected_node_gate_part == node_types.Z:239240self.update()241242def rotate_gate_absolute(self, gate_node, radians):243if gate_node.node_type == node_types.X or \244gate_node.node_type == node_types.Y or \245gate_node.node_type == node_types.Z:246circuit_grid_node = self.circuit_grid_model.get_node(gate_node.wire_num, gate_node.column_num)247248# Don't allow rotation of controlled X or Y gates249if circuit_grid_node.ctrl_a == -1:250circuit_grid_node.radians = radians251self.circuit_grid_model.set_node(gate_node.wire_num, gate_node.column_num, circuit_grid_node)252# TODO: Handle crz correctly253# elif selected_node_gate_part == node_types.Z:254255self.update()256257def place_ctrl_qubit(self, gate_wire_num, candidate_ctrl_wire_num):258"""Attempt to place a control qubit on a wire.259If successful, return the wire number. If not, return -1260"""261if candidate_ctrl_wire_num < 0 or candidate_ctrl_wire_num >= self.circuit_grid_model.max_wires:262return -1263candidate_wire_gate_part = \264self.circuit_grid_model.get_node_gate_part(candidate_ctrl_wire_num,265self.selected_column)266if candidate_wire_gate_part == node_types.EMPTY or \267candidate_wire_gate_part == node_types.TRACE:268circuit_grid_node = self.circuit_grid_model.get_node(gate_wire_num, self.selected_column)269circuit_grid_node.ctrl_a = candidate_ctrl_wire_num270self.circuit_grid_model.set_node(gate_wire_num, self.selected_column, circuit_grid_node)271self.circuit_grid_model.set_node(candidate_ctrl_wire_num, self.selected_column,272CircuitGridNode(node_types.EMPTY))273self.update()274return candidate_ctrl_wire_num275else:276print("Can't place control qubit on wire: ", candidate_ctrl_wire_num)277return -1278279def delete_controls_for_gate(self, gate_wire_num, column_num):280control_a_wire_num = self.circuit_grid_model.get_node(gate_wire_num, column_num).ctrl_a281control_b_wire_num = self.circuit_grid_model.get_node(gate_wire_num, column_num).ctrl_b282283# Choose the control wire (if any exist) furthest away from the gate wire284control_a_wire_distance = 0285control_b_wire_distance = 0286if control_a_wire_num >= 0:287control_a_wire_distance = abs(control_a_wire_num - gate_wire_num)288if control_b_wire_num >= 0:289control_b_wire_distance = abs(control_b_wire_num - gate_wire_num)290291control_wire_num = -1292if control_a_wire_distance > control_b_wire_distance:293control_wire_num = control_a_wire_num294elif control_a_wire_distance < control_b_wire_distance:295control_wire_num = control_b_wire_num296297if control_wire_num >= 0:298# TODO: If this is a controlled gate, remove the connecting TRACE parts between the gate and the control299# ALSO: Refactor with similar code in this method300for wire_idx in range(min(gate_wire_num, control_wire_num),301max(gate_wire_num, control_wire_num) + 1):302print("Replacing wire ", wire_idx, " in column ", column_num)303circuit_grid_node = CircuitGridNode(node_types.EMPTY)304self.circuit_grid_model.set_node(wire_idx, column_num, circuit_grid_node)305306307class CircuitGridBackground(pygame.sprite.Sprite):308"""Background for circuit grid"""309def __init__(self, circuit_grid_model):310pygame.sprite.Sprite.__init__(self)311312self.image = pygame.Surface([GRID_WIDTH * (circuit_grid_model.max_columns + 2),313GRID_HEIGHT * (circuit_grid_model.max_wires + 1)])314self.image.convert()315self.image.fill(WHITE)316self.rect = self.image.get_rect()317pygame.draw.rect(self.image, BLACK, self.rect, LINE_WIDTH)318319for wire_num in range(circuit_grid_model.max_wires):320pygame.draw.line(self.image, BLACK,321(GRID_WIDTH * 0.5, (wire_num + 1) * GRID_HEIGHT),322(self.rect.width - (GRID_WIDTH * 0.5), (wire_num + 1) * GRID_HEIGHT),323LINE_WIDTH)324325326class CircuitGridGate(pygame.sprite.Sprite):327"""Images for nodes"""328def __init__(self, circuit_grid_model, wire_num, column_num):329pygame.sprite.Sprite.__init__(self)330self.circuit_grid_model = circuit_grid_model331self.wire_num = wire_num332self.column_num = column_num333334self.update()335336def update(self):337node_type = self.circuit_grid_model.get_node_gate_part(self.wire_num, self.column_num)338339if node_type == node_types.H:340self.image, self.rect = load_image('gate_images/h_gate.png', -1)341elif node_type == node_types.X:342node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)343if node.ctrl_a >= 0 or node.ctrl_b >= 0:344# This is a control-X gate or Toffoli gate345# TODO: Handle Toffoli gates more completely346if self.wire_num > max(node.ctrl_a, node.ctrl_b):347self.image, self.rect = load_image('gate_images/not_gate_below_ctrl.png', -1)348else:349self.image, self.rect = load_image('gate_images/not_gate_above_ctrl.png', -1)350elif node.radians != 0:351self.image, self.rect = load_image('gate_images/rx_gate.png', -1)352self.rect = self.image.get_rect()353pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)354pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)355else:356self.image, self.rect = load_image('gate_images/x_gate.png', -1)357elif node_type == node_types.Y:358node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)359if node.radians != 0:360self.image, self.rect = load_image('gate_images/ry_gate.png', -1)361self.rect = self.image.get_rect()362pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)363pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)364else:365self.image, self.rect = load_image('gate_images/y_gate.png', -1)366elif node_type == node_types.Z:367node = self.circuit_grid_model.get_node(self.wire_num, self.column_num)368if node.radians != 0:369self.image, self.rect = load_image('gate_images/rz_gate.png', -1)370self.rect = self.image.get_rect()371pygame.draw.arc(self.image, MAGENTA, self.rect, 0, node.radians % (2 * np.pi), 6)372pygame.draw.arc(self.image, MAGENTA, self.rect, node.radians % (2 * np.pi), 2 * np.pi, 1)373else:374self.image, self.rect = load_image('gate_images/z_gate.png', -1)375elif node_type == node_types.S:376self.image, self.rect = load_image('gate_images/s_gate.png', -1)377elif node_type == node_types.SDG:378self.image, self.rect = load_image('gate_images/sdg_gate.png', -1)379elif node_type == node_types.T:380self.image, self.rect = load_image('gate_images/t_gate.png', -1)381elif node_type == node_types.TDG:382self.image, self.rect = load_image('gate_images/tdg_gate.png', -1)383elif node_type == node_types.IDEN:384self.image, self.rect = load_image('gate_images/iden_gate.png', -1)385elif node_type == node_types.CTRL:386# TODO: Handle Toffoli gates correctly387if self.wire_num > \388self.circuit_grid_model.get_gate_wire_for_control_node(self.wire_num, self.column_num):389self.image, self.rect = load_image('gate_images/ctrl_gate_bottom_wire.png', -1)390else:391self.image, self.rect = load_image('gate_images/ctrl_gate_top_wire.png', -1)392elif node_type == node_types.TRACE:393self.image, self.rect = load_image('gate_images/trace_gate.png', -1)394elif node_type == node_types.SWAP:395self.image, self.rect = load_image('gate_images/swap_gate.png', -1)396else:397self.image = pygame.Surface([GATE_TILE_WIDTH, GATE_TILE_HEIGHT])398self.image.set_alpha(0)399self.rect = self.image.get_rect()400401self.image.convert()402403404class CircuitGridCursor(pygame.sprite.Sprite):405"""Cursor to highlight current grid node"""406def __init__(self):407pygame.sprite.Sprite.__init__(self)408self.image, self.rect = load_image('images/circuit-grid-cursor-60px.png', -1)409self.image.convert_alpha()410411412413414