📚 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#16# TODO: Add user interaction:17# TODO: - Selecting circuit node with mouse18# TODO: - Same gate twice erases (e.g. pressing X key already on an X gate erases it)19# TODO: - If gate was rotated, make unrotated (e.g. pressing X on rotated X gate makes X)20# TODO: Use NUM_STATE_DIMS everywhere21# TODO: Fix weights on network graph22# TODO: Create UI component for adjacency matrix23# TODO: Use better looking fonts24# TODO: Modify NetworkGraph to:25# TODO: - move vertices to each's side of the cut26# TODO: Make TSP and other demos, including chemistry27# TODO: Make displays update during optimization28# TODO: Modify optimizer to fit pluggable Aqua framework29# TODO: Create network graph component?30# TODO: Update QSphere visualization and leverage it here31#32"""Demonstrate Variational Quantum Eigensolver (VQE) concepts using Qiskit and Pygame"""3334from pygame.locals import *35from qiskit import ClassicalRegister36from qiskit import execute37# from qiskit_aqua.translators.ising import maxcut38from .containers import *39from .controls.circuit_grid import *40from .model.circuit_grid_model import *41from .utils.gamepad import *42from .utils.states import NUM_QUBITS, NUM_STATE_DIMS43from .viz.expectation_grid import ExpectationGrid44from .viz.network_graph import NetworkGraph45from .controls.adjacency_matrix import AdjacencyMatrix46from .controls.button import Button4748WINDOW_SIZE = 1650, 95049NUM_OPTIMIZATION_EPOCHS = 1505152class VQEPlayground():53"""Main object for application"""54def __init__(self):55self.screen = pygame.display.set_mode(WINDOW_SIZE)56self.background = pygame.Surface(self.screen.get_size())57self.circuit_grid_model = None58self.circuit_grid = None59self.top_sprites = None60self.right_sprites = None61self.expectation_grid = None62self.network_graph = None63self.adjacency_matrix = None64self.optimize_button = None65self.circ_viz_dirty = False6667# Optimization state variables, so that the display can update while68# the optimizing algorithm is running69self.optimization_desired = False70self.optimization_initialized = False71self.optimized_rotations = None72self.cur_optimization_epoch = 073self.cur_rotation_num = 074self.min_distance = None75self.rotation_initialized = False76self.finished_rotating = True77self.rotation_iterations = 078self.proposed_cur_ang_rad = 079self.cur_ang_rad = 080self.frequent_viz_update = True8182def main(self):83if not pygame.font: print('Warning, fonts disabled')84if not pygame.mixer: print('Warning, sound disabled')8586pygame.init()8788pygame.joystick.init()89num_joysticks = pygame.joystick.get_count()9091joystick = False92if num_joysticks > 0:93joystick = pygame.joystick.Joystick(0)94joystick.init()9596self.background = pygame.Surface(self.screen.get_size())97self.background = self.background.convert()98self.background.fill(WHITE)99100pygame.font.init()101102self.circuit_grid_model = CircuitGridModel(NUM_QUBITS, 21)103104pygame.display.set_caption('VQE Playground')105106self.screen.blit(self.background, (0, 0))107pygame.display.flip()108109# Prepare objects110clock = pygame.time.Clock()111112self.circuit_grid_model.set_node(0, 0, CircuitGridNode(node_types.Y, np.pi))113self.circuit_grid_model.set_node(1, 0, CircuitGridNode(node_types.Y, np.pi))114self.circuit_grid_model.set_node(2, 0, CircuitGridNode(node_types.Y, np.pi))115self.circuit_grid_model.set_node(3, 0, CircuitGridNode(node_types.Y, np.pi))116self.circuit_grid_model.set_node(4, 0, CircuitGridNode(node_types.Y, np.pi))117118self.circuit_grid_model.set_node(1, 1, CircuitGridNode(node_types.X, 0, 0))119self.circuit_grid_model.set_node(2, 2, CircuitGridNode(node_types.X, 0, 1))120self.circuit_grid_model.set_node(3, 3, CircuitGridNode(node_types.X, 0, 2))121self.circuit_grid_model.set_node(4, 4, CircuitGridNode(node_types.X, 0, 3))122123self.circuit_grid_model.set_node(0, 5, CircuitGridNode(node_types.Y, np.pi))124self.circuit_grid_model.set_node(1, 5, CircuitGridNode(node_types.Y, np.pi))125self.circuit_grid_model.set_node(2, 5, CircuitGridNode(node_types.Y, np.pi))126self.circuit_grid_model.set_node(3, 5, CircuitGridNode(node_types.Y, np.pi))127self.circuit_grid_model.set_node(4, 5, CircuitGridNode(node_types.Y, np.pi))128129self.circuit_grid_model.set_node(1, 6, CircuitGridNode(node_types.X, 0, 0))130self.circuit_grid_model.set_node(2, 7, CircuitGridNode(node_types.X, 0, 1))131self.circuit_grid_model.set_node(3, 8, CircuitGridNode(node_types.X, 0, 2))132self.circuit_grid_model.set_node(4, 9, CircuitGridNode(node_types.X, 0, 3))133134self.circuit_grid_model.set_node(0, 10, CircuitGridNode(node_types.Y, np.pi))135self.circuit_grid_model.set_node(1, 10, CircuitGridNode(node_types.Y, np.pi))136self.circuit_grid_model.set_node(2, 10, CircuitGridNode(node_types.Y, np.pi))137self.circuit_grid_model.set_node(3, 10, CircuitGridNode(node_types.Y, np.pi))138self.circuit_grid_model.set_node(4, 10, CircuitGridNode(node_types.Y, np.pi))139140self.circuit_grid_model.set_node(1, 11, CircuitGridNode(node_types.X, 0, 0))141self.circuit_grid_model.set_node(2, 12, CircuitGridNode(node_types.X, 0, 1))142self.circuit_grid_model.set_node(3, 13, CircuitGridNode(node_types.X, 0, 2))143self.circuit_grid_model.set_node(4, 14, CircuitGridNode(node_types.X, 0, 3))144145self.circuit_grid_model.set_node(0, 15, CircuitGridNode(node_types.Y, np.pi))146self.circuit_grid_model.set_node(1, 15, CircuitGridNode(node_types.Y, np.pi))147self.circuit_grid_model.set_node(2, 15, CircuitGridNode(node_types.Y, np.pi))148self.circuit_grid_model.set_node(3, 15, CircuitGridNode(node_types.Y, np.pi))149self.circuit_grid_model.set_node(4, 15, CircuitGridNode(node_types.Y, np.pi))150151self.circuit_grid_model.set_node(1, 16, CircuitGridNode(node_types.X, 0, 0))152self.circuit_grid_model.set_node(2, 17, CircuitGridNode(node_types.X, 0, 1))153self.circuit_grid_model.set_node(3, 18, CircuitGridNode(node_types.X, 0, 2))154self.circuit_grid_model.set_node(4, 19, CircuitGridNode(node_types.X, 0, 3))155156self.circuit_grid_model.set_node(0, 20, CircuitGridNode(node_types.Y, np.pi))157self.circuit_grid_model.set_node(1, 20, CircuitGridNode(node_types.Y, np.pi))158self.circuit_grid_model.set_node(2, 20, CircuitGridNode(node_types.Y, np.pi))159self.circuit_grid_model.set_node(3, 20, CircuitGridNode(node_types.Y, np.pi))160self.circuit_grid_model.set_node(4, 20, CircuitGridNode(node_types.Y, np.pi))161162circuit = self.circuit_grid_model.compute_circuit()163164initial_adj_matrix = np.array([165[0, 3, 1, 3, 0],166[3, 0, 0, 0, 2],167[1, 0, 0, 3, 0],168[3, 0, 3, 0, 2],169[0, 2, 0, 2, 0]170])171172# maxcut_op, maxcut_shift = maxcut.get_maxcut_qubitops(initial_adj_matrix)173# # print("maxcut_op: ", maxcut_op, ", maxcut_shift: ", maxcut_shift)174#175# # TODO: Find different approach of calculating and retrieving diagonal176# maxcut_op._paulis_to_matrix()177# eigenvectors = maxcut_op._dia_matrix178179self.adjacency_matrix = AdjacencyMatrix(950, 10, initial_adj_matrix)180self.expectation_grid = ExpectationGrid(circuit,181self.adjacency_matrix.adj_matrix_numeric)182183self.network_graph = NetworkGraph(self.adjacency_matrix.adj_matrix_numeric)184self.optimize_button = Button("Optimize", 150, 40)185186self.top_sprites = HBox(50, 20, self.network_graph, self.optimize_button)187self.right_sprites = VBox(1010, 0, self.expectation_grid)188189self.circuit_grid = CircuitGrid(10, 540, self.circuit_grid_model)190self.screen.blit(self.background, (0, 0))191192self.top_sprites.draw(self.screen)193self.right_sprites.draw(self.screen)194self.circuit_grid.draw(self.screen)195self.adjacency_matrix.draw(self.screen)196pygame.display.flip()197198gamepad_repeat_delay = 100199gamepad_neutral = True200gamepad_pressed_timer = 0201gamepad_last_update = pygame.time.get_ticks()202203# Main Loop204going = True205while going:206clock.tick(30)207208pygame.time.wait(10)209210if joystick:211gamepad_move = False212joystick_hat = joystick.get_hat(0)213214if joystick_hat == (0, 0):215gamepad_neutral = True216gamepad_pressed_timer = 0217else:218if gamepad_neutral:219gamepad_move = True220gamepad_neutral = False221else:222gamepad_pressed_timer += pygame.time.get_ticks() - gamepad_last_update223if gamepad_pressed_timer > gamepad_repeat_delay:224gamepad_move = True225gamepad_pressed_timer -= gamepad_repeat_delay226if gamepad_move:227if joystick_hat == (-1, 0):228self.move_update_circuit_grid_display(MOVE_LEFT)229elif joystick_hat == (1, 0):230self.move_update_circuit_grid_display(MOVE_RIGHT)231elif joystick_hat == (0, 1):232self.move_update_circuit_grid_display(MOVE_UP)233elif joystick_hat == (0, -1):234self.move_update_circuit_grid_display(MOVE_DOWN)235gamepad_last_update = pygame.time.get_ticks()236237# Check left thumbstick position238# left_thumb_x = joystick.get_axis(0)239# left_thumb_y = joystick.get_axis(1)240241# Handle Input Events242for event in pygame.event.get():243pygame.event.pump()244245# if event.type != MOUSEMOTION:246# print("event: ", event)247if event.type == QUIT:248pygame.quit()249print("Quitting VQE Playground")250return251# going = False252253elif event.type == MOUSEBUTTONDOWN:254if self.optimize_button.rect.collidepoint(event.pos):255if self.optimize_button.get_enabled():256self.optimize_button.set_enabled(False)257self.optimization_desired = True258else:259for idx, picker in enumerate(self.adjacency_matrix.number_pickers_list):260if picker.rect.collidepoint(event.pos):261self.adjacency_matrix.handle_element_clicked(picker)262self.expectation_grid.set_adj_matrix(self.adjacency_matrix.adj_matrix_numeric)263self.circ_viz_dirty = True264if self.adjacency_matrix.adj_matrix_graph_dirty:265self.network_graph.set_adj_matrix(self.adjacency_matrix.adj_matrix_numeric)266self.adjacency_matrix.adj_matrix_graph_dirty = False267self.expectation_grid.basis_state_dirty = True268269elif event.type == JOYBUTTONDOWN:270if event.button == BTN_A:271# Place X gate272self.circuit_grid.handle_input_x()273self.circ_viz_dirty = True274elif event.button == BTN_X:275# Place Y gate276self.circuit_grid.handle_input_y()277self.circ_viz_dirty = True278elif event.button == BTN_B:279# Place Z gate280self.circuit_grid.handle_input_z()281self.circ_viz_dirty = True282elif event.button == BTN_Y:283# Place Hadamard gate284self.circuit_grid.handle_input_h()285self.circ_viz_dirty = True286elif event.button == BTN_RIGHT_TRIGGER:287# Delete gate288self.circuit_grid.handle_input_delete()289self.circ_viz_dirty = True290elif event.button == BTN_RIGHT_THUMB:291# Add or remove a control292self.circuit_grid.handle_input_ctrl()293self.circ_viz_dirty = True294295elif event.type == JOYAXISMOTION:296# print("event: ", event)297if event.axis == AXIS_RIGHT_THUMB_X and joystick.get_axis(AXIS_RIGHT_THUMB_X) >= 0.95:298self.circuit_grid.handle_input_rotate(np.pi / 8)299self.circ_viz_dirty = True300if event.axis == AXIS_RIGHT_THUMB_X and joystick.get_axis(AXIS_RIGHT_THUMB_X) <= -0.95:301self.circuit_grid.handle_input_rotate(-np.pi / 8)302self.circ_viz_dirty = True303if event.axis == AXIS_RIGHT_THUMB_Y and joystick.get_axis(AXIS_RIGHT_THUMB_Y) <= -0.95:304self.circuit_grid.handle_input_move_ctrl(MOVE_UP)305self.circ_viz_dirty = True306if event.axis == AXIS_RIGHT_THUMB_Y and joystick.get_axis(AXIS_RIGHT_THUMB_Y) >= 0.95:307self.circuit_grid.handle_input_move_ctrl(MOVE_DOWN)308self.circ_viz_dirty = True309310elif event.type == KEYDOWN:311index_increment = 0312if event.key == K_ESCAPE:313going = False314elif event.key == K_a:315self.move_update_circuit_grid_display(MOVE_LEFT)316elif event.key == K_d:317self.move_update_circuit_grid_display(MOVE_RIGHT)318elif event.key == K_w:319self.move_update_circuit_grid_display(MOVE_UP)320elif event.key == K_s:321self.move_update_circuit_grid_display(MOVE_DOWN)322elif event.key == K_x:323self.circuit_grid.handle_input_x()324self.circ_viz_dirty = True325elif event.key == K_y:326self.circuit_grid.handle_input_y()327self.circ_viz_dirty = True328elif event.key == K_z:329self.circuit_grid.handle_input_z()330self.circ_viz_dirty = True331elif event.key == K_h:332self.circuit_grid.handle_input_h()333self.circ_viz_dirty = True334elif event.key == K_BACKSLASH:335self.circuit_grid.handle_input_delete()336self.circ_viz_dirty = True337elif event.key == K_c:338# Add or remove a control339self.circuit_grid.handle_input_ctrl()340self.circ_viz_dirty = True341elif event.key == K_UP:342# Move a control qubit up343self.circuit_grid.handle_input_move_ctrl(MOVE_UP)344self.circ_viz_dirty = True345elif event.key == K_DOWN:346# Move a control qubit down347self.circuit_grid.handle_input_move_ctrl(MOVE_DOWN)348self.circ_viz_dirty = True349elif event.key == K_LEFT:350# Rotate a gate351self.circuit_grid.handle_input_rotate(-np.pi/8)352self.circ_viz_dirty = True353elif event.key == K_RIGHT:354# Rotate a gate355self.circuit_grid.handle_input_rotate(np.pi / 8)356self.circ_viz_dirty = True357elif event.key == K_o:358if self.optimize_button.get_enabled():359self.optimize_button.set_enabled(False)360self.optimization_desired = True361362if self.optimization_desired:363if self.cur_optimization_epoch < NUM_OPTIMIZATION_EPOCHS:364if not self.optimization_initialized:365self.expectation_grid.draw_expectation_grid()366rotation_gate_nodes = self.circuit_grid_model.get_rotation_gate_nodes()367368self.optimized_rotations = np.full(len(rotation_gate_nodes), np.pi)369self.cur_optimization_epoch = 0370self.cur_rotation_num = 0371372rotation_bounds = np.zeros((len(rotation_gate_nodes), 2))373374self.optimization_initialized = True375376self.optimize_rotations(self.expectation_value_objective_function,377self.circuit_grid, self.expectation_grid, rotation_gate_nodes)378379# print('opt_rotations: ', self.optimized_rotations)380381cost, basis_state_str = self.expectation_grid.calc_expectation_value()382print('cost: ', cost, 'basis_state_str: ', basis_state_str)383384solution = np.zeros(NUM_STATE_DIMS)385for idx, char in enumerate(basis_state_str):386solution[idx] = int(char)387388# TODO: Uncomment to update display more often?389# self.network_graph.set_solution(solution)390391else:392self.optimization_initialized = False393self.optimization_desired = False394self.cur_optimization_epoch = 0395self.optimize_button.set_enabled(True)396397# Select top-left node in circuit, regardless of gate type398self.circuit_grid.highlight_selected_node(0, 0)399400self.circ_viz_dirty = True401print("Finished")402# self.network_graph.set_solution(solution)403404if self.expectation_grid.basis_state_dirty:405cost, basis_state_str = self.expectation_grid.calc_expectation_value()406407solution = np.zeros(NUM_STATE_DIMS)408for idx, char in enumerate(basis_state_str):409solution[idx] = int(char)410411self.network_graph.set_solution(solution)412413self.circ_viz_dirty = True414self.expectation_grid.basis_state_dirty = False415416if self.circ_viz_dirty:417self.update_circ_viz()418self.circ_viz_dirty = False419420pygame.quit()421422def optimize_rotations(self, objective_function, circuit_grid, expectation_grid, rotation_gate_nodes):423424move_radians = np.pi / 8425426# For each rotation this will be either 1 or -1, signifying direction of movement427unit_direction_array = np.ones(len(self.optimized_rotations))428429self.min_distance = objective_function(circuit_grid, expectation_grid, rotation_gate_nodes)430431# print('self.cur_optimization_epoch: ', self.cur_optimization_epoch)432# print('self.cur_rotation_num: ', self.cur_rotation_num)433if self.cur_optimization_epoch < NUM_OPTIMIZATION_EPOCHS:434if self.cur_rotation_num < len(self.optimized_rotations):435if not self.rotation_initialized:436self.cur_ang_rad = self.optimized_rotations[self.cur_rotation_num]437self.proposed_cur_ang_rad = self.cur_ang_rad438439# Highlight gate being operated on440cur_wire_num = rotation_gate_nodes[self.cur_rotation_num].wire_num441cur_column_num = rotation_gate_nodes[self.cur_rotation_num].column_num442self.circuit_grid.highlight_selected_node(cur_wire_num, cur_column_num)443if self.frequent_viz_update:444self.circ_viz_dirty = True445446self.rotation_initialized = True447448# Decide whether to increase or decrease angle449unit_direction_array[self.cur_rotation_num] = 1450if self.cur_ang_rad > np.pi:451unit_direction_array[self.cur_rotation_num] = -1452self.proposed_cur_ang_rad += move_radians * unit_direction_array[self.cur_rotation_num]453if 0.0 <= self.proposed_cur_ang_rad < np.pi * 2 + 0.01:454self.optimized_rotations[self.cur_rotation_num] = self.proposed_cur_ang_rad455456temp_distance = objective_function(circuit_grid, expectation_grid, rotation_gate_nodes)457if temp_distance > self.min_distance:458# Moving in the wrong direction so restore the angle in the array and switch direction459self.optimized_rotations[self.cur_rotation_num] = self.cur_ang_rad460unit_direction_array[self.cur_rotation_num] *= -1461else:462# Moving in the right direction so use the proposed angle463self.cur_ang_rad = self.proposed_cur_ang_rad464self.min_distance = temp_distance465466self.finished_rotating = False467self.rotation_iterations = 0468469elif self.rotation_initialized and not self.finished_rotating:470self.rotation_iterations += 1471self.proposed_cur_ang_rad += move_radians * unit_direction_array[self.cur_rotation_num]472if 0.0 <= self.proposed_cur_ang_rad <= np.pi * 2 + 0.01:473self.optimized_rotations[self.cur_rotation_num] = self.proposed_cur_ang_rad474temp_distance = objective_function(circuit_grid, expectation_grid, rotation_gate_nodes)475476if temp_distance >= self.min_distance: # TODO: Better?477# if temp_distance > self.min_distance:478# Distance is increasing so restore the angle in the array and leave the loop479self.optimized_rotations[self.cur_rotation_num] = self.cur_ang_rad480self.finished_rotating = True481self.cur_rotation_num += 1482self.rotation_initialized = False483elif self.rotation_iterations > np.pi * 2 / move_radians:484print("Unexpected: self.rotation_iterations: ", self.rotation_iterations)485self.finished_rotating = True486self.cur_rotation_num += 1487self.rotation_initialized = False488else:489# Distance is not increasing, so use the proposed angle490self.cur_ang_rad = self.proposed_cur_ang_rad491self.min_distance = temp_distance492if self.frequent_viz_update:493self.circ_viz_dirty = True494else:495self.finished_rotating = True496self.cur_rotation_num += 1497self.rotation_initialized = False498499# self.cur_rotation_num += 1500else:501self.cur_rotation_num = 0502self.cur_optimization_epoch += 1503# print('self.min_distance: ', self.min_distance)504505objective_function(circuit_grid, expectation_grid, rotation_gate_nodes)506# print('exp_val: ', expectation_grid.calc_expectation_value())507508509def expectation_value_objective_function(self, circuit_grid,510expectation_grid, rotation_gate_nodes):511for idx in range(len(rotation_gate_nodes)):512circuit_grid.rotate_gate_absolute(rotation_gate_nodes[idx], self.optimized_rotations[idx])513expectation_grid.set_circuit(circuit_grid.circuit_grid_model.compute_circuit())514cost, basis_state = expectation_grid.calc_expectation_value()515516# print("self.optimized_rotations: ", self.optimized_rotations, ", cost: ", cost, ", basis_state: ", basis_state)517return cost518519def update_circ_viz(self):520# print("in update_circ_viz")521self.screen.blit(self.background, (0, 0))522circuit = self.circuit_grid_model.compute_circuit()523self.expectation_grid.set_circuit(circuit)524self.top_sprites.arrange()525self.right_sprites.arrange()526self.top_sprites.draw(self.screen)527self.right_sprites.draw(self.screen)528self.adjacency_matrix.arrange()529self.adjacency_matrix.draw(self.screen)530self.circuit_grid.draw(self.screen)531pygame.display.flip()532533def move_update_circuit_grid_display(self, direction):534self.circuit_grid.move_to_adjacent_node(direction)535self.circuit_grid.draw(self.screen)536pygame.display.flip()537538539if __name__ == "__main__":540VQEPlayground().main()541542543