# Beginner

Superdense coding is a procedure that allows us to send two classical bits to another party using just a single qubit during communication. In a simplified way, this protocol replaces two classical bits with one qubit. In this tutorial we will gain a basic understanding of Super Dense Coding. We will understand the steps of the protocol using 2 qubits and finally implement the circuit initialized using 2 qubits in a simulator and real quantum device.

Steps Involved:

1. Preparation: Prepare (by convention) state; split the two pair among sender and receiver.
2. Transport: Sender physically moves away with h(is/er) pair of entangled qubit.
3. Encoding: Sender encodes classical bits of information to h(is/er) pair of qubit.
4. Transmission: Sender transmits h(is/er) qubit to receiver.
5. Decoding: Receiver decodes and measures h(is/er) qubit to retrieve sender's classical state.

### Step One : Preparation

It starts with a third party (say, Charlie) who prepares a pair of entangled states at |0⟩ Applying Hadamard gates to both the initialized qubits a CX gate is applied in order to create an entangled state. (First one as control and second one as the target).

def preparation(qc):
qc.h(0)
qc.cx(0,1)


### Step Two : Separation

Sender takes q0 and receiver takes q1, they travel far apart.

def seperation(qc):
qc.barrier()


### Step Three : Encoding

Charlie sends the first qubit to sender and the second qubit to receiver. The goal of the protocol is for sender to send 2 classical bits of information to receiver using his/her qubit. But before he/she does, sender needs to apply a set of quantum gates to his/her qubit depending on the 2 bits of information sender wants to send. Thus if the sender wants to send a 00, he/she does nothing to the qubit (apply the identity (I) gate). If wants to send a 01, then applies the X gate. Depending on what the sender wants to send, the sender applies the appropriate gate, then sends the qubit to the receiver for the final step in the process.

## Sender can manipulate h(is/er) part of entangled qubits to encode 2 bits of classical information
def state_00(qc):
pass

def state_01(qc):
qc.z(0)

def state_10(qc):
qc.x(0)

def state_11(qc):
qc.x(0)
qc.z(0)

def encoding(qc):
message = input('Enter your intended message from 00, 01, 10 and 11 :   ')
if message == '00':
state_00(qc)
elif message == '01':
state_01(qc)
elif message == '10':
state_10(qc)
elif message == '11':
state_11(qc)
else:
print('Invalid message: Sending 00')


### Step Four : Transmission

Sender sends h(is/er) encoded qubit physically to receiver.

def transmission(qc):
qc.barrier()


### Step Five : Decoding

The receiver receives the sender's qubit (leftmost qubit) and uses his qubit to decode the sent message. Notice that he/she does not need to have knowledge of the state in order to decode it — he/she simply uses the restoration operation. The receiver applies a CNOT gate using the leftmost qubit as control and the rightmost as target. Then he applies a Hadamard gate and finally performs a measurement on both qubits to extract transmitted message.

## Receiver bell-measures their qubits to retreive the encoded classical bits information:

def bell_measure(qc):
qc.cx(0,1)
qc.h(0)
qc.measure(range(2),range(2))
display(qc.draw('mpl'))
## Retrieving Information:
backend_sim = Aer.get_backend('statevector_simulator')
#     backend_real_qc = least_busy(provider.backends(filters = lambda x: x.configuration().n_qubits >= 2
#                                           and not x.configuration().simulator
#                                           and x.status().operational == True))
#     print("Running on: ", backend_real_qc)
#     job = execute(qc,backend_real_qc)
job = execute(qc,backend_sim)
result = job.result()
counts = result.get_counts(qc)
received = dict([(value,key) for key,value in counts.items()])
display(plot_histogram(counts))


### The Complete Protocol in a Simulator

## Initializing:
qc_test = QuantumCircuit(2,2)

## Preparing entangled state:
preparation(qc_test) 