Your objective in this project, should you choose to accept it, you and your team will play dice at a Las Vegas casino and win every time! How? By creating a pair of quantum dice!
In order for this to work, you will need to create at least two entangled dice. However, be sure that you entangle them in a way that security teams won't figure it out and catch you before you make your big move! Ideally you want the dice to provide enough information to you without anyone figuring out what happened. You can do this by ensuring that the entangled dice not roll out the same entangled result each time, but rather different results.
For example: If all of your dice roll the same values, it would be too obvious: Dice 1 = 101, Dice 2 = 101 (Where each entangled die is at a different table)
If all your dice roll different yet expected values, it would not be too obvious: Dice 1 = 101, Dice 2 = 010 (This is perfect, not easily detected yet as you can see by the results, perfectly entangled)
You're team will occupy two tables. The first table we will call the 'source', and the other tables we will call the 'target'. The plan is to have your team at the source table, and you at the target table.
You will then bet according to the results of the roll of the dice of the team at the Source table. So the source table rolls a '010' (2), then because your dice are entangled with the Source dice, you will roll the opposite (binary digits), '101' (5). This is why we want to ensure that the values are opposite, so that casino security won't think there is anything wrong, wheras if both dice are entangled resulting in the same value each.
The next steps here are to "program" your entangled dice, good luck!
In [1]:
# begin by importing the essential libraries from qiskit
from qiskit import IBMQ, Aer
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
# The following options are recomended for Jupter notebook visualization using matplotlib
%matplotlib inline
Now let's create the circuit to implement and visualize the quantum circuit needed to create the entangled dice. In this example, to keep things simple, we will create an 8-sided die with binary values 0-7.
In [2]:
# set up Quantum Register and Classical Register for 3 qubits (binary values 000 -> 111)
q = QuantumRegister(3)
c = ClassicalRegister(3)
# Create a Quantum Circuit
qc = QuantumCircuit(q, c)
In [3]:
# now let's set our source die into superposition and measure
# so to simulate random die values 0-7, or 1-8 since we dice do not have a 0 value.
# Note: the 'barrier' is there just so the circuit displays well, it has no effect on the circuit itself.
qc.h(q)
qc.barrier(q)
qc.measure(q, c)
Out[3]:
In [4]:
# For completeness, let's see what the openqasm code looks like.
QASM_source = qc.qasm()
print(QASM_source)
In [5]:
# While we're at it, let's also look at our circuit visually.
# This is similar to what we would see in the IBM Q Composer.
qc.draw()
Out[5]:
Now let's run and test out our die. We will run a single shot (to simulate rolling the die on a casino table). Rerun the cell below a few times so to see the values of the die change for each roll.
In [6]:
from qiskit import execute
job = execute(qc, backend=Aer.get_backend('qasm_simulator'), shots=1)
result = job.result().get_counts(qc)
print(result)
In this section we will now create the target die, which we will entangle to our source die. To simplify this let's create the second die by adding 3 more qubits. Then the first three qubits we can refer to as the source die, and the second set of three qubits are for the target die.
For example, the classical bit result {010111} means that the source die (first three bits from left, LSB) = 111, and the target die (remaing three bits) are the bits to the right of the first three = 010.
In [7]:
# load IBM Q account, only if you want to run this on a real device
IBMQ.load_accounts()
In [8]:
# First let's double the size of our bit set.
bitSize = 3
# since we have just two dice, source and target, we will multiply by two.
q = QuantumRegister(2*bitSize)
c = ClassicalRegister(2*bitSize)
# Create a Quantum Circuit, same as before.
qc = QuantumCircuit(q, c)
# now, just as before, let's set our source die into superposition and measure
qc.h(q)
qc.barrier(q)
qc.measure(q, c)
Out[8]:
In [9]:
# Review the openqasm code.
QASM_source = qc.qasm()
print(QASM_source)
Note the measure mapping above maps to 6 classic bits, we will view bits 0-2 (first three) as the source, and bits 3-5 (remaining three) as the target.
In [10]:
# Now draw the circuit with a bundled classical register
qc.draw()
Out[10]:
Ok, now that you have built the circuit we are ready to test this out. Run this with one shot and note the results.
In [11]:
# We will use the qasm simulator to run this first.
# You can then try running it on the real machine and compare the results.
job = execute(qc, backend=Aer.get_backend('qasm_simulator'), shots=1)
result = job.result().get_counts(qc)
print(result)
After executing the above, you will note that each time you run it, the source and target dice change randomly. This is fine, except this does not yet help with what we need to accomplish our objective. We want to now entangle them so that they are not so random between source and target. In order to do this we need to do two things.
In [12]:
# First, reset the circuit
q = QuantumRegister(2*bitSize)
c = ClassicalRegister(2*bitSize)
# Create a Quantum Circuit, same as before.
qc = QuantumCircuit(q, c)
# Set only the source die to random by adding a Hadamard gate to the first three, then
# we want to entangle the the first three qubits to the last three qubits. We'll do this using a simple for loop.
# Adding h to first three, and cx between source and target
for i in range(bitSize):
qc.h(q[i])
qc.cx(q[i], q[i+bitSize])
qc.barrier(q)
# Measure the full circuit
qc.measure(q, c)
Out[12]:
In [13]:
# Visualize the circuit
qc.draw()
Out[13]:
In [14]:
# Execute the circuit on the simulator and note the results,
# set to just a single shot and run a few times to compare results.
job = execute(qc, backend=Aer.get_backend('qasm_simulator'), shots=1)
result = job.result().get_counts(qc)
print(result)
Since we have these dice entangled we should see the results with the source bits (bit 0-2, LSB) and target bits (3-5), and are exactly the same each and every time we execute the experiment.
Since we don't want the casino to get too suspicious by noticing that two tables always have the same results, we will now update this to that the two dice are entangled, but not the same value. In order to do this, we will want the source and target to have "opposite" yet predictable values.
For example: {101010} Source = 101 Target = 010
Create a circuit which would represent this.
In [15]:
bitSize = 3
q = QuantumRegister(2*bitSize)
c = ClassicalRegister(2*bitSize)
qc = QuantumCircuit(q, c)
for i in range(bitSize):
qc.h(q[i])
qc.x(q[i+bitSize])
qc.cx(q[i], q[i+bitSize])
qc.barrier(q)
qc.measure(q, c)
job = execute(qc, backend=Aer.get_backend('qasm_simulator'), shots=1)
result = job.result().get_counts(qc)
print(result)
In [16]:
# Visualize the circuit
qc.draw()
Out[16]:
In [ ]: