Deutsch-Jozsa Algorithm

# Needed to set up the quantum circuit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
# Needed to simulate running a quantum computer
from qiskit_aer import AerSimulator
# Neded to visualize the results of running a quantum computer
from qiskit.visualization import plot_histogram
# Needed for vectors and stuff
import numpy as np
# This import let's us visualize the Qiskit qubits as state vectors (bra-ket notation)
from qiskit.quantum_info import Statevector
# This import let's us visualize 
from qiskit.visualization import plot_bloch_multivector
# Constant Oracle
def constant_oracle_0 (qc, num_qubits):
    None
# Constant Oracle
def constant_oracle_1 (qc, num_qubits):
    qc.x(num_qubits-1)
# Balanced Oracle
def balanced_oracle (qc, num_qubits):
    n = num_qubits-1
    
    # Create a random binary string
    b_str = ''
    for i in range(n):
        b_str+=str(np.random.randint(2))
    
    print("Balanced Oracle Random String:", b_str)
    
    # Place X-gates
    for qubit in range(len(b_str)):
        if b_str[qubit] == '1':
            qc.x(qubit)
    
    # Use barrier as divider
    qc.barrier()
    
    # Controlled-NOT gates
    for qubit in range(n):
        qc.cx(qubit, n)
    
    qc.barrier()
    
    # Place X-gates
    for qubit in range(len(b_str)):
        if b_str[qubit] == '1':
            qc.x(qubit)
    
# Full Algorithm

# Step 0: Create circuit
n = 2
q = QuantumRegister(n+1)
c = ClassicalRegister(n+1)
qc = QuantumCircuit(q,c)

# Step 1: NOT Gate on Auxilary Qubit
qc.x(n)
qc.barrier()
print("******STEP 1*******")
print("NOT GATE TO AUXILIARY/OUTPUT QUBIT")
print(qc.draw())
print()

# Step 2: Hadamard Gate for All
qc.h(range(n+1))
qc.barrier()
print("******STEP 2*******")
print("HADAMARD GATES FOR EVERYONE")
print("TOP QUBITS ARE NOW |+>")
print("AUXILIARY/OUTPUT QUBIT IS NOW |->")
print(qc.draw())
print()

# Step 3: Add Oracle
print("******STEP 3*******")
print("APPLY THE ORACLE")
print("IF A CONSTANT ZERO ORACLE THE QUBITS DO NOT CHANGE AND THE AUXILLIARY QUBIT DOES NOT CHANGE")
print("IF A CONSTANT ONE ORACLE THE QUBITS DO NOT CHANGE AND THE AUXILLARY QUIBIT CHANGES TO -|->")
print("IF A BALANCED ORACLE THE QUBITS CHANGE INTO |-> DO TO PHASE KICKBACK DUE TO THE CNOT GATES")
print("THE AUXILLIARY QUBIT COULD STAY IN |-> STATE OR CHANGE TO THE -|-> STATE")
#constant_oracle_0(qc, n+1)
c#onstant_oracle_1(qc,n+1)
balanced_oracle(qc,n+1)
qc.barrier()
print(qc.draw())
print()

# Step 4: Hadamard Gates for All Qubits
qc.h(range(n+1))
qc.barrier()
print("******STEP 4*******")
print("APPLY HADAMARD TO ALL QUBITS TO CONVERT BACK INTO THE UP/DOWN BASIS")
print(qc.draw())
print()

# Step 5: Display the statevector
print("******STEP 5*******")
print("PRINT THE STATE VECTOR, THE OUTPUT QUBIT IS THE ONE CLOSEST TO THE |")
print("1 IS EQUIVALENT TO THE CLASSICAL OUTPUT 0, -1 IS EQUIVALENT TO THE CLASSICAL OUTPUT 1")
# Get the quantum state that is represented by the circuit
statevector = Statevector(qc)
# Display the state in formatted text
print("STATE VECTOR:")
display(statevector.draw(output = 'latex'))

# Step 6: Measure All Qubits Except the Auxillary
qc.measure(range(n+1),range(n+1))
print("******STEP 6*******")
print("MEASURE THE STATE OF ALL THE QUBITS, INCLUDING THE OUTPUT QUBIT")
print("NOTE THAT IN THE STANDARD ALGORITHM YOU ONLY MEASURE OTHER QUBITS")
print(qc.draw())
print()

# Step 7: Simulate Running the Circuit
# Run the simulation
simulator = AerSimulator()
shots = 2048
results = simulator.run(qc, shots=shots).result()
answer = results.get_counts()
print("******STEP 7*******")
print("SIMULATE RUNNING THE CIRCUIT")
print("IF ALL OF THE TOP QUBITS ARE 0 THEN IT WAS THE CONSTANT ORACLE")
print("IF ALL OF THE TOP QUBITS ARE 1 THEN IT WAS THE BALANCED ORACLE")
zeros_string = ""
zeros_string = zeros_string.rjust(n, '0')
ones_string = ""
ones_string = ones_string.rjust(n, '1')
for result in answer:
    result = result[1:]
    print("SIMULATION RESULT:", result)
    if result == zeros_string:
        print("CONSTANT ORACLE")
    if result == ones_string:
        print("BALANCED ORACLE")
******STEP 1*******
NOT GATE TO AUXILIARY/OUTPUT QUBIT
             ░ 
q77_0: ──────░─
             ░ 
q77_1: ──────░─
       ┌───┐ ░ 
q77_2: ┤ X ├─░─
       └───┘ ░ 
c76: 3/════════
               

******STEP 2*******
HADAMARD GATES FOR EVERYONE
TOP QUBITS ARE NOW |+>
AUXILIARY/OUTPUT QUBIT IS NOW |->
             ░ ┌───┐ ░ 
q77_0: ──────░─┤ H ├─░─
             ░ ├───┤ ░ 
q77_1: ──────░─┤ H ├─░─
       ┌───┐ ░ ├───┤ ░ 
q77_2: ┤ X ├─░─┤ H ├─░─
       └───┘ ░ └───┘ ░ 
c76: 3/════════════════
                       

******STEP 3*******
APPLY THE ORACLE
IF A CONSTANT ZERO ORACLE THE QUBITS DO NOT CHANGE AND THE AUXILLIARY QUBIT DOES NOT CHANGE
IF A CONSTANT ONE ORACLE THE QUBITS DO NOT CHANGE AND THE AUXILLARY QUIBIT CHANGES TO -|->
IF A BALANCED ORACLE THE QUBITS CHANGE INTO |-> DO TO PHASE KICKBACK DUE TO THE CNOT GATES
THE AUXILLIARY QUBIT COULD STAY IN |-> STATE OR CHANGE TO THE -|-> STATE
Balanced Oracle Random String: 01
             ░ ┌───┐ ░  ░            ░       ░ 
q77_0: ──────░─┤ H ├─░──░───■────────░───────░─
             ░ ├───┤ ░  ░   │        ░ ┌───┐ ░ 
q77_1: ──────░─┤ H ├─░──░───┼────■───░─┤ X ├─░─
       ┌───┐ ░ ├───┤ ░  ░ ┌─┴─┐┌─┴─┐ ░ └───┘ ░ 
q77_2: ┤ X ├─░─┤ H ├─░──░─┤ X ├┤ X ├─░───────░─
       └───┘ ░ └───┘ ░  ░ └───┘└───┘ ░       ░ 
c76: 3/════════════════════════════════════════
                                               

******STEP 4*******
APPLY HADAMARD TO ALL QUBITS TO CONVERT BACK INTO THE UP/DOWN BASIS
             ░ ┌───┐ ░  ░            ░       ░ ┌───┐ ░ 
q77_0: ──────░─┤ H ├─░──░───■────────░───────░─┤ H ├─░─
             ░ ├───┤ ░  ░   │        ░ ┌───┐ ░ ├───┤ ░ 
q77_1: ──────░─┤ H ├─░──░───┼────■───░─┤ X ├─░─┤ H ├─░─
       ┌───┐ ░ ├───┤ ░  ░ ┌─┴─┐┌─┴─┐ ░ └───┘ ░ ├───┤ ░ 
q77_2: ┤ X ├─░─┤ H ├─░──░─┤ X ├┤ X ├─░───────░─┤ H ├─░─
       └───┘ ░ └───┘ ░  ░ └───┘└───┘ ░       ░ └───┘ ░ 
c76: 3/════════════════════════════════════════════════
                                                       

******STEP 5*******
PRINT THE STATE VECTOR, THE OUTPUT QUBIT IS THE ONE CLOSEST TO THE |
1 IS EQUIVALENT TO THE CLASSICAL OUTPUT 0, -1 IS EQUIVALENT TO THE CLASSICAL OUTPUT 1
STATE VECTOR:

\[- |111\rangle\]

******STEP 6*******
MEASURE THE STATE OF ALL THE QUBITS, INCLUDING THE OUTPUT QUBIT
NOTE THAT IN THE STANDARD ALGORITHM YOU ONLY MEASURE OTHER QUBITS
             ░ ┌───┐ ░  ░            ░       ░ ┌───┐ ░ ┌─┐      
q77_0: ──────░─┤ H ├─░──░───■────────░───────░─┤ H ├─░─┤M├──────
             ░ ├───┤ ░  ░   │        ░ ┌───┐ ░ ├───┤ ░ └╥┘┌─┐   
q77_1: ──────░─┤ H ├─░──░───┼────■───░─┤ X ├─░─┤ H ├─░──╫─┤M├───
       ┌───┐ ░ ├───┤ ░  ░ ┌─┴─┐┌─┴─┐ ░ └───┘ ░ ├───┤ ░  ║ └╥┘┌─┐
q77_2: ┤ X ├─░─┤ H ├─░──░─┤ X ├┤ X ├─░───────░─┤ H ├─░──╫──╫─┤M├
       └───┘ ░ └───┘ ░  ░ └───┘└───┘ ░       ░ └───┘ ░  ║  ║ └╥┘
c76: 3/═════════════════════════════════════════════════╩══╩══╩═
                                                        0  1  2 

******STEP 7*******
SIMULATE RUNNING THE CIRCUIT
IF ALL OF THE TOP QUBITS ARE 0 THEN IT WAS THE CONSTANT ORACLE
IF ALL OF THE TOP QUBITS ARE 1 THEN IT WAS THE BALANCED ORACLE
SIMULATION RESULT: 11
BALANCED ORACLE
STATE VECTOR:

\[ |11\rangle\]

STATE VECTOR:

\[\frac{1}{2} |00\rangle- \frac{1}{2} |01\rangle- \frac{1}{2} |10\rangle+\frac{1}{2} |11\rangle\]