# Evaluate on the Real Devices
The [PennyLane-Qiskit plugin](https://docs.pennylane.ai/projects/qiskit/en/latest/index.html) merges Qiskit's quantum computing framework with PennyLane's quantum machine learning capabilities.

To test our model on a real quantum device using Qiskit, the main steps are as follows.

## Install Requirements

```Bash
pip install pennylane-qiskit==0.33.0
```

## Qiskit Set Up 

For newcomers to Qiskit's online services, an API Token is required, which can be obtained from your [Qiskit account](https://quantum.ibm.com/) .

You can try out the code below by filling in your API token to save your account locally. Ensure that your network connection is stable.
```Python
from qiskit_ibm_provider import IBMProvider
IBMProvider.save_account(token='MY_API_TOKEN')
provider = IBMProvider() # loads saved account from disk
```
## Device Selection
PennyLane-Qiskit supports running PennyLane on IBM Q hardware via the `qistkit.ibmq` device. We can choose between different backends.
```Python
dev = qml.device(name='qiskit.ibmq', backend='ibm_osaka', wires=n_qubits)
```
We can check the currently available backends on the Qiskit website, keeping in mind that different devices may support different sets of quantum gates. Our model is compatible with at least `ibm_osaka` and `ibm_brisbane`.

There's some other simulator devices that may also be used:
- [`qiskit.aer`](https://docs.pennylane.ai/projects/qiskit/en/latest/devices/aer.html), which enables deploying and executing pennylane models on the backends and simulators offered by `Qiskit Aer`.
- [`defualt.qubit`](https://docs.pennylane.ai/en/stable/code/api/pennylane.devices.default_qubit.html), which is PennyLane’s standard qubit-based device.
- [`default.mixed`](https://docs.pennylane.ai/en/stable/code/api/pennylane.devices.default_mixed.html), which is PennyLane’s standard qubit simulator for mixed-state computations and supports direct density matrix returns.
## Model Migration

Real quantum devices cannot return a quantum state vector directly; they can only return classical measurement outcomes.


To be more specific, we need to modify the return of the quantum circuit to obtain measurement outcomes instead of quantum states. This typically involves using functions like `qml.probs`, `qml.eval` to retrieve the probability distribution or classical measurement results, respectively, instead of the quantum state itself.
```Python
@qml.qnode(device)
def circuit(inputs):
    # ...

    # To get the state vector, if it's avilable on ideal simulators.
    return qml.state()

    # To get the probability distribution from measuring the quantum state in the computational basis.
    return qml.probs(wires=range(n_qubits))

    # To get the results from performing classical shadow measurements on the quantum state.
    return qml.classical_shadow(wires=range(n_qubits))

    # ...
```
If we obtain a probability distribution, we can compare the differences between the probability distributions obtained from an ideal simulator and a real quantum device using **KL Divergence**.
```Python
from scipy import stats
entropy = stats.entropy(p, q)  # the KL-Divergence between p and q
```
If we aim to compute the fidelity between two states represented as density matrices, we can utilize the [`default.mixed`](https://docs.pennylane.ai/en/stable/code/api/pennylane.devices.default_mixed.html) device to acquire the density matrix on an ideal simulator. However, obtaining the density matrix on a real device necessitates the application of *Quantum State Tomography*.

Quantum state tomography fully characterizes unknown quantum states but requires precise measurements for many observables, which grows exponentially with qubit count. Huang et al. introduced the **[classical shadow](https://pennylane.ai/qml/demos/tutorial_classical_shadows/#example-reconstructing-a-bell-state)** approximation as a solution. This method efficiently constructs a classical shadow representation of the quantum state, allowing estimation of properties such as state fidelity, Hamiltonian expectation values, and more.
So by using `qml.classical_shadow` and [`shadow_state_reconstruction`](eval_AE_on_real_devices.ipynb) function, we can obtain an approximation of the density matrix of the quantum state output by our models, which helps us calculate fidelity.
```Python
import pennylane as qml
qml.math.fidelity(P, Q)  # the Fidelity between P and Q
```
Note that the input matrices for `qml.math.fidelity` must satisfy the conditions of density matrices, such as having a trace of 1 and being positive semi-definite, to ensure that the computed fidelity falls within the range of $[0, 1]$.

---
Finally, instructions for transferring our models to real quantum devices are as follows:
- [AE](eval_AAE_on_real_device.ipynb): 
    + Device Transition
    + Modify the output of the quantum circuit
    (e.g. from `qml.state` to `qml.probs`)
    + Calculate the KL divergence between two probability distributions
- [AAE](eval_AAE_on_real_device.ipynb) & [SuperEncoder](eval_SuperEncoder_on_real_device.ipynb):
    + Device Transition
    + Retrieve the weights of the PQC output from our models
    + Load these parameters onto the real device's PQC
    + Modify the output of the quantum circuit
    (e.g. from `qml.state` to `qml.probs`)
    + Calculate the KL divergence between two probability distributions