Skip to content

Instantly share code, notes, and snippets.

@tudoanh
Forked from quantumjim/Engine.py
Created May 16, 2022 15:06
Show Gist options
  • Save tudoanh/4cbf7f16c9e1800e0a67aa7c159fbeec to your computer and use it in GitHub Desktop.
Save tudoanh/4cbf7f16c9e1800e0a67aa7c159fbeec to your computer and use it in GitHub Desktop.
Gamified tutorial for QISKit to run on PythonAnywhere
# -*- coding: utf-8 -*-
import math
def ClearScreen () :
# DESCRIPTION:
# Prints a whole bunch of space to screen
print("\n"*200)
def ColouredString (message,colour) :
# DESCRIPTION:
# Prints in a colour specified by the colour in ["r","b","p"] and then sets what follows back to white
# first the colour
coloured_message = ""
if colour=="r": # red
coloured_message += "\x1b[1;31m"
elif colour=="g": # green
coloured_message += "\x1b[1;32m"
elif colour=="b": # blue
coloured_message += "\x1b[1;34m"
elif colour=="p": # purple
coloured_message += "\x1b[1;35m"
else: # black (which should actually be the default text colour)
coloured_message += "\x1b[0m"
# then the message
coloured_message += message
# and back to black
coloured_message += "\x1b[0m"
return coloured_message
def Expect2Prob ( expect ) :
# DESCRIPTION:
# Converts expectation value to a probability of getting the outcome 1.
return (1-expect)/2
def Expect2Polar ( boxes ) :
# DESCRIPTION:
# Takes the contents of an X and Z box and turns it into polar coordinates for the Bloch circle.
# INPUT:
# {String: Float} boxes Should have entries for "X" and "Z", which serve as the horizontal and vertical boxes respectively.
# OUTPUT:
# [Float] [ degrees, radius ] degrees is between 0 and 1. It is the angle clockwise from the top as a fraction of 2*Pi. Radius is how far out the point is. Will be 1 for pure states and 0 for maximally mixed.
radius = math.sqrt( boxes["X"]**2 + boxes["Z"]**2)
degrees = 0 # default value of 0
if radius>0.0:
degrees = math.acos( -boxes["Z"] / radius ) / (2*math.pi)
if boxes["X"]>=0.0:
degrees = 1 - degrees
return [ degrees, radius ]
def Polar2Expect ( polar_coords ) :
# DESCRIPTION:
# As Boxes2Polar, but with inputs and outputs reversed
boxes = {}
boxes["X"] = -polar_coords[1] * math.sin( 2*math.pi*polar_coords[0])
boxes["Z"] = -polar_coords[1] * math.cos( 2*math.pi*polar_coords[0])
return boxes
def ExchangeBoxes ( state, box1, box2 ) :
# DESCRIPTION:
# Given a state and two of its boxes, the values for these boxes are exchanged
output_state = state
temp = output_state[box1]
output_state[box1] = output_state[box2]
output_state[box2] = temp
return output_state
def Swap ( state ) :
# DESCRIPTION:
# Determines the state after a swap gate
# INPUT:
# {String: Float} state Two qubit state.
# OUTPUT:
# {String: Float} state Transformed version of input state.
swapped_state = {}
for box in state.keys():
swapped_state[box[1]+box[0]] = state[box]
return swapped_state
def ApplyGate ( state, gate, qubit ) :
# DESCRIPTION:
# Transforms the given state according to the given gate.
# INPUT:
# {String: Float} state Full two qubit state. Needs entries for XI, ZI, IZ, IX, XX, XZ, ZX, ZZ and YY
# String gate QISKit style str to specify a gate (can be x, z, h, q, qdg, or cz)
# Int qubit Qubit on which single qubit gate is applied. Unused for CZ.
# OUTPUT:
# {String: Float} state Transformed version of input state.
# We begin constructing the output state by copying the input state.
output_state = state
# Single qubit gates require special treatment, so we deal with these separately.
if gate in ["x","z","h","q","qdg"] :
# Single qubit gates act on pairs of boxes. These are the pairs that form Bloch circles.
# For qubit 0, these pairs are (XI, ZI), (XX,ZX) and (XZ,ZZ).
# For qubit 2 they are (IX, IZ), (XX,XZ) and (ZX,ZZ).
# Here we loop over and construct the three pairs, which in each case will be (p[0],p[1]).
for rc in ["I","X","Z"] :
box_name = {"X":rc,"Z":rc}
for p in ["X","Z"] :
if qubit=="0" :
box_name[p] = p + box_name[p]
else :
box_name[p] = box_name[p] + p
# What we do to the pairs depends on the gate we apply.
if gate=="x": # invert Z box and invert YY
output_state[box_name["Z"]] = -output_state[box_name["Z"]]
output_state["YY"] = -output_state["YY"]
elif gate=="z" :# invert X box and invert YY
output_state[box_name["X"]] = -output_state[box_name["X"]]
output_state["YY"] = -output_state["YY"]
elif gate=="h" : # exchange X and Z boxes and invert YY
output_state = ExchangeBoxes( output_state, box_name["X"], box_name["Z"])
output_state["YY"] = -output_state["YY"]
elif gate in ["q","qdg"] :
polar_coords = Expect2Polar( { "X":output_state[box_name["X"]], "Z":output_state[box_name["Z"]] } ) # convert to polar coords
if gate=="q" : # change angle according to the rotation
polar_coords[0] += 1/8
else :
polar_coords[0] -= 1/8
# convert back to boxes
output_boxes = Polar2Expect(polar_coords)
for p in ["X","Z"] :
output_state[box_name[p]] = output_boxes[p]
else :
print("Error: Unknown single qubit gate")
# Now for the two qubit gates
elif gate=="cz" :
# exchange contents of XI and XZ
output_state = ExchangeBoxes( output_state, "XI", "XZ")
# exchange contents of IX and ZX
output_state = ExchangeBoxes( output_state, "IX", "ZX")
# exchange contents of XX and YY
output_state = ExchangeBoxes( output_state, "XX", "YY")
elif gate=="cx" :
if qubit=="1" :
# exchange contents of XI and XX
output_state = ExchangeBoxes( output_state, "XI", "XX")
# exchange contents of IZ and ZZ
output_state = ExchangeBoxes( output_state, "IZ", "ZZ")
# exchange contents of XZ and YY
output_state = ExchangeBoxes( output_state, "XZ", "YY")
# invert XZ
output_state["XZ"] = -output_state["XZ"]
elif qubit=="0" :
# exchange contents of ZI and ZZ
output_state = ExchangeBoxes( output_state, "ZI", "ZZ")
# exchange contents of IX and XX
output_state = ExchangeBoxes( output_state, "IX", "XX")
# exchange contents of ZX and YY
output_state = ExchangeBoxes( output_state, "ZX", "YY")
# invert ZX
output_state["ZX"] = -output_state["ZX"]
output_state["YY"] = -output_state["YY"] # invert YY
else :
print("Error: Unknown gate")
return output_state
def MakeState ( initial_state, gates ) :
# DESCRIPTION:
# Constructs a state given an initial state and list of gates
# Can also be used to construct an initial state such that the target state and solution are as specified by the input
# In this case, note that the inverse of the desired solution must be supplied
# Put simply, this means that the first gate should be on the right, and the substition q <-> qdg should be used
state = initial_state
for gate in gates :
state = ApplyGate( state, gate[0], gate[1] )
return state
def MakeCell ( cell_state ) :
# DESCRIPTION:
# Prints a single box or Bloch circle, depending on the input state.
# INPUT:
# {String: Float} cell_state With key "X", value is the expectation value for horizontal red box.
# Similarly for "Z" and the vertical blue box, and "Y" for a diagonal purple box.
# Note that a cell that uses "Y" will correspond to XZ or ZZ boxes. Not a Y box.
# OUTPUT:
# [String] lines List of 12 lines, which when printed sequentially will display the cell.
# PROCESS:
# When a single key is supplied, the corresponding box is printed.
# When both "X" and "Z" are supplied, the boxes are combined into a Bloch circle.
# In all cases, the level on the box is first converted from the expectation value to the probability.
reso = {"X":17, "Z":9, "Y":13} # number of characters used for each type of box (including center)
bottom = "───────────────────────" # bottom border
right = "|" # right border
# The 8 points around the circle and one in the middle. Default to a dot
c = []
c.append("˙")
c.append(".")
c.append("·")
c.append("˙")
c.append(".")
c.append("˙")
c.append("·")
c.append(".")
c.append(" ")
# When a Bloch sphere is displayed, the point corresponding to the state is denoted by "*"
# This is displayed only for pretty mixed states (radius <0.25) and pretty pure ones (radius>0.75)
if ( "X" in cell_state.keys() and "Z" in cell_state.keys() ) : # both X and Z boxes need to be present in the cell
if ( Expect2Polar( cell_state )[1]<0.25 ) : # if state is pretty mixed, point is shown in the center
c[8] = "*"
elif Expect2Polar( cell_state )[1]>0.75 : # if state is pretty pure, it is one of the 8 around the edge
point = 0
degree = Expect2Polar( cell_state )[0]
for eighth in range(1,8) :
if degree>(float(eighth)/8 - 1.0/16) and degree<=(float(eighth)/8 + 1.0/16 ) :
point = eighth
if point in [1,4,7] :
c[ point ] = "⁎"
else :
c[ point ] = "*"
# Strings for the three boxes. Blank if unused, but filled with █ and ░ if used to represent how 'filled' they are.
b = {"X":[], "Z":[], "Y":[] }
for box in [ "X", "Z", "Y" ] :
this_b = []
if box not in cell_state.keys() :
for _ in range(1,reso[box]+1) :
this_b.append(" ")
else :
prob = Expect2Prob( cell_state[box] ) # convert from expectation value to prob of a 1 outcome
fill = int( float(reso[box]) * prob )
if (prob>0.5 and fill != reso[box]) : # if over half filled, but not completely filled, round up (for clear visuals)
fill += 1
for _ in range(fill) :
if box == "X" :
this_b.append( ColouredString( "█" , "r" ) )
elif box == "Z" :
this_b.append( ColouredString( "█" , "b" ) )
elif box == "Y" :
this_b.append( ColouredString( "█" , "p" ) )
for _ in range(fill,reso[box]) :
if box == "X" :
this_b.append( ColouredString( "░" , "r" ) )
elif box == "Z" :
this_b.append( ColouredString( "░" , "b" ) )
elif box == "Y" :
this_b.append( ColouredString( "░" , "p" ) )
b[box] = this_b
# center is X with the colour of the cell, unless the cell is empty
if "Y" in cell_state.keys() :
c[8] = ColouredString( b["Y"][int(reso["Y"]/2)], "p" )
elif "X" in cell_state.keys() :
c[8] = ColouredString( b["X"][int(reso["X"]/2)], "r" )
elif "Z" in cell_state.keys() :
c[8] = ColouredString( b["Z"][int(reso["Z"]/2)], "b" )
b["X"][int(reso["X"]/2)] = c[8] # The center will be the ninth element of c instead
# Declare and construct the lines.
lines = []
if cell_state=={"II":1.0} :
for _ in range(11) :
lines.append( " "+right )
else :
lines.append( " .· "+c[0]+" ·. "+right )
lines.append( " "+c[7]+"˙ "+b["Z"][8]+" ˙"+c[1]+" "+right )
lines.append( " · "+b["Z"][7]+" "+b["Y"][11]+b["Y"][12]+" · "+right )
lines.append( " · "+b["Z"][6]+" "+b["Y"][9]+b["Y"][10]+" · "+right )
lines.append( " "+b["Z"][5]+""+b["Y"][7]+b["Y"][8]+" "+right )
lines.append( ""+c[6]+" "+"".join(b["X"])+" "+c[2]+" "+right )
lines.append( " "+b["Y"][4]+b["Y"][5]+""+b["Z"][3]+" "+right )
lines.append( " ˙ "+b["Y"][2]+b["Y"][3]+" "+b["Z"][2]+" ˙ "+right )
lines.append( " · "+b["Y"][0]+b["Y"][1]+" "+b["Z"][1]+" · "+right )
lines.append( " "+c[5]+". "+b["Z"][0]+" ."+c[3]+" "+right )
lines.append( " ˙· "+c[4]+" ·˙ "+right )
lines.append( bottom + right )
return lines
def MakeGrid ( state, shown_qubit, active_qubit, bloch ) :
# DESCRIPTION:
# The inputs describe what the grid is doing. This function puts all that information together to actually make the grid.
# INPUT:
# {String: Float} state Full two qubit state. Needs entries for XI, ZI, IZ, IX, XX, XZ, ZX, ZZ and YY
# Int shown_qubit If this is 0 or 1, only the two boxes of that qubit are shown. Otherwise, all are shown.
# Int active_qubit If this is 0 or 1, the diagonals are straightened according to their function for this qubit. Otherwise they remain diagonal.
# Bool bloch Whether or not to display Bloch circles for the qubit specified above
# OUTPUT:
# [String] grid_lines An array of strs that represent the grid
cells = { "XI":[], "ZI":[], "XX":[],"XZ":[],"ZX":[],"ZZ":[],"YY":[],"II":[], "IX":[], "IZ":[] }
# determine which cells behave in which way (I is blank, B is Bloch circle, X and Z are horizontal and vertical boxes and Y are diagonal)
grid_lines = []
cell_types = {}
if bloch :
if shown_qubit==0 :
cell_types = {"I":["II","ZI","XX","XZ","ZX","ZZ","YY","IX","IZ"],"X":[],"Y":[],"Z":[],"B":["XI"]} # shown_qubit is used as active qubit
elif shown_qubit==1 :
cell_types = {"I":["II","XI","ZI","XX","XZ","ZX","ZZ","YY","IZ"],"X":[],"Y":[],"Z":[],"B":["IX"]} # shown_qubit is used as active qubit
else :
if active_qubit==0 :
cell_types = {"I":["II","ZI","ZX","ZZ"],"X":["IX"],"Y":[],"Z":["IZ"],"B":["XI","XX","XZ"]}
elif active_qubit==1 :
cell_types = {"I":["II","IZ","XZ","ZZ"],"X":["XI"],"Y":[],"Z":["ZI"],"B":["IX","XX","ZX"]} # these are the same as above but with strs reversed
else :
print("Error: A valid qubit must be chosen to display a Bloch circle")
else :
if shown_qubit==0 :
cell_types = {"I":["II","XX","XZ","ZX","ZZ","YY","IX","IZ"],"X":["XI"],"Y":[],"Z":["ZI"],"B":[]}
elif shown_qubit==1 :
cell_types = {"I":["II","XI","ZI","XX","XZ","ZX","ZZ","YY","IX","IZ"],"X":["IX"],"Y":[],"Z":["IZ"],"B":[]}
else :
# Y for diagonal boxes, since there is no active qubit
cell_types = {"I":["II"],"X":["XI","IX","XX"],"Y":["XZ","ZX"],"Z":["ZI","IZ","ZZ"],"B":[]}
# make blank cell
for cell in cell_types["I"] :
if cell=="II" :
cells[cell] = MakeCell( {"II":1.0} )
else :
cells[cell] = MakeCell( {} )
# make cells with horizontal and vertical boxes
for cell_type in ["X","Z"] :
for cell in cell_types[cell_type] :
cells[cell] = MakeCell( { cell_type: state[cell] } )
# make cells with diagonal boxes
for cell in cell_types["Y"] :
if active_qubit in [0,1] :
index = active_qubit
cells[cell] = MakeCell( { str(cell[index]):state[cell] } ) # qubit dependent cell type is used
else :
cells[cell] = MakeCell( { "Y":state[cell] } ) # same as in X,Z loop above
# make cells with Bloch circle
for cell in cell_types["B"] :
index = (1-active_qubit)
if active_qubit==0 :
cells[cell] = MakeCell( { "X":state["X"+str(cell[index])], "Z":state["Z"+str(cell[index])] } )
else :
cells[cell] = MakeCell( { "X":state[str(cell[index])+"X"], "Z":state[str(cell[index])+"Z"] } )
for l in range(12) :
grid_lines.append( cells["II"][l] + cells["ZI"][l] + cells["XI"][l] )
for l in range(12) :
grid_lines.append( cells["IZ"][l] + cells["ZZ"][l] + cells["XZ"][l] )
for l in range(12) :
grid_lines.append( cells["IX"][l] + cells["ZX"][l] + cells["XX"][l] )
return grid_lines
def PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, allowed_gates, required_gates ) :
print("\n"*3)
# set up the screen: job 1
# text at the top
ClearScreen ()
print("\nLevel "+str(level+1)+"\n")
for line in intro[level] :
print("> " + line + "...")
if program != [] :
print("\nYour QISKit program so far\n")
for line in program :
print(line)
print("\n")
# set up the screen: job 2
# the grid
print("\n")
grid_lines = MakeGrid( state, shown_qubit, active_qubit, bloch )
for line in grid_lines :
print(" "+line)
# set up the screen: job 3
# state allowed gates
for qubit in allowed_gates.keys() :
gate_list = ""
for gate in allowed_gates[qubit].keys() :
if required_gates[qubit][gate] > 0 :
gate_list += " "+gate+" (use exactly "+str(required_gates[qubit][gate])+" times)"
elif allowed_gates[qubit][gate]==0:
gate_list += " "+gate
if gate_list!="" :
if qubit=="both" :
print("\nAllowed two qubit operations:")
else :
print("\nAllowed operations for qubit " + qubit + ":")
print(gate_list)
return input("\n"+message+"\n")
def WriteQISKit( gate, qubit, qubits_used, program, mode):
if mode=="5":
regL = ""
regR = ""
else:
regL = "qubit["
regR = "]"
if gate=="cz" :
program.append("program.cz( "+regL+qubits_used[0]+regR+", "+regL+qubits_used[1]+regR+" )")
elif gate=="cx" :
if qubit==qubits_used[0] :
program.append("program.cx( "+regL+qubits_used[1]+regR+", "+regL+qubits_used[0]+" )")
else :
program.append("program.cx( "+regL+qubits_used[0]+regR+", "+regL+qubits_used[1]+" )")
elif gate in ["q","qdg"]:
sign = "-"*(gate=="qdg")
program.append("program.u3( "+sign+"math.pi/4,0,0, "+regL+qubit+regR+" )")
else :
program.append("program."+gate+"( "+regL+qubit+regR+" )")
def MakeTarget ( initial_state, gates ):
if gates=='swap':
target = Swap( initial_state )
else:
target = MakeState( initial_state, gates )
target_string = "\n"
for line in MakeGrid(target,2,2,False):
target_string += " " + line + "\n"
return target, target_string
# -*- coding: utf-8 -*-
# Created by James Wootton
# Copyright © 2018 University of Basel. All rights reserved.
from Engine import *
# Story Mode
def GetLevelStory ( ) :
state_list = []
success_condition_list = []
qubits_used_list = []
allowed_gates_list = []
intro = []
outro = []
#1
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":3}, "1":{}, "both":{} } )
intro.append( ["In these puzzles, you'll see a grid and be given a task",
"Your job is to complete that task by using quantum programming",
"Your first job concerns a single qubit, which we call qubit 0",
"The state of this is visualized on the grid using two coloured boxes",
"Each is either on, off, or something in between",
"In the first puzzle, your job is to turn the blue box off using the command known as x",
"One x should be enough but, to make sure it's working properly, do it three times instead",
"TARGET: Turn off the blue box, and use the x command 3 times"] )
outro.append( ["Great!",
"The x command turns the blue box on and off, and does nothing to the red box"] )
#2
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{}, "1":{"x":3}, "both":{} } )
intro.append( ["Now we'll look at another qubit, called qubit 1",
"It's also described by two boxes. But because it's a different qubit, they are in a different place",
"To reset it, and to test out the x, do the same as before",
"TARGET: Turn off the blue box, and use the x command 3 times"] )
outro.append( ["Great!",
"Now you've gotten the hang of x, you won't need to repeat it 3 times any more."] )
#3
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0}, "1":{}, "both":{} } )
intro.append( ["Now we move on to another chip, with another qubit 0. But what is a qubit?",
"A qubit is a quantum object that can be turned into a bit",
"There are many ways we can do this, and the result we get depends on the method we use",
"The two boxes represent our two favourite methods of extracting a bit",
"We usually use the blue box: If it's 'off', we get 0; if it's 'on' we get 1",
"TARGET: Turn off the blue box"] )
outro.append( ["Great!","You can also learn about how we visualze the qubits at\n https://medium.com/qiskitters/visualizing-bits-and-qubits-9af287047b28 ","Now for the next qubit"] )
#4
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{}, "1":{"x":0}, "both":{} } )
intro.append( ["Looks like this will just be another blue box to turn off",
"TARGET: Turn off the blue box"] )
outro.append( ["Great!","Now on to the next chip"] )
#5
state_list.append( {"XI":1.0, "ZI":0.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"h":3}, "1":{}, "both":{} } )
intro.append( ["This chip has a new operation enabled, called h",
"It swaps the contents of the two boxes",
"To test it out, do the old trick of repeating three times",
"TARGET: Turn off the blue box, and use the h command 3 times"] )
outro.append( ["Great!",
"Now for the next qubit"] )
#6
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":1.0, "IZ":0.0} )
success_condition_list.append( {"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{}, "1":{"x":3,"h":0}, "both":{} } )
intro.append( ["Have you noticed that x has no effect when the blue box is only half on?",
"It inverts what the box is doing: off becomes on, on becomes off, but half remains half",
"TARGET: Turn off the blue box, and use the x command 3 times"] )
outro.append( ["Great!",
"By the way, extracting an output from a box that's half on would give a random result"] )
#7
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"z":0,"h":0}, "1":{}, "both":{} } )
intro.append( ["The x operation isn't working on this chip",
"But you will have a new operation, called z, which inverts the red box instead",
"You'll need work out how to reset the qubits with z and h",
"TARGET: Turn off the blue box without using the x command"] )
outro.append( ["Great!","Now for the next qubit"] )
#8
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":1.0, "IZ":0.0} )
success_condition_list.append( {"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{}, "1":{"z":0,"h":0}, "both":{} } )
intro.append( ["The red boxes represent another way to get a bit out of a qubit",
"Again, 'off' gives you a 0 and 'on' gives you a 1."
"The red box can only be certain of the result it would give when the blue box is completely random",
"Qubits only have a limited amount of certainty to go round",
"The h command swaps the results you'd get for blue and red",
"TARGET: Move the off to blue and the randomness to red"] )
outro.append( ["Great!","Now on to the next chip"] )
#9
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":1.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["Now you know the basic tools, you can tackle both qubits at once",
"You'll see the four boxes you are used to, which represent the two qubits",
"There will be some extra ones too, but don't worry about these for now",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!","Now on to the next chip"] )
#10
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":0.0,"XZ":1.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"z":0,"h":0}, "1":{"z":0,"h":0}, "both":{} } )
intro.append( ["Another chip with the x turned off",
"So another chance to practice your workarounds",
"TARGET: Turn off the blue boxes without using the x command"] )
outro.append( ["Great!","Now on to the next chip"] )
#11
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["In case you are wondering about the extra boxes, they tell us about the relationship between the two qubits",
"The blue one, for example, keeps track of how likely the two blue outputs are to agree",
"It is off when the blue ouptuts of the two qubits will definitely agree, and on when they'll definitely disagree",
"Keep an eye on how it changes on this chip",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!",
"The extra red box similarly looks for agreements of the red boxes",
"The two purple ones are for the blue output of one qubit, and the red one of the other"] )
#12
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":1.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["The x doesn't just invert the blue box of qubit 0, but all the boxes in that column",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!","Now on to the next chip"] )
#13
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":0.0,"XZ":1.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"z":0,"h":0}, "1":{"z":0,"h":0}, "both":{} } )
intro.append( ["The z for qubit 0 similarly affects all the boxes of the other column",
"TARGET: Turn off the blue boxes without using the x command"] )
outro.append( ["Great!","Now on to the next chip"] )
#14
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":0.0,"XZ":1.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"z":0,"h":0}, "1":{"z":0,"h":0}, "both":{} } )
intro.append( ["The h for qubit 0 swaps the two columns",
"TARGET: Turn off the blue boxes without using the x command"] )
outro.append( ["Great!","Now on to the next chip"] )
#15
state_list.append( {"XI":1.0, "ZI":-1.0,
"XX":1.0,"XZ":0.0,"ZX":1.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["The operations for qubit 1 have similar effects, but on the rows instead of the columns",
"The x affects one row, z affects the other, and h swaps them",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!","Now on to the next chip"] )
#16
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":1.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["This is a nice chip with everything working. So just do your magic!",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!","Now on to the next chip",
"Have you noticed the program that is getting displayed as you work",
"This is the code you are writing for the quantum computer, using the QISKit SDK",
"Check out qiskit.org for more information"] )
#17
state_list.append( {"XI":1.0, "ZI":-1.0,
"XX":1.0,"XZ":0.0,"ZX":1.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["Another well behaved chip",
"By the way, most of IBM's chips have more than just two qubits",
"In fact, you can use a real device with 16 qubits from the comfort of your own home"
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!","Now on to the next chip"] )
#18
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":0.0,"XZ":1.0,"ZX":1.0,"ZZ":0.0,"YY":1.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{"cz":0} } )
intro.append( ["This chip has a new operation enabled: cz",
"Like h, we can think of this as swapping the contents of boxes",
"It swaps the contents of the red box of each qubit with the purple box next to it",
"These swaps let us make big changes to the way the qubits are correlated with each other",
"For this reason, cz is known as an entangling operation",
"You'll probably need to use it to disentangle the qubits here",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!",
"This box swapping interpretation is just one way to understand the cz",
"On the next chip, you'll need to use another"] )
#19
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":-1.0,"ZZ":0.0,"YY":0.0,
"IX":-1.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["The cz can also be thought of as a conditional operation",
"If the blue box for qubit 0 is off, the cz does nothing",
"But if it is on, the cz does a z on qubit 1",
"Since the normal z is not working for on this chip, and x only works for qubit 0, this feature of the cz should come in handy",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!","Now on to the next chip"] )
#20
state_list.append( {"XI":0.0, "ZI":0.0,
"XX":1.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":-1.0,
"IX":0.0, "IZ":0.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{"cz":0} } )
intro.append( ["This chip is in an entangled state. The qubits are correlated in a way that only quantum objects can be",
"Though outputs coming from both qubits will be completely random, and have not yet decided what to randomly be, they are certain to always agree",
"What does this mean for the cz? If the blue boxes are full of undecided randomness, does a z get applied to qubit 1 or not?",
"For the time being, you are probably better off with the box swapping interpretation when stuff like this is going on",
"TARGET: Turn off the blue boxes"] )
outro.append( ["Great!",
"Now on to the next chip, and another interpretation of the cz"] )
#21
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":0.0,"XZ":-1.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"ZI":1.0,"IZ":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0},"1":{"x":0,"h":0}, "both":{"cz":0} } )
intro.append( ["You have used cz to do something to qubit 1 that was condition on qubit 0",
"Sometimes you might want an operation that works the other way",
"It turns out that cz does this too! The exact same operation can be interpreted in the opposite way",
"Which is handy, since it is qubit 0 for which x and z aren't working for this chip",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great! The fact that all three of these interpretations of cz are true at once is quite an interesting feature",
"There are those who like to reflect upon the mystery of how this works",
"But they aren't tiny, and stuck in a fancy fridge and trying to do a job that they can't even remember",
"So you'd better save your reflections for later. On to the next chip!"] )
#22
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":-1.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["Combining the cz with other operations, we can get new kinds of conditional behaviour",
"For example, what if we wanted a conditional x rather than a conditional z",
"Using your expertise with using a z to make an x, you should be able to work it out",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!",
"You made an operation that does an x on qubit 1, depending on whether the blue box of qubit 0 is on",
"We call this the cx operation, or the cnot"] )
#23
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":-1.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"x":0,"z":0,"h":0}, "both":{"cz":0} } )
intro.append( ["Unlike cz, the cx operation is not symmetric",
"To make it conditional on qubit 1 instead, you'll need to apply h elsewhere",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!","Now on to the next chip"] )
#24
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":-1.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0}, "1":{"h":0,"cx":0}, "both":{} } )
intro.append( ["Most IBM quantum devices actually use cx as the basic entangling operation instead of cz",
"Which means you have to worry about which way round you are allowed to apply them",
"This chip is one of those devices",
"You have a cx that is conditional on the blue box of qubit 0, and which can apply an x to qubit 1",
"Though it affects both qubits, you'll find it in your list of allowed operations for qubit 1",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!","Now on to the next chip"] )
#25
state_list.append( {"XI":-1.0, "ZI":0.0,
"XX":-1.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,
"IX":1.0, "IZ":0.0} )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0,"z":0,"cx":0}, "both":{} } )
intro.append( ["Like the cz, there are different ways to interpret the cx",
"In the last chip we used the fact that it does an x to qubit 1 conditional on the blue box of qubit 0",
"But it can also be interpreted as doing a z to qubit 0, conditional on the red box of qubit 1",
"This is the interpretation you'll need to reset this chip",
"But since everyone is so obsessed with the other interpretation, you'll still find it in your list of allowed qubit 1 operations",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!","Now on to the next chip"] )
#26
state_list.append( {"XI":0.0, "ZI":-1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,
"IX":0.0, "IZ":-1.0} )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0,"x":0,"cx":0}, "1":{"h":0}, "both":{} } )
intro.append( ["The cx on this chip is the other way around: It does an x to qubit 0 conditional on the blue box of qubit 1",
"Unfortunately, one that does the x to qubit 1 is what you need to reset this chip",
"But don't worry. With a few h operations you can turn the cx around",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!",
"With an h on both qubits before and after a cx, it effectively works the other way around",
"If you remember anything from your time here, it should be this",
"It is a very useful trick when using IBM's cx based devices",
"Now on to the next chip"] )
#27
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0}, [["qdg","0"],["qdg","0"],["qdg","0"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"bloch":1,"q":0}, "1":{}, "both":{"unbloch":0} } )
intro.append( ["This chip is used to test an operation called u3(math.pi,0,0)",
"But that's a lot to type, so we'll call it q instead",
"To introduce the q gate, we'll go back to doing the qubits one-by-one",
"The q operation makes most sense when the qubits boxes are drawn on top of each other",
"To do this, use the bloch command",
"TARGET: Use the bloch command on qubit 0, and turn off the blue boxes using the q gate"] )
outro.append( ["Great!", "On to the next qubit"] )
#28
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0}, [["qdg","1"],["qdg","1"],["qdg","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{}, "1":{"bloch":1,"q":0}, "both":{"unbloch":0} } )
intro.append( ["So what does this q thing do?",
"Around the boxes, are a circle",
"In this circle, at the point where the levels of the boxes meet, is a *",
"The job of the q operation is to rotate that point, dragging the levels of the boxes with it",
"One q rotates the point 1/8 of the way around the circle in the clockwise direction",
"TARGET: Use the bloch command on qubit 1, and turn off the blue boxes using the q gate"] )
outro.append( ["Great!", "On to the next qubit"] )
#29
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0},
[["qdg","0"],["qdg","0"],["qdg","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"bloch":0,"q":0}, "1":{"bloch":0,"q":0}, "both":{"unbloch":0} } )
intro.append( ["Now you can do both qubits at once",
"Notice that q on qubit 0 affects the columns, just like other qubit 0 operations",
"And q on qubit 1 affects the rows",
"You don't need to use the bloch operations for the qubits to apply q",
"But I'd advise that you do",
"TARGET: Turn off the blue boxes using the q gate"] )
outro.append( ["Great!", "On to the next chip"] )
#30
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0},
[["qdg","0"],["q","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0,"bloch":0,"q":1}, "1":{"x":0,"z":0,"h":0,"bloch":0,"q":1}, "both":{"unbloch":0} } )
intro.append( ["This chip will only let you do one q on each qubit",
"So hopefully you won't need more than that",
"If you do, you might be able to hack a solution using other operations",
"TARGET: Turn off the blue boxes using only one q gate on each qubit"] )
outro.append( ["Great!", "On to the next chip"] )
#31
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0},
[["q","0"],["q","0"],["q","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0}, "1":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0}, "both":{"unbloch":0} } )
intro.append( ["A shortcut way of doing this hack is qdg",
"This is simply a anti-clockwise version of q",
"Or counter clockwise, if you prefer",
"Or widdershins. Widdershins is a great word. Let's all use widdershins",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!", "On to the next chip"] )
#32
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0},
[["z","0"],["h","0"],["h","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"bloch":0,"q":0,"qdg":0}, "1":{"x":0,"z":0,"bloch":0,"q":0,"qdg":0}, "both":{"unbloch":0} } )
intro.append( ["The h operation is not working on this chip",
"Fortunately, you can build it using q or qdg",
"Use them to remind this chip how h is supposed to be done",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!", "On to the next chip"] )
#33
state_list.append( MakeState( {"XI":0, "ZI":1,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0},
[["h","1"]]) )
success_condition_list.append( {"IZ":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0}, "1":{"x":0,"z":0,"bloch":0,"q":1,"qdg":1}, "both":{"unbloch":0,"cz":0} } )
intro.append( ["With q and qdg, we can make conditional operations that are more interesting than just cz and cx",
"Like a one that does an h to one qubit, conditional on the other",
"Qubit 1 on this chip has a bunch of broken gates",
"So you may need to create one of these",
"TARGET: Turn off the blue boxes using the allowed gates"] )
outro.append( ["Great!",
"You have successfully tested and reset a whole bunch of qubits",
"To find out how to do more than just reset them, check out the other modes in this program"] )
level_num = len(state_list)
return state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro
# sandbox
def GetLevelSwaps () :
state_list = []
success_condition_list = []
qubits_used_list = []
allowed_gates_list = []
intro = []
outro = []
# 1
state_list.append( {"XI":0.5, "ZI":0.5,
"XX":-0.5,"XZ":0.0,"ZX":0.0,"ZZ":-0.5,"YY":0.0,
"IX":-0.5, "IZ":-0.5} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["In these puzzles, your job is to swap the states of two qubits",
"This can always be done with nothing more than h operations, and either cz or cx",
"TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!","Now try another"] )
# 2
state_list.append( {"XI":0.5, "ZI":0.0,
"XX":0.5,"XZ":0.5,"ZX":0.0,"ZZ":-0.5,"YY":0.5,
"IX":0.0, "IZ":0.5} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!","Now try another"] )
# 3
state_list.append( {"XI":0.0, "ZI":0.5,
"XX":-0.5,"XZ":0.0,"ZX":-0.5,"ZZ":0.5,"YY":0.5,
"IX":-0.5, "IZ":0.0} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!","Now try another"] )
# 4
state_list.append( {"XI":0.0, "ZI":-0.5,
"XX":0.0,"XZ":0.5,"ZX":0.5,"ZZ":-0.5,"YY":0.5,
"IX":0.0, "IZ":0.5} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"])
outro.append( ["Well done!","Now try another"] )
# 5
state_list.append( {"XI":0.0, "ZI":0.5,
"XX":0.0,"XZ":0.5,"ZX":-0.5,"ZZ":0.5,"YY":-0.5,
"IX":0.0, "IZ":0.5} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!","Now try another"] )
# 6
state_list.append( {"XI":0.5, "ZI":0.0,
"XX":-0.5,"XZ":-0.5,"ZX":-0.5,"ZZ":0.0,"YY":0.5,
"IX":-0.5, "IZ":0.0} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!","Now try another"] )
# 7
state_list.append( {"XI":0.5, "ZI":0.0,
"XX":0.5,"XZ":0.5,"ZX":-0.5,"ZZ":0.0,"YY":-0.5,
"IX":0.5, "IZ":0.0} )
target, target_string = MakeTarget( state_list[-1], 'swap' )
success_condition_list.append( target )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"h":0}, "1":{"h":0}, "both":{"cz":0} } )
intro.append( ["TARGET: Everything from qubit 0 must be moved to the same place on qubit 1, and vice-versa",
"So given the initial state you'll get, the final state should look like this",
target_string,
"Once the actual puzzle grid comes, you'll have to scroll up to see this again"] )
outro.append( ["Well done!",
"Your solution to each of these probably would have worked for all the others too",
"If so, you suceeded in making a swap gate!"] )
level_num = len(state_list)
return state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro
# sandbox
def GetLevelSandbox () :
state_list = []
success_condition_list = []
qubits_used_list = []
allowed_gates_list = []
intro = []
outro = []
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"XI":1.0,"ZI":1.0} )
qubits_used_list.append( [ "0", "1" ] )
allowed_gates_list.append( { "0":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0,"cx":0}, "1":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0,"cx":0}, "both":{"unbloch":0,"cz":0} } )
intro.append( ["This is a sandox. You have all gates available, and can do what you want",
"The initial state is the one with ouput 00, which is the usual initial state for quantum computers",
"There are no conditions for success. You can just keep playing as long as you like",
"Bear in mind that ascii art isn't the most precise way of representing quantum states. So things might start looking strange for complex programs"] )
outro.append( ["If you are seeing this, you somehow disproved Heisenberg's uncertainty principle",
"Either that or there is a bug"] )
level_num = len(state_list)
return state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro
# image superposer
def GetLevelSuperposer ( string1, string2 ) :
state_list = []
success_condition_list = []
qubits_used_list = []
allowed_gates_list = []
intro = []
outro = []
definite = {"0":[],"1":[]}
superpos = {"same":[],"diff":[]}
first_super = -1
for j in range(4) :
if string1[j]==string2[j] :
definite[str(string1[j])].append( str(j) )
else :
if first_super == -1 :
first_super = j
superpos["same"].append( str(j) )
else :
if string1[j]==string1[first_super] :
superpos["same"].append( str(j) )
else :
superpos["diff"].append( str(j) )
# if a bit is 0 in both file names, nothing need be done
# if a bit is 1 in both, this needs done
for j in definite["1"] :
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":-1.0,"ZZ":0.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"IZ":-1.0,} )
qubits_used_list.append( [ "a", "b[" + j + "]" ] )
allowed_gates_list.append( { "a":{}, "b[" + j + "]":{"x":0,"z":0,"h":0}, "both":{} } )
intro.append( ["Since bit "+j+" has the value 1 for both in both file names, the qubit named b["+j+"] should be given a full blue box"] )
outro.append( ["Great!", "Copy the QISKit program into the notebook."] )
if first_super != -1 :
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"XI":1.0} )
qubits_used_list.append( [ "a", "b[0}" ] )
allowed_gates_list.append( { "a":{"x":0,"z":0,"h":0}, "b[0}":{}, "both":{} } )
intro.append( ["Since we are making a superposition of two things, so let's start with a superposition of 0 and 1 on a single qubit",
"We'll do it on qubit a, even though this won't actually be part of our final results",
"The superposition can be spread to the other qubits from here",
"Since we want a superposition of 0 and 1, we need a half full blue box",
"Let's go for the superposition for which the red box is empty"] )
outro.append( ["Great!",
"Copy the QISKit program into the notebook."] )
for j in superpos["same"] :
if j==str(first_super) :
state_list.append( {"XI":1.0, "ZI":0.0, "XX":0.0,"XZ":1.0,"ZX":0.0,"ZZ":0.0,"YY":0.0, "IX":0.0, "IZ":1.0} )
else :
state_list.append( {"XI":0.0, "ZI":0.0, "XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0, "IX":0.0, "IZ":1.0} )
success_condition_list.append( {"ZZ":1.0} )
qubits_used_list.append( [ "a", "b[" + j + "]" ] )
allowed_gates_list.append( { "a":{"z":0}, "b[" + j + "]":{"x":0,"z":0,"h":0}, "both":{"cz":0} } )
intro.append( ["Since bit "+j+" has different values for the two file names, it needs to be part of the superposition",
"So we spread the superposition from qubit a to qubit b["+j+"] by copying the superposed bit",
"This will mean that both qubits will have random outputs from their blue boxes, but those outputs will always agree",
"The signature of this is that the middle blue box will be empty, so work towards this target"] )
outro.append( ["Great!",
"Copy the QISKit program into the notebook."] )
for j in superpos["diff"] :
state_list.append( {"XI":0.0, "ZI":0.0,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":0.0,"YY":0.0,"IX":0.0, "IZ":1.0} )
success_condition_list.append( {"ZZ":-1.0} )
qubits_used_list.append( [ "a", "b[" + j + "]" ] )
allowed_gates_list.append( { "a":{"z":0}, "b[" + j + "]":{"x":0,"z":0,"h":0}, "both":{"cz":0} } )
intro.append( ["Since bit "+j+" has different values for the two file names, it needs to be part of the superposition",
"But since its value is always different to the first bit in the superposition, we also need to make it disagree",
"This means that the middle blue box will need to be full"] )
outro.append( ["Great!",
"Copy the QISKit program into the notebook."] )
level_num = len(state_list)
return state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro
# sandbox
def GetLevelBell () :
state_list = []
success_condition_list = []
qubits_used_list = []
allowed_gates_list = []
intro = []
outro = []
# create target state and string
target, target_string = MakeTarget( {"XI":0, "ZI":1.0,"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,"IX":0.0, "IZ":1.0}, [["h","0"],["cx","1"],["q","0"],["h","0"]] )
# add them in
state_list.append( {"XI":0.0, "ZI":1.0,
"XX":0.0,"XZ":0.0,"ZX":0.0,"ZZ":1.0,"YY":0.0,
"IX":0.0, "IZ":1.0} )
success_condition_list.append( target )
allowed_gates_list.append( { "A":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0,"cx":0}, "B":{"x":0,"z":0,"h":0,"bloch":0,"q":0,"qdg":0,"cx":0}, "both":{"unbloch":0,"cz":0} } )
intro.append( ["This mode relates to a Jupyter notebook in which you can explore Bell tests",
"You'll find the notebook at https://github.com/decodoku/Quantum_Programming_Tutorial/tree/master/bell-test",
"A good state to aim for is the following",
target_string,
"But you can also use this mode as a sandbox and make whatever you like"])
qubits_used_list.append( [ "A", "B" ] )
outro.append( ["Great!",
"Copy the QISKit program into the quantum setup_variables() function in the bell_test notebook."] )
level_num = len(state_list)
return state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro
# -*- coding: utf-8 -*-
# Created by James Wootton
# Copyright © 2018 University of Basel. All rights reserved.
# import things we need
import math, copy
from Engine import *
from Levels import *
# clear screen and put title
ClearScreen ()
print("")
print("")
print(" ██╗ ██╗███████╗██╗ ██╗ ██████╗ ")
print(" ██║ ██║██╔════╝██║ ██║ ██╔═══██╗ ")
print(" ███████║█████╗ ██║ ██║ ██║ ██║ ")
print(" ██╔══██║██╔══╝ ██║ ██║ ██║ ██║ ")
print(" ██║ ██║███████╗███████╗███████╗╚██████╔╝ ")
print(" ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ")
print(" ")
print(" ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗████████╗██╗ ██╗███╗ ███╗")
print(" ██╔═══██╗██║ ██║██╔══██╗████╗ ██║╚══██╔══╝██║ ██║████╗ ████║")
print(" ██║ ██║██║ ██║███████║██╔██╗ ██║ ██║ ██║ ██║██╔████╔██║")
print(" ██║▄▄ ██║██║ ██║██╔══██║██║╚██╗██║ ██║ ██║ ██║██║╚██╔╝██║")
print(" ╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║ ██║ ╚██████╔╝██║ ╚═╝ ██║")
print(" ╚══▀▀═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝")
print("\n")
print("\n")
print(" A GAMIFIED TUTORIAL FOR QUANTUM PROGRAMMING")
print(" ")
print(" For more information visit:")
print(" github.com/decodoku/Quantum_Programming_Tutorial")
print("\n")
print("\n")
print("\n")
print("\n")
print("\n")
print("\n")
input("> Note: For the best experience, you may need to zoom out\n> Press Enter to continue...\n")
ClearScreen()
print(" Choose which mode you want to play from the following list")
print("")
print(" 1 - Main Tutorial\n A gamified tutorial for programming quantum computers.\n")
print(" 2 - Qubit Swapper\n A few puzzles dedicated to the task of swapping qubits\n")
print(" 3 - Sandbox\n A chance to do what you want with two qubits.\n")
print(" 4 - Image Superposer\n Write a quantum program to create superpositions of images.\n")
print(" 5 - Bell Test\n Write a quantum program to explore the unique nature of quantum variables.\n")
choosing = True
while choosing:
mode = input("> Input a number to select a mode...\n")
if mode in ["1","2","3","4","5"]:
choosing = False
else:
input("> That's not a valid mode. Press Enter to try again...\n")
if mode in ["1","2"]:
if mode=="1":
state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelStory()
elif mode=="2":
state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSwaps()
choosing = True
while choosing:
level = input("\n> Select a level (from 1 to "+str(level_num)+")...\n")
if level.isdigit():
level = int(level)
if level in range(1,level_num+1):
level = int(level)-1
choosing = False
else:
input("> That level does not exist. Press Enter to try again...\n")
else:
level = 0
choosing = False
input("> That was not a valid level, so we'll start from the beginning...\n")
elif mode=="3":
state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSandbox()
level = 0
elif mode=="4":
allowed_filenames = ["0000","0001","0010","0011", "0100","0101","0110","0111", "1000","1001","1010","1011", "1100","1101","1110","1111"]
input("\n> This mode relates to a Jupyter notebook, which you'll find at\n https://github.com/decodoku/Quantum_Programming_Tutorial/tree/master/image-superposer...\n")
input("> There you'll find 16 images, all with a bit string as their filename...\n")
choosing = True
while choosing :
string1 = input("> Choose one of these images by typing the filename below...\n")
if string1 in allowed_filenames:
choosing = False
else:
input("> That was not a valid filename. Press Enter to try again...\n")
choosing = True
while choosing :
string2 = input("\n> Choose another image typing the filename below...\n")
if string2 in allowed_filenames and string2!=string1:
choosing = False
else:
input("> That was not a valid filename. Press Enter to try again...\n")
input("\n> You'll now write a QISKit program to superpose these two images...\n")
input("\n> This can then be run on a real machine using the Jupyter notebook...\n")
state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelSuperposer(string1,string2)
level = 0
elif mode=="5":
state_list, success_condition_list, qubits_used_list, allowed_gates_list, level_num, intro, outro = GetLevelBell()
level = 0
else:
print("> Oops! You shouldn't have been allowed to pick this mode. Restart the program and try again...")
while (level<level_num) :
# specifications for this level
state = copy.deepcopy(state_list[level])
allowed_gates = copy.deepcopy(allowed_gates_list[level])
required_gates = copy.deepcopy(allowed_gates)
qubits_used = copy.deepcopy(qubits_used_list[level])
success_condition = copy.deepcopy(success_condition_list[level])
bloch = False # always start a level with Bloch off
active_qubit = -1 # and no active qubit
program = [] # and an empty program
# every puzzle is thought of as one involving two qubits, even if only one of those qubits is involved
# qubits_used should therefore always be a list of two qubits, the first is the one displayed on the top, and the second is the one on the bottom
if allowed_gates[qubits_used[0]]=={} : # if no gates are allowed for the qubit on top, we know to only show the qubit on the side
shown_qubit = 1
elif allowed_gates[qubits_used[1]]=={} : # and vice versa
shown_qubit = 0
else :
shown_qubit = 2
success = False
restart = False
ClearScreen ()
print("\nLevel "+str(level+1)+"\n")
for line in intro[level] :
input("> " + line + "...")
while success==False and restart==False :
# ask player to choose an operation
combined_gates = []
for qubit in [qubits_used[0],qubits_used[1],"both"]:
for gate in allowed_gates[qubit].keys() :
if allowed_gates[qubit][gate]==0 or required_gates[qubit][gate]>0:
if gate not in combined_gates:
combined_gates.append(gate)
gate = ""
while gate=="":
message = "> Choose one of the allowed operations from the list above by typing its name below\n> Or type restart to start this puzzle over..."
given_input = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, allowed_gates, required_gates )
if given_input in combined_gates or given_input=='restart' :
gate = given_input
# ask player for a qubit to act on (if there is a choice)...
qubit = ""
if gate in ["cz","unbloch","restart"]: # for gates that act symmetrically the choice is irrelevant, and so set by default to both
qubit = "both"
elif shown_qubit==0 : # if only the top qubit is shown, that's what the gate acts on
qubit = qubits_used[0]
elif shown_qubit==1 : # if only the side qubit is shown, that's what the gate acts on
qubit = qubits_used[1]
elif gate not in allowed_gates[qubits_used[0]].keys(): # if the gate isn't an allowed one for the top qubit, it acts on the side
qubit = qubits_used[1]
elif gate not in allowed_gates[qubits_used[1]].keys(): # and vice-versa
qubit = qubits_used[0]
else :
while qubit not in [qubits_used[0],qubits_used[1]]:
message = "> Choose a qubit to act on:\n> Type in "+qubits_used[0]+" or "+qubits_used[1]+"..."
qubit = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, allowed_gates, required_gates )
# if it is a gate, apply it
if gate in ["x","z","h","q","qdg","cz","cx"] :
# the apply gate function adresses qubits as 0 and 1, so we convert back to this before applying
if qubit==qubits_used[0] :
qubit_pos = "0"
elif qubit==qubits_used[1] :
qubit_pos = "1"
else :
qubit_pos = "both"
# now apply
state = ApplyGate( state, gate, qubit_pos )
# then we write the qiskit commands
WriteQISKit( gate, qubit, qubits_used, program, mode )
# if it is a visualization command, apply it
elif gate=="bloch" :
bloch = True
if qubit==qubits_used[0] :
active_qubit = 0
elif qubit==qubits_used[1] :
active_qubit = 1
elif gate=="unbloch" :
bloch = False
active_qubit = -1
elif gate=="restart" :
restart = True
else :
print("Error: Something's not quite right")
# log the number of these gates if it is important
if gate!="restart" :
if allowed_gates[qubit][gate] > 0 :
required_gates[qubit][gate] -= 1
if required_gates[qubit][gate] < 0 :
input("> You have used this gate too many times, so the level will restart...\n")
restart = True
# see if success conditions are met
success = True
# the final state has to be right
for box, expect in success_condition.items() :
success = success and state[box]==expect
# and the number of ops has to be right
for qubit in required_gates.keys() :
for gate in required_gates[qubit].keys() :
if (required_gates[qubit][gate] > 0) :
success = False
if restart==False :
message = "\n> Target achieved.\n\n\n> Press Enter to continue..."
given_input = PrintScreen ( message, level, intro, program, state, shown_qubit, active_qubit, bloch, {}, required_gates )
# print the outro
for line in outro[level] :
input("> " + line + "...")
# interate the level
level += 1
if mode in ["1","2"] :
input("> That's all the levels we have for now\nRestart the program to explore the other modes, or continue your quantum journey at ibm.biz/helloquantum\n")
elif mode=="3" :
input("> How are you seeing this?!?!?!?!?!?!?!\n")
elif mode=="4" :
input("> Now you have your QISKit program. You just need to run the notebook for your image.\nhttps://github.com/decodoku/Quantum_Programming_Tutorial\n")
elif mode=="4" :
input("> Now you have your QISKit program. You just need to run the notebook see what it d.\nhttps://github.com/decodoku/Quantum_Programming_Tutorial/tree/master/bell-test\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment