A bean machine is a triangular maze of pins through which balls are fed. At each node, the ball can decide to go left or right. We model this system using a triangular grid of nodes, where each node is a qubit. Each node takes a boolean value, which indicates whether the ball should pass on the left or right, each with 50% probability.
%matplotlib inline
import pylab as pl
import numpy as np
import pandas as pd
import networkx as nx
from networkx.drawing.nx_agraph import write_dot, graphviz_layout
G = nx.DiGraph()
G.add_edges_from([("q0", "q1"), ("q0", "q2")])
pl.figure(figsize=(2,2))
pos=graphviz_layout(G, prog='dot')
nx.draw(G, pos, with_labels=True, arrows=True)
from qiskit import BasicAer
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit import execute
from qiskit.tools.visualization import plot_state_city
# set up Quantum Register and Classical Register for 3 qubits
q = QuantumRegister(3, name="q")
c = ClassicalRegister(3)
qc = QuantumCircuit(q, c)
q0, q1, q2 = q
qc.h(q0)
qc.cx(q0, q1) # Left branch
qc.x(q0) # Flip q0 to check for right branch
qc.cx(q0, q2) # Right branch
qc.barrier()
# flip q0 back
qc.x(q0)
# measure all qubits
qc.barrier()
qc.measure(q, c)
qc.draw(output='mpl')
job = execute(qc, backend=BasicAer.get_backend('qasm_simulator'), shots=10000)
result = job.result()
result.get_counts(qc)
job = execute(qc, backend=BasicAer.get_backend('statevector_simulator'))
result = job.result()
state = result.get_statevector()
state
plot_state_city(state, title='Left or right')
sum([s != 0 for s in state]) # Number of possible paths
G = nx.DiGraph()
G.add_edges_from([("q0", "q1"), ("q0", "q2"), ("q1", "q3"), ("q1", "q4"), ("q2", "q4"), ("q2", "q5")])
pl.figure(figsize=(4,3))
pos=graphviz_layout(G, prog='dot')
nx.draw(G, pos, with_labels=True, arrows=True)
# set up Quantum Register and Classical Register for 3 qubits
q = QuantumRegister(6, name="q")
c = ClassicalRegister(6)
qc = QuantumCircuit(q)
q0, q1, q2, q3, q4, q5 = q
# First layer. q0 is the control qubit.
qc.h(q0)
qc.cx(q0, q1)
qc.x(q0)
qc.cx(q0, q2)
qc.barrier()
# Second layer: right branch. q0 and q2 are control qubits.
qc.h(q2)
qc.ccx(q0, q2, q4)
qc.x(q2)
qc.ccx(q0, q2, q5)
qc.barrier()
# Second layer: left branch. q0 and q1 are control qubits.
qc.x(q0) # Flip q0 to use as control
qc.h(q1)
qc.ccx(q0, q1, q3)
qc.x(q1)
qc.ccx(q0, q1, q4)
qc.barrier()
# Flip things back
qc.x(q1)
qc.x(q2)
qc.draw(output='mpl')
# Measure all qubits
qc.add_register(c) # Adding classical register
qc.barrier()
qc.measure(q, c)
job = execute(qc, backend=BasicAer.get_backend('statevector_simulator'))
result = job.result()
state = result.get_statevector()
state
sum([s != 0 for s in state]) # Number of possible paths
job = execute(qc, backend=BasicAer.get_backend('qasm_simulator'), shots=10000)
result = job.result()
counts = result.get_counts(qc)
counts
# Get mapping from bitstrings to nodes
nodes = [f"{q.name}{n}" for n in range(q.size)]
node_indices = [nodes.index(x) for x in G.nodes() if G.out_degree(x)==0]
bitstrings_to_nodes = {c: [nodes[i] for i in node_indices if list(c)[::-1][i] is "1"][0] for c in counts}
node_indices
bitstrings_to_nodes
node_counts = pd.DataFrame(bitstrings_to_nodes.items(), columns=["bitstring", "node"]
).merge(pd.DataFrame(counts.items(), columns=["bitstring", "count"]))
node_counts
count_per_node = node_counts.groupby("node").sum()
count_per_node
count_per_node.plot(kind="bar")