# **Qiskit Global Hackathon**
# Quantum Anomaly Detection with Quantum Density Estimation

## Data Load

We will work with the following datasets:

*   SpamBase: dataset from UCI Machine Learning Repository, includes 3485 emails classified as spam or non-spam. This dataset has 58 attributes. We treat spam as outliers. The anomaly ratio is 0.2
*   Cardiotocography: data is also from UCI Machine Learning Repository and is related to heart diseases. This dataset contains 22 attributes. It describes 3
classes: normal, suspect, and pathological. Normal patients are treated as inliers and the remaining as outliers. The anomaly ratio is 0.2

### Imports and Libraries

```python
import numpy as np
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
```

```python
!pip install qiskit
!pip install pylatexenc
```

    Collecting qiskit
      Downloading qiskit-0.32.0.tar.gz (13 kB)
    Collecting qiskit-terra==0.18.3
      Downloading qiskit_terra-0.18.3-cp37-cp37m-manylinux2010_x86_64.whl (6.1 MB)
         |████████████████████████████████| 6.1 MB 5.0 MB/s
    Collecting qiskit-aer==0.9.1
      Downloading qiskit_aer-0.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (17.9 MB)
         |████████████████████████████████| 17.9 MB 112 kB/s
    Collecting qiskit-ibmq-provider==0.18.0
      Downloading qiskit_ibmq_provider-0.18.0-py3-none-any.whl (237 kB)
         |████████████████████████████████| 237 kB 43.6 MB/s
    Collecting qiskit-ignis==0.6.0
      Downloading qiskit_ignis-0.6.0-py3-none-any.whl (207 kB)
         |████████████████████████████████| 207 kB 47.4 MB/s
    Collecting qiskit-aqua==0.9.5
      Downloading qiskit_aqua-0.9.5-py3-none-any.whl (2.1 MB)
         |████████████████████████████████| 2.1 MB 56.4 MB/s
    Requirement already satisfied: numpy>=1.16.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.19.5)
    Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.4.1)
    Collecting yfinance>=0.1.62
      Downloading yfinance-0.1.66-py2.py3-none-any.whl (25 kB)
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (5.4.8)
    Collecting retworkx>=0.8.0
      Downloading retworkx-0.10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.4 MB)
         |████████████████████████████████| 1.4 MB 54.0 MB/s
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.1.5)
    Collecting docplex>=2.21.207
      Downloading docplex-2.22.213.tar.gz (634 kB)
         |████████████████████████████████| 634 kB 52.7 MB/s
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.7.1)
    Collecting dlx<=1.0.4
      Downloading dlx-1.0.4.tar.gz (5.5 kB)
    Requirement already satisfied: h5py<3.3.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (3.1.0)
    Requirement already satisfied: fastdtw<=0.3.4 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.3.4)
    Collecting quandl
      Downloading Quandl-3.7.0-py2.py3-none-any.whl (26 kB)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (57.4.0)
    Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.0.1)
    Collecting requests-ntlm>=1.1.0
      Downloading requests_ntlm-1.1.0-py2.py3-none-any.whl (5.7 kB)
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.0->qiskit) (1.24.3)
    Collecting websocket-client>=1.0.1
      Downloading websocket_client-1.2.1-py2.py3-none-any.whl (52 kB)
         |████████████████████████████████| 52 kB 1.5 MB/s
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.0->qiskit) (2.23.0)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.0->qiskit) (2.8.2)
    Collecting ply>=3.10
      Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
         |████████████████████████████████| 49 kB 7.2 MB/s
    Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (2.6.0)
    Collecting symengine>0.7
      Downloading symengine-0.8.1-cp37-cp37m-manylinux2010_x86_64.whl (38.2 MB)
         |████████████████████████████████| 38.2 MB 25 kB/s
    Collecting python-constraint>=1.4
      Downloading python-constraint-1.4.0.tar.bz2 (18 kB)
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (0.3.4)
    Collecting tweedledum<2.0,>=1.1
      Downloading tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (943 kB)
         |████████████████████████████████| 943 kB 10 kB/s
    Collecting fastjsonschema>=2.10
      Downloading fastjsonschema-2.15.1-py3-none-any.whl (21 kB)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from docplex>=2.21.207->qiskit-aqua==0.9.5->qiskit) (1.15.0)
    Requirement already satisfied: cached-property in /usr/local/lib/python3.7/dist-packages (from h5py<3.3.0->qiskit-aqua==0.9.5->qiskit) (1.5.2)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.0->qiskit) (2021.10.8)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.0->qiskit) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.0->qiskit) (3.0.4)
    Collecting cryptography>=1.3
      Downloading cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl (3.5 MB)
         |████████████████████████████████| 3.5 MB 44.6 MB/s
    Collecting ntlm-auth>=1.0.2
      Downloading ntlm_auth-1.5.0-py2.py3-none-any.whl (29 kB)
    Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.0->qiskit) (1.15.0)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.0->qiskit) (2.21)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (1.1.0)
    Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (3.0.0)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->qiskit-aqua==0.9.5->qiskit) (1.2.1)
    Collecting lxml>=4.5.1
      Downloading lxml-4.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (6.3 MB)
         |████████████████████████████████| 6.3 MB 34.4 MB/s
    Requirement already satisfied: multitasking>=0.0.7 in /usr/local/lib/python3.7/dist-packages (from yfinance>=0.1.62->qiskit-aqua==0.9.5->qiskit) (0.0.9)
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->qiskit-aqua==0.9.5->qiskit) (2018.9)
    Collecting inflection>=0.3.1
      Downloading inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
    Requirement already satisfied: more-itertools in /usr/local/lib/python3.7/dist-packages (from quandl->qiskit-aqua==0.9.5->qiskit) (8.11.0)
    Building wheels for collected packages: qiskit, dlx, docplex, python-constraint
      Building wheel for qiskit (setup.py) ... done
      Created wheel for qiskit: filename=qiskit-0.32.0-py3-none-any.whl size=11773 sha256=0e0a8f61973bfacbfac251dd07a663d0f6707738f0cbc5e4e4014557db3dbd72
      Stored in directory: /root/.cache/pip/wheels/95/84/c4/6a084a33c1909266fca66eed2303c3883e03f33c905b1ebed9
      Building wheel for dlx (setup.py) ... done
      Created wheel for dlx: filename=dlx-1.0.4-py3-none-any.whl size=5719 sha256=7cc96e79bc1798836dc0f5d6c5f351c90e09f9c192a514b0572066399983cc0c
      Stored in directory: /root/.cache/pip/wheels/78/55/c8/dc61e772445a566b7608a476d151e9dcaf4e092b01b0c4bc3c
      Building wheel for docplex (setup.py) ... done
      Created wheel for docplex: filename=docplex-2.22.213-py3-none-any.whl size=696881 sha256=8faa8a1c2cd65a8a49d2c9d7a51cf958b5aedd9a61046de2159ce286a6c5c26a
      Stored in directory: /root/.cache/pip/wheels/90/69/6b/1375c68a5b7ff94c40263b151c86f58bd72200bf0c465b5ba3
      Building wheel for python-constraint (setup.py) ... done
      Created wheel for python-constraint: filename=python_constraint-1.4.0-py2.py3-none-any.whl size=24081 sha256=64ae286c8ef37ebabdd0951729b52507e0a65f96b08904700973a2ab323956fd
      Stored in directory: /root/.cache/pip/wheels/07/27/db/1222c80eb1e431f3d2199c12569cb1cac60f562a451fe30479
    Successfully built qiskit dlx docplex python-constraint
    Installing collected packages: tweedledum, symengine, retworkx, python-constraint, ply, fastjsonschema, qiskit-terra, ntlm-auth, lxml, inflection, cryptography, yfinance, websocket-client, requests-ntlm, quandl, qiskit-ignis, docplex, dlx, qiskit-ibmq-provider, qiskit-aqua, qiskit-aer, qiskit
      Attempting uninstall: lxml
        Found existing installation: lxml 4.2.6
        Uninstalling lxml-4.2.6:
          Successfully uninstalled lxml-4.2.6
    Successfully installed cryptography-35.0.0 dlx-1.0.4 docplex-2.22.213 fastjsonschema-2.15.1 inflection-0.5.1 lxml-4.6.4 ntlm-auth-1.5.0 ply-3.11 python-constraint-1.4.0 qiskit-0.32.0 qiskit-aer-0.9.1 qiskit-aqua-0.9.5 qiskit-ibmq-provider-0.18.0 qiskit-ignis-0.6.0 qiskit-terra-0.18.3 quandl-3.7.0 requests-ntlm-1.1.0 retworkx-0.10.2 symengine-0.8.1 tweedledum-1.1.1 websocket-client-1.2.1 yfinance-0.1.66
    Collecting pylatexenc
      Downloading pylatexenc-2.10.tar.gz (162 kB)
         |████████████████████████████████| 162 kB 5.0 MB/s
    Building wheels for collected packages: pylatexenc
      Building wheel for pylatexenc (setup.py) ... done
      Created wheel for pylatexenc: filename=pylatexenc-2.10-py3-none-any.whl size=136833 sha256=6602924c1ea488fd85901d972722d5635be4d14ab56a168df09742ee09f41dcb
      Stored in directory: /root/.cache/pip/wheels/f1/8a/f5/33ee79d4473eb201b519fa40f989b842e373237395a3421f52
    Successfully built pylatexenc
    Installing collected packages: pylatexenc
    Successfully installed pylatexenc-2.10

```python
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit import Aer, execute
```

### Load from Drive

```python
#Loading SpamBase dataset file
!gdown --id 1Hdq-Svp31YkTj7wFZY3D2fsnBM1tbr_t
```

    Downloading...
    From: https://drive.google.com/uc?id=1Hdq-Svp31YkTj7wFZY3D2fsnBM1tbr_t
    To: /content/SpamBase.npy
    100% 1.65M/1.65M [00:00<00:00, 52.5MB/s]

```python
#Loading Cardiotocography dataset file
!gdown --id 1sVygYNPrTQI9d-8kVnx7w5nGWOSosveX
```

    Downloading...
    From: https://drive.google.com/uc?id=1sVygYNPrTQI9d-8kVnx7w5nGWOSosveX
    To: /content/Cardiotocography.npy
    100% 379k/379k [00:00<00:00, 79.9MB/s]

```python
spambase = np.load("SpamBase.npy")

cardio = np.load("Cardiotocography.npy")
```

### Preprocessing

This function extracts and separates the features and the labels from the whole dataset. We will apply it to both datasets. Normal data will be labeled as '1' and anomalies will be labeled as '0'.

```python
from sklearn.preprocessing import MinMaxScaler

def preprocessing(data):
    features = data[:,:-1]
    labels = data[:,-1]

    scaler = MinMaxScaler()
    scaler.fit(features)
    features = scaler.transform(features)

    labels = labels[:,np.newaxis]
    labels[labels == -1] = 0

    return features, labels
```

```python
spam_X, spam_y = preprocessing(spambase)
cardio_X, cardio_y = preprocessing(cardio)
```

```python
spam_X.shape
```

    (3485, 58)

```python
cardio_X.shape
```

    (2060, 22)

## Random Fourier Features

To perform dimensionality reduction, we will use Random Fourier Features, an approach stablished by Rahimi and Recht, 2007. RFF is a type of quantum feature map from data to quantum states, in such a way that it can fit more precisely the underlying probability distribution of data.

```python
from sklearn.kernel_approximation import RBFSampler
"""
  Code from https://arxiv.org/abs/2004.01227
"""
class QFeatureMap:

  def get_dim(self, num_features):
    pass

  def batch2wf(self, X):
    pass

  def batch2dm(self, X):
    pass

  def batch2dm(self, X):
    psi = self.batch2wf(X)
    rho = np.einsum('...i,...j', psi, np.conj(psi))
    return rho

class QFeatureMap_rff(QFeatureMap):

  def __init__(self, rbf_sampler):
    self.rbf_sampler = rbf_sampler
    self.weights = np.array(rbf_sampler.random_weights_)
    self.offset = np.array(rbf_sampler.random_offset_)
    self.dim = rbf_sampler.get_params()['n_components']

  def get_dim(self, num_features):
    return self.dim

  def batch2wf(self, X):
    vals = np.dot(X, self.weights) + self.offset
    vals = np.cos(vals)
    vals *= np.sqrt(2.) / np.sqrt(self.dim)
    norms = np.linalg.norm(vals, axis=1)
    psi = vals / norms[:, np.newaxis]
    return psi
```

## Cardio 4x4 Mixed State

First, we will work with Cardiotocography dataset.

The number of RFF we will use depends on the size of the density matrix we will build. In this case, we will use a 4x4 density matrix, so we will reduce our samples to arrays of length 4. Transforming each sample will allow us to convert those values into two-qubit states.

```python
# Create the RandomFourierFeature map
dim = 4
rm_state = 2
sigma = 1.5  #dataset dependant
gamma = 1 / (2 * sigma**2)

feature_map_fourier = RBFSampler(gamma=gamma, n_components=dim, random_state=rm_state)
X_feat_train = feature_map_fourier.fit(cardio_X)

rffmap = QFeatureMap_rff(rbf_sampler=feature_map_fourier)

Crff = rffmap.batch2wf(cardio_X)

print(Crff.shape)
```

    (2060, 4)

Once our samples have been reduced, it's important to normalize them. Also we will split the dataset into three groups, a training partition with only normal data (70%), and a test partition with a group of normal data (10%) and a group of outlier data (20%).

```python
Cones = []
Ctrain = []
Ctest_zeros = []
Ctest_ones = []

for i in range(Crff.shape[0]):
    if (cardio_y[i] == 1):
        Cones.append(Crff[i] / np.linalg.norm(Crff[i]))
    elif (cardio_y[i] == 0):
        Ctest_zeros.append(Crff[i] / np.linalg.norm(Crff[i]))

Ctest_ones = Cones[:len(Ctest_zeros)//2]
Ctrain = Cones[len(Ctest_zeros)//2:]

print(len(Ctrain))
print(len(Ctest_zeros))
print(len(Ctest_ones))
```

    1442
    412
    206

```python
print(Ctrain[0])
print(sum(Ctrain[0]**2))
```

    [ 0.6607845   0.60963091 -0.29528374 -0.32329786]
    1.0000000000000002

Now, we will define the training state using the Mixed State approach. We will use the density matrix corresponding to the training state, $\rho_1$. So we have

$$
\rho_1 = \frac{1}{m} \sum |\phi_1 \rangle \langle \phi_1 |
$$

being $|\phi_1 \rangle$ the states labeled with '1' (aka training normal data), and $m$ the amount of samples. To construct the density matrices, only train samples will be used.

```python
import copy
def round_lambda(lambda_param_2):
  lambda_param = copy.copy(lambda_param_2)
  for i in range(4):
    if lambda_param[i] < 0.00000001:
      lambda_param[i] = 0.00000001
  lambda_param[3] = 1 - lambda_param[0] - lambda_param[1] - lambda_param[2]
  return lambda_param
```

```python
Z_train = np.outer(np.abs(Ctrain[0]), np.abs(Ctrain[0]))
for i in range(1, len(Ctrain)):
    Z_train += np.outer(np.abs(Ctrain[i]), np.abs(Ctrain[i]))

Z_train *= 1/len(Ctrain)
print(Z_train)
```

    [[0.28539094 0.21797308 0.12163383 0.28368129]
     [0.21797308 0.20571635 0.10524944 0.25986083]
     [0.12163383 0.10524944 0.08864394 0.14422189]
     [0.28368129 0.25986083 0.14422189 0.42024877]]

```python
lambda_P1_temp, U_train = np.linalg.eigh(Z_train)
lambda_train = round_lambda(lambda_P1_temp)
print(lambda_train)
print(U_train)
```

    [0.01929039 0.02967972 0.06373443 0.88729546]
    [[ 0.41964602 -0.29678184 -0.67344497 -0.53130936]
     [-0.87135899 -0.04328634 -0.15939879 -0.46200847]
     [ 0.11676352  0.94808647 -0.13909626 -0.26105661]
     [ 0.22582516 -0.10573021  0.70831923 -0.66038473]]

```python
lambda_train = lambda_train / np.linalg.norm(lambda_train)
print(lambda_train)
print((sum(lambda_train**2)))
```

    [0.02166764 0.0333373  0.07158875 0.99664145]
    0.9999999999999999

The quantum circuit we propose will be based in a Qiskit gate called `Isometry`, that allows to prepare arbitrary unitary rotations.

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|00 \rangle$ obtained. This value will be proportional to the density value estimated by the circuit.

```python
backend2 = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 4x4
Cpred_ones = []

for i in range(len(Ctest_ones)):

    qc = QuantumCircuit(4, 2)

    qc.initialize(Ctest_ones[i], [0, 1])
    qc.initialize(lambda_train, [2, 3])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.cnot(3, 1)
    qc.cnot(2, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Cpred_ones.append((counts['00'])/5000)

print(Cpred_ones)
```

    [0.214, 0.0926, 0.0502, 0.1488, 0.0394, 0.024, 1.7982, 0.139, 0.215, 0.0768, 0.8086, 0.0146, 1.2966, 0.3184, 0.5238, 0.0308, 1.652, 1.8776, 1.1904, 1.6002, 2.102, 1.6478, 2.3404, 3.03, 1.912, 0.6914, 2.5166, 0.8448, 1.0294, 1.9948, 1.7792, 2.6616, 0.1438, 0.2088, 0.365, 0.016, 0.0134, 0.0192, 0.0416, 0.0148, 0.0114, 0.4522, 0.0524, 0.0104, 0.025, 0.0088, 0.0356, 0.0512, 0.0162, 0.0168, 0.0122, 0.032, 0.0128, 0.0122, 0.0154, 0.0136, 0.0238, 0.023, 0.0346, 0.0196, 0.0152, 0.0166, 0.0266, 0.0202, 0.0176, 2.2426, 0.406, 0.7414, 0.1448, 0.3144, 0.185, 0.1002, 0.4938, 1.2452, 0.8562, 0.1414, 1.4406, 0.7256, 0.4566, 0.2614, 2.3354, 2.038, 0.7778, 0.8568, 1.2222, 2.0038, 1.44, 0.977, 1.8636, 0.0114, 0.0584, 0.0152, 0.0418, 0.0548, 0.0428, 0.0394, 0.07, 0.0514, 0.0114, 0.0216, 0.0282, 0.013, 0.0132, 0.012, 0.0152, 0.0102, 0.0154, 0.1048, 0.8522, 0.6226, 1.0562, 0.994, 0.9546, 2.4066, 1.6386, 1.971, 1.9234, 0.4604, 1.1696, 0.8214, 0.0248, 0.0176, 0.022, 0.0766, 1.7426, 1.5724, 1.8348, 0.6992, 0.6444, 1.223, 1.0258, 0.2486, 2.0646, 0.9304, 2.1496, 1.3494, 0.5732, 0.2638, 0.229, 0.4562, 0.0378, 0.0372, 0.016, 0.0148, 0.0144, 0.4088, 0.2708, 0.8768, 0.6712, 0.5704, 0.9874, 0.7964, 0.2752, 0.2256, 0.3818, 0.1682, 1.2658, 0.5136, 1.177, 0.2318, 0.1374, 0.3546, 0.219, 0.058, 0.3042, 0.3052, 0.0712, 0.4282, 0.3678, 0.3186, 0.104, 0.0922, 0.112, 0.29, 0.2824, 0.1354, 0.282, 0.1428, 0.273, 0.5684, 0.6214, 0.5926, 0.1006, 0.021, 0.0312, 0.2068, 1.466, 1.125, 2.0816, 2.0992, 2.9582, 3.4812, 0.2064, 0.6486, 0.8182, 0.8778, 0.581, 0.5314, 0.6316, 0.3652, 0.5612, 0.119, 1.982, 2.0294, 0.9474, 0.1484]

```python
# Mixed state case
Cpred_zeros = []

for i in range(len(Ctest_zeros)):

    qc = QuantumCircuit(4, 2)

    qc.initialize(Ctest_zeros[i], [0, 1])
    qc.initialize(lambda_train, [2, 3])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.cnot(3, 1)
    qc.cnot(2, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Cpred_zeros.append((counts['00'])/5000)

print(Cpred_zeros)
```

    [0.0684, 0.1196, 0.0228, 0.0174, 0.0092, 0.5514, 0.789, 0.0748, 0.0082, 0.0876, 1.1682, 0.0102, 1.1918, 0.0184, 0.04, 0.1338, 0.3542, 0.2672, 0.248, 0.28, 0.011, 0.0152, 0.0174, 0.5672, 0.2106, 0.0248, 0.009, 0.021, 0.0268, 0.041, 0.2644, 0.3378, 0.016, 0.7752, 1.2374, 0.5204, 0.0172, 0.0104, 0.0708, 0.037, 0.1128, 0.0136, 0.0252, 0.115, 0.0616, 0.6658, 0.0126, 0.2878, 0.0214, 0.0672, 0.0874, 0.0114, 0.0582, 0.3758, 0.009, 0.2992, 0.0364, 0.1518, 0.0722, 0.3478, 0.111, 0.0648, 0.5902, 0.0242, 1.0012, 0.0164, 0.0106, 0.2602, 0.0152, 0.0772, 1.1338, 0.593, 0.0168, 0.8484, 0.0268, 0.0232, 0.0114, 0.0176, 0.4136, 0.7822, 0.3392, 0.0552, 1.6286, 0.7188, 0.1488, 0.0286, 0.0144, 0.0146, 0.84, 0.3134, 0.014, 0.015, 0.0148, 0.064, 0.06, 0.117, 0.056, 0.0116, 0.1024, 1.4278, 0.0128, 0.2292, 0.026, 0.0262, 0.069, 0.356, 0.0104, 0.0924, 0.0272, 0.015, 0.0238, 0.0434, 0.474, 0.0202, 0.0856, 0.0202, 0.0542, 0.063, 0.3098, 0.0218, 0.0302, 0.028, 0.405, 0.0528, 0.0622, 0.1258, 0.0154, 0.0336, 0.0142, 0.107, 0.8516, 0.0092, 0.3008, 0.0148, 0.077, 0.0624, 0.0142, 0.1434, 0.0188, 0.0234, 0.1496, 0.6172, 0.049, 0.0336, 0.0726, 1.4534, 0.0308, 0.0124, 0.0118, 0.1766, 0.187, 0.0326, 0.042, 0.2916, 0.0082, 0.0072, 0.7636, 0.0358, 0.1036, 0.0144, 0.017, 0.0318, 0.0248, 0.0134, 0.0164, 1.7168, 0.1312, 0.0398, 0.0172, 0.0204, 0.0214, 1.0062, 0.0314, 1.04, 0.0124, 0.055, 0.0104, 0.0186, 0.144, 0.0246, 0.155, 0.2672, 0.2018, 0.027, 0.0846, 0.7332, 0.0436, 0.0332, 0.0124, 0.054, 1.2874, 0.0144, 0.2728, 0.152, 0.0138, 0.5634, 0.0642, 0.517, 1.6048, 0.1502, 0.0106, 0.0106, 0.0174, 0.0996, 0.0358, 0.0718, 0.4016, 0.0204, 0.038, 0.1126, 0.5868, 0.3066, 0.0138, 0.2104, 0.0318, 0.058, 0.0126, 0.684, 0.3044, 0.2122, 0.1494, 0.816, 0.054, 0.0136, 0.8636, 0.0996, 0.039, 0.0476, 0.0088, 0.1226, 0.0112, 0.011, 0.0172, 1.5268, 0.0226, 0.0288, 0.01, 0.0098, 0.24, 0.1726, 0.0364, 0.0142, 0.0274, 0.053, 0.291, 0.0242, 0.0124, 0.1734, 0.0546, 0.0516, 0.246, 0.1534, 0.013, 0.3654, 0.0224, 0.0804, 0.049, 0.8916, 0.0404, 0.0174, 0.195, 0.016, 0.051, 1.1064, 0.0494, 0.0284, 0.0254, 0.0556, 0.02, 0.422, 0.0366, 0.0116, 0.688, 0.0166, 0.0172, 0.0716, 0.012, 0.034, 0.0618, 0.1032, 0.0662, 0.561, 0.0264, 0.0394, 0.0072, 0.115, 0.1048, 0.0406, 0.0684, 0.0226, 1.189, 0.0242, 0.0286, 0.069, 0.3132, 0.115, 0.4874, 0.6444, 0.1186, 0.0132, 0.0896, 0.0752, 0.0638, 0.6342, 0.3828, 0.0104, 0.0156, 0.0606, 0.8116, 0.0088, 0.0604, 0.0582, 0.1752, 0.202, 0.1716, 0.0246, 0.021, 0.1954, 0.017, 0.1268, 0.0158, 0.0238, 0.016, 0.0914, 0.0492, 0.1222, 0.0164, 0.1958, 0.4514, 0.1094, 0.8494, 0.0846, 0.0342, 0.021, 0.0304, 0.2868, 0.0152, 0.0214, 0.5778, 0.086, 0.029, 0.037, 0.0262, 0.0806, 0.0208, 0.138, 0.0834, 0.0668, 0.0182, 0.0762, 0.0252, 0.0352, 0.0656, 0.0848, 0.1108, 0.0182, 0.013, 0.0086, 0.0182, 0.0658, 0.011, 0.1028, 0.0782, 0.0882, 0.0146, 0.2364, 0.0734, 0.1326, 0.0184, 0.0162, 0.0178, 0.2402, 0.0436, 0.0166, 0.0646, 0.0156, 0.0124, 0.3606, 0.0484, 0.4092, 0.0174, 0.2526, 0.023, 0.0226, 0.214, 2.4952, 0.0372, 0.017, 0.1602, 0.0318, 0.0146, 0.016, 0.011, 0.0736, 0.3264, 0.6418, 0.2806, 1.2536, 0.3376, 0.0106, 0.0348, 0.0092, 0.2552, 0.0106, 0.0404, 0.082, 0.0126, 0.038, 0.0778, 0.0248, 0.0122, 0.2288]

Once we have obtained the predicted density values, we need to determine a threshold $t$ such that if a sample has a density value lower than $t$, it is classified as an outlier. For this, we will define a validation partition from our test groups, and we'll try to find the best possible value for $t$ using only validation samples (based on the best possible value of weighted F1-score). After that, we'll use this threshold to compare with all the remaining test samples.

```python
Cpred_val = np.concatenate((Cpred_ones[:100], Cpred_zeros[:100]))
print(Cpred_val.shape)

Cy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Cy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Cy_val, Cpred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Cpred_val > t).astype(int)
    metric = f1_score(Cy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.3758

We have obtained that our best threshold is at $t = 0.3758$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Cpr_zeros = (np.array(Cpred_zeros) > T).astype(int)

Cpr_ones = (np.array(Cpred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(106), np.zeros(312))),
        np.concatenate((Cpr_ones[100:], Cpr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.8431    0.8782    0.8603       312
             1.0     0.5914    0.5189    0.5528       106

        accuracy                         0.7871       418
       macro avg     0.7172    0.6985    0.7065       418
    weighted avg     0.7793    0.7871    0.7823       418

## Cardio 4x4 Pure State

Now, we will define another way to represent the training density matrix: the Pure State approach. First, we will define the state  $|\psi_1 \rangle$.

$$
 |\psi_1 \rangle = \frac {\sum |\phi_1 \rangle} {\left \| \sum |\phi_1 \rangle \right \|}
$$

being $|\phi_1 \rangle$ the states labeled with '1'. To construct this state, only train samples will be used.

Once obtained this state, it represents all the information from training states. So we build the density matrix as

$$
\rho_1 = |\psi_1 \rangle \langle ψ_1 |
$$

```python
# Given the eigenvalues this function return a Unitary gate which converts the |0> -> |lambda>
def create_U_train(x_train_param, seed=0):
  np.random.seed(seed)
  size_U = len(x_train_param)
  U_train = np.zeros((size_U, size_U))
  x_1 = x_train_param
  U_train[:, 0] = x_1
  for i in range(1, size_U):
    x_i = np.random.randn(size_U)
    for j in range(0, i):
      x_i -= x_i.dot(U_train[:, j]) * U_train[:, j]
    x_i = x_i/np.linalg.norm(x_i)
    U_train[:, i] = x_i

  return U_train
```

```python
phi_train = np.sum(np.abs(Ctrain), axis=0)
phi_train = phi_train / np.linalg.norm(phi_train)

U_train = create_U_train(phi_train)

print(U_train)
```

    [[ 0.53308287  0.2309175   0.69538315 -0.42301541]
     [ 0.46304288 -0.88279704 -0.0144998   0.07778448]
     [ 0.26452084  0.21352962  0.27364652  0.89975075]
     [ 0.65684295  0.34892921 -0.66434149 -0.0738656 ]]

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|00 \rangle$ obtained.

```python
backend = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 4x4
Cpred_ones = []

for i in range(len(Ctest_ones)):

    qc = QuantumCircuit(2, 2)
    qc.initialize(Ctest_ones[i], [0, 1])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Cpred_ones.append((counts['00'])/5000)
    except:
        Cpred_ones.append(0)

print(Cpred_ones)
```

    [0.1996, 0.0846, 0.0454, 0.1368, 0.0266, 0.0068, 1.7762, 0.1236, 0.1914, 0.0546, 0.7986, 0.0002, 1.3286, 0.3082, 0.517, 0.0216, 1.6652, 1.8668, 1.1788, 1.6422, 2.1298, 1.619, 2.3728, 3.0332, 1.9352, 0.6874, 2.5484, 0.8588, 1.027, 1.9708, 1.777, 2.684, 0.1366, 0.2012, 0.3582, 0.0082, 0.0026, 0.0086, 0.0328, 0.0034, 0.0002, 0.465, 0.047, 0.0022, 0.0084, 0.001, 0.0262, 0.0354, 0.0022, 0.0048, 0.0014, 0.0186, 0.0008, 0, 0.0024, 0, 0.0054, 0.0094, 0.0206, 0.0118, 0.001, 0.0034, 0.0152, 0.01, 0.0068, 2.2896, 0.3976, 0.7394, 0.1436, 0.3064, 0.1648, 0.0842, 0.4678, 1.239, 0.8436, 0.1286, 1.4268, 0.7234, 0.4642, 0.2516, 2.3294, 2.0588, 0.7798, 0.8872, 1.2348, 2.0036, 1.4612, 0.9752, 1.8852, 0.0006, 0.0474, 0.005, 0.0296, 0.0446, 0.0286, 0.0298, 0.0688, 0.038, 0.0006, 0.0078, 0.0192, 0.002, 0, 0.0002, 0.0032, 0.002, 0.0016, 0.1096, 0.8788, 0.6206, 1.0462, 1.0264, 0.9898, 2.4422, 1.668, 1.985, 1.9574, 0.4466, 1.187, 0.8152, 0.0114, 0.0078, 0.0104, 0.0622, 1.7514, 1.582, 1.8674, 0.7056, 0.649, 1.2374, 1.04, 0.2398, 2.0512, 0.9438, 2.1594, 1.3436, 0.5694, 0.279, 0.2144, 0.4622, 0.0182, 0.0292, 0.005, 0.008, 0.0074, 0.3964, 0.2662, 0.91, 0.6912, 0.567, 0.9536, 0.8, 0.2822, 0.213, 0.3856, 0.1646, 1.271, 0.4966, 1.2232, 0.2312, 0.1152, 0.3588, 0.211, 0.0522, 0.3026, 0.302, 0.0674, 0.4134, 0.3532, 0.3264, 0.0932, 0.074, 0.1006, 0.277, 0.272, 0.1228, 0.273, 0.1318, 0.2576, 0.5556, 0.62, 0.6026, 0.0882, 0.013, 0.0228, 0.193, 1.4726, 1.1258, 2.1138, 2.1274, 2.9494, 3.5138, 0.2066, 0.631, 0.8064, 0.8994, 0.5738, 0.527, 0.6478, 0.3434, 0.5528, 0.1024, 1.9802, 2.0098, 0.9384, 0.1304]

```python
# Pure state case
Cpred_zeros = []

for i in range(len(Ctest_zeros)):

    qc = QuantumCircuit(2, 2)
    qc.initialize(Ctest_zeros[i], [0, 1])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Cpred_zeros.append((counts['00'])/5000)
    except:
        Cpred_zeros.append(0)

print(Cpred_zeros)
```

    [0.0598, 0.1026, 0.0114, 0.0046, 0.0002, 0.556, 0.8118, 0.0616, 0, 0.0732, 1.194, 0, 1.2268, 0.0088, 0.0272, 0.127, 0.3356, 0.2658, 0.2252, 0.2706, 0.0002, 0.0048, 0.0056, 0.5674, 0.1846, 0.008, 0, 0.0084, 0.013, 0.0212, 0.2624, 0.3156, 0.0086, 0.8002, 1.223, 0.5228, 0.006, 0, 0.0554, 0.0276, 0.1162, 0, 0.0146, 0.1088, 0.038, 0.6774, 0, 0.29, 0.011, 0.0558, 0.0818, 0, 0.045, 0.3692, 0.0004, 0.315, 0.0282, 0.1598, 0.0712, 0.3218, 0.086, 0.0512, 0.5896, 0.0152, 0.9942, 0, 0.0004, 0.261, 0.0052, 0.0766, 1.1412, 0.62, 0, 0.842, 0.0176, 0.0182, 0.0004, 0.0142, 0.3906, 0.7982, 0.3298, 0.0412, 1.6536, 0.727, 0.1456, 0.0172, 0, 0.0072, 0.8646, 0.319, 0.0042, 0.0018, 0.0024, 0.0574, 0.0498, 0.098, 0.041, 0.0002, 0.0984, 1.4236, 0.0002, 0.232, 0.0134, 0.0162, 0.0604, 0.353, 0, 0.082, 0.0126, 0.0056, 0.012, 0.0444, 0.452, 0.0042, 0.074, 0.0086, 0.047, 0.066, 0.311, 0.012, 0.0176, 0.0048, 0.4014, 0.034, 0.0452, 0.128, 0.0084, 0.0138, 0.0014, 0.1082, 0.8632, 0.0014, 0.2948, 0.0034, 0.0724, 0.044, 0.0048, 0.1338, 0.0056, 0.002, 0.132, 0.6268, 0.0442, 0.0192, 0.052, 1.4748, 0.0302, 0.0002, 0.004, 0.1506, 0.1682, 0.0216, 0.0352, 0.2738, 0.0014, 0.0002, 0.7602, 0.0246, 0.0912, 0.0032, 0.0064, 0.0184, 0.0106, 0.0048, 0.0008, 1.7116, 0.1258, 0.0248, 0.0108, 0.0096, 0.0108, 1.0116, 0.0202, 1.032, 0.0022, 0.05, 0.0018, 0.0004, 0.134, 0.0074, 0.148, 0.2744, 0.1904, 0.0176, 0.0734, 0.7384, 0.0278, 0.0174, 0.0012, 0.046, 1.2948, 0.0008, 0.2664, 0.1642, 0.0022, 0.5812, 0.055, 0.4998, 1.6366, 0.143, 0.003, 0, 0.0004, 0.0854, 0.0178, 0.0674, 0.3784, 0.0152, 0.0194, 0.0992, 0.606, 0.3162, 0.0034, 0.1998, 0.0214, 0.0394, 0.0018, 0.6774, 0.2974, 0.2008, 0.1512, 0.8478, 0.0466, 0.0004, 0.8666, 0.0928, 0.0278, 0.0358, 0.0002, 0.1156, 0.0012, 0.0002, 0.007, 1.5266, 0.0038, 0.015, 0, 0.0012, 0.2304, 0.1542, 0.0264, 0.0008, 0.0128, 0.042, 0.2764, 0.0122, 0.001, 0.1464, 0.0466, 0.0474, 0.2438, 0.127, 0.0006, 0.3564, 0.0134, 0.0558, 0.0356, 0.9062, 0.0312, 0.0084, 0.189, 0.0062, 0.0394, 1.1194, 0.0404, 0.008, 0.014, 0.0426, 0.009, 0.4, 0.0252, 0.0012, 0.6902, 0.0046, 0.0028, 0.0644, 0, 0.0114, 0.0554, 0.0904, 0.0596, 0.5346, 0.0068, 0.0346, 0.0008, 0.1116, 0.092, 0.0302, 0.0706, 0.0126, 1.1898, 0.0094, 0.0186, 0.0534, 0.2902, 0.1206, 0.4848, 0.6418, 0.1022, 0.002, 0.0926, 0.07, 0.0494, 0.6082, 0.3758, 0, 0.0072, 0.0488, 0.7876, 0.0002, 0.0516, 0.0404, 0.1518, 0.1992, 0.1576, 0.0174, 0.006, 0.1982, 0.002, 0.1082, 0.0056, 0.0092, 0.0074, 0.0788, 0.039, 0.1144, 0.0082, 0.1896, 0.4492, 0.0964, 0.8648, 0.0724, 0.0224, 0.0104, 0.0214, 0.2658, 0.002, 0.0158, 0.5514, 0.0752, 0.006, 0.0286, 0.0196, 0.0784, 0.0116, 0.1304, 0.0546, 0.0532, 0.0074, 0.065, 0.0116, 0.017, 0.0548, 0.0752, 0.102, 0.0008, 0.0038, 0, 0.006, 0.0626, 0.0008, 0.1, 0.0714, 0.0742, 0.0014, 0.2422, 0.0658, 0.1124, 0.004, 0.0036, 0.0072, 0.2384, 0.0304, 0.0078, 0.0582, 0.004, 0.0002, 0.3568, 0.0332, 0.4258, 0.0082, 0.2454, 0.0138, 0.004, 0.21, 2.4716, 0.0186, 0.0068, 0.1514, 0.0204, 0.0024, 0.0076, 0.0002, 0.0626, 0.3102, 0.612, 0.2724, 1.2288, 0.3316, 0.0024, 0.023, 0.0002, 0.239, 0.004, 0.0228, 0.0744, 0.005, 0.021, 0.0656, 0.0136, 0, 0.214]

Once we have obtained the predicted density values, we need to determine a threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier. For this, we will define a validation partition from our test groups, and we'll try to find the best possible value for $t$ (based on weighted F1-score) using only validation samples. After that, we'll use this threshold to compare with all the remaining test samples.

```python
Cpred_val = np.concatenate((Cpred_ones[:100], Cpred_zeros[:100]))
print(Cpred_val.shape)

Cy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Cy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Cy_val, Cpred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Cpred_val > t).astype(int)
    metric = f1_score(Cy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.3692

We have obtained that our best threshold is at $t = 0.3692$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Cpr_zeros = (np.array(Cpred_zeros) > T).astype(int)

Cpr_ones = (np.array(Cpred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(106), np.zeros(312))),
        np.concatenate((Cpr_ones[100:], Cpr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.8431    0.8782    0.8603       312
             1.0     0.5914    0.5189    0.5528       106

        accuracy                         0.7871       418
       macro avg     0.7172    0.6985    0.7065       418
    weighted avg     0.7793    0.7871    0.7823       418

## Cardio 8x8 Mixed State

Now, we will use a 8x8 density matrix, so we will call a new RFF sampler to reduce our samples to arrays of length 8. Transforming each sample will allow us to convert those values into three-qubit states.

```python
# Create the RandomFourierFeature map
dim = 8
rm_state = 2
sigma = 1.5  #dataset dependant
gamma = 1 / (2 * sigma**2)

feature_map_fourier = RBFSampler(gamma=gamma, n_components=dim, random_state=rm_state)
X_feat_train = feature_map_fourier.fit(cardio_X)

rffmap = QFeatureMap_rff(rbf_sampler=feature_map_fourier)

Crff = rffmap.batch2wf(cardio_X)

print(Crff.shape)
```

    (2060, 8)

Once our samples have been reduced, it's important to normalize them. Also we will split the dataset into three groups, a training partition with only normal data (70%), and a test partition with a group of normal data (10%) and a group of outlier data (20%).

```python
Cones = []
Ctrain = []
Ctest_zeros = []
Ctest_ones = []

for i in range(Crff.shape[0]):
    if (cardio_y[i] == 1):
        Cones.append(Crff[i] / np.linalg.norm(Crff[i]))
    elif (cardio_y[i] == 0):
        Ctest_zeros.append(Crff[i] / np.linalg.norm(Crff[i]))

Ctest_ones = Cones[:len(Ctest_zeros)//2]
Ctrain = Cones[len(Ctest_zeros)//2:]

print(len(Ctrain))
print(len(Ctest_zeros))
print(len(Ctest_ones))
```

    1442
    412
    206

```python
print(Ctrain[0])
print(sum(Ctrain[0]**2))
```

    [ 0.45302627  0.48066672  0.39341633  0.02643167 -0.45585263  0.42733187
      0.13250014  0.01677011]
    0.9999999999999999

Then we build our traning matrix $ρ_1$:

```python
Z_train = np.outer(np.abs(Ctrain[0]), np.abs(Ctrain[0]))
for i in range(1, len(Ctrain)):
    Z_train += np.outer(np.abs(Ctrain[i]), np.abs(Ctrain[i]))

Z_train *= 1/len(Ctrain)
print(Z_train)
```

    [[0.09403968 0.11342847 0.11443919 0.08022018 0.11041908 0.10061245
      0.03776868 0.07929061]
     [0.11342847 0.1772893  0.17837182 0.11881422 0.17201055 0.15747309
      0.05284141 0.11505695]
     [0.11443919 0.17837182 0.18674813 0.12177096 0.17508085 0.16288168
      0.05327093 0.12032412]
     [0.08022018 0.11881422 0.12177096 0.10021428 0.11710474 0.11107623
      0.03627804 0.07623142]
     [0.11041908 0.17201055 0.17508085 0.11710474 0.17365567 0.15717696
      0.0512283  0.1112635 ]
     [0.10061245 0.15747309 0.16288168 0.11107623 0.15717696 0.15136805
      0.04690002 0.10472527]
     [0.03776868 0.05284141 0.05327093 0.03627804 0.0512283  0.04690002
      0.02382305 0.03774223]
     [0.07929061 0.11505695 0.12032412 0.07623142 0.1112635  0.10472527
      0.03774223 0.09286184]]

```python
lambda_P1_temp, U_train = np.linalg.eigh(Z_train)
lambda_train = round_lambda(lambda_P1_temp)
print(lambda_train)
print(U_train)
```

    [0.00249992 0.00366071 0.00566582 0.98817355 0.01189256 0.01993744
     0.02346068 0.92597342]
    [[-0.01908266 -0.10728949  0.09923328  0.2770666   0.37310267  0.46527216
      -0.68053859  0.28756759]
     [ 0.69899499  0.06961987 -0.44261374 -0.18261394  0.26442004 -0.11875085
       0.07720354  0.43280965]
     [-0.43307213 -0.64715466 -0.33970307  0.10127081 -0.04834919 -0.24078409
       0.1023804   0.44405974]
     [-0.04540449  0.13002963 -0.17242185 -0.06056506 -0.49554992  0.72782953
       0.28459714  0.30221221]
     [-0.47655819  0.59840474  0.09469492 -0.0740207   0.39542259 -0.11473368
       0.22125313  0.42647635]
     [ 0.29721548 -0.20047704  0.74998691  0.23256838 -0.08768977 -0.08546069
       0.28817368  0.39606364]
     [-0.07575347 -0.19022164  0.2772552  -0.89716308 -0.03936614  0.03101793
      -0.23699     0.1327744 ]
     [ 0.01566788  0.33684065 -0.02829192  0.1072084  -0.61430518 -0.40034708
      -0.50126668  0.291688  ]]

```python
lambda_train = lambda_train / np.linalg.norm(lambda_train)
print(lambda_train)
print((sum(lambda_train**2)))
```

    [0.00184545 0.00270234 0.00418252 0.72947213 0.00877911 0.01471787
     0.01731873 0.68355584]
    1.0

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|000 \rangle$ obtained. This value will be proportional to the density estimated by the circuit.

```python
backend2 = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 8x8
Cpred_ones = []

for i in range(len(Ctest_ones)):

    qc = QuantumCircuit(6, 3)

    qc.initialize(Ctest_ones[i], [0, 1, 2])
    qc.initialize(lambda_train, [3, 4, 5])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.cnot(5, 2)
    qc.cnot(4, 1)
    qc.cnot(3, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Cpred_ones.append((counts['000'])/5000)

print(Cpred_ones)
```

    [0.6158, 0.743, 0.7676, 0.6516, 0.7004, 0.5738, 0.3952, 0.2466, 0.4458, 0.432, 0.392, 0.3572, 0.0706, 0.0992, 0.0232, 0.3182, 0.268, 0.2286, 0.2898, 0.2514, 0.2774, 0.2286, 0.2708, 0.2238, 0.3276, 0.4232, 0.3984, 0.1948, 0.1398, 0.257, 0.1746, 0.0412, 1.3336, 1.1856, 0.1764, 0.8602, 0.8536, 0.8698, 0.8702, 0.8246, 0.5788, 0.7808, 0.9938, 0.8766, 1.0566, 0.7624, 1.0942, 1.0146, 0.5646, 0.55, 0.513, 0.536, 0.6804, 0.7148, 0.9138, 0.8002, 0.6806, 0.6782, 0.5028, 0.8026, 0.6796, 0.5996, 0.5652, 0.836, 0.8854, 0.2854, 0.5158, 0.5414, 0.5782, 0.568, 0.5482, 0.3956, 0.2572, 0.4906, 0.422, 0.581, 0.476, 0.4792, 0.689, 0.6464, 0.1642, 0.0874, 0.5432, 0.5586, 0.4826, 0.6322, 0.4702, 0.4466, 0.4402, 1.1046, 0.961, 1.109, 0.9828, 1.5206, 1.3786, 1.3758, 0.9738, 0.2846, 0.5484, 0.6896, 0.635, 0.8386, 1.007, 0.8082, 1.0682, 1.0578, 1.0168, 0.6166, 0.512, 0.5992, 0.5676, 0.5248, 0.5272, 0.2412, 0.5152, 0.3008, 0.3134, 0.4864, 0.4674, 0.5556, 0.5282, 0.5244, 0.6492, 0.4836, 0.4874, 0.4346, 0.4864, 0.5906, 0.547, 0.5414, 0.532, 0.5878, 0.4666, 0.478, 0.3906, 0.5, 0.2366, 0.266, 0.3246, 0.2432, 0.295, 0.4818, 0.6902, 0.8298, 0.7102, 0.4692, 0.468, 0.5246, 0.6188, 0.609, 0.5714, 0.5934, 0.4762, 0.4608, 0.5954, 0.6842, 0.4926, 0.5978, 0.344, 0.5184, 0.5158, 0.5702, 0.5536, 0.6774, 0.6054, 0.5726, 0.6298, 0.577, 0.6078, 0.5876, 0.4452, 0.5162, 0.5062, 0.4766, 0.6008, 0.5358, 0.5952, 0.5412, 0.4606, 0.628, 0.612, 0.4072, 0.6402, 0.7128, 0.6356, 0.6406, 0.2832, 0.3548, 0.3594, 0.2658, 0.3004, 0.2628, 0.2928, 0.4608, 0.5096, 0.453, 0.421, 0.505, 0.4952, 0.5464, 0.5784, 0.641, 0.4258, 0.3942, 0.6412, 0.5964]

```python
# Mixed state case
Cpred_zeros = []

for i in range(len(Ctest_zeros)):

    qc = QuantumCircuit(6, 3)

    qc.initialize(Ctest_zeros[i], [0, 1, 2])
    qc.initialize(lambda_train, [3, 4, 5])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.cnot(5, 2)
    qc.cnot(4, 1)
    qc.cnot(3, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)
    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Cpred_zeros.append((counts['000'])/5000)

print(Cpred_zeros)
```

    [1.2566, 0.8392, 0.823, 0.868, 0.692, 0.2758, 0.4006, 0.4426, 0.7928, 0.2826, 0.417, 1.2288, 0.1878, 0.6434, 0.674, 0.4046, 0.406, 0.3468, 0.2662, 0.338, 0.751, 0.8, 0.7308, 0.4616, 0.413, 0.3488, 0.7376, 0.796, 0.3672, 0.3688, 0.56, 0.504, 0.4114, 0.1402, 0.2516, 0.1738, 0.8018, 0.4604, 0.3956, 0.387, 0.631, 0.6238, 0.6196, 0.5906, 0.3412, 0.5356, 0.6682, 0.5734, 0.866, 0.6056, 0.5622, 0.8014, 1.2236, 0.483, 0.7098, 0.1732, 0.593, 0.2206, 0.8026, 0.4662, 0.7884, 0.3162, 0.4838, 0.667, 0.3612, 0.304, 0.7814, 0.4824, 0.5766, 0.617, 0.4032, 0.4908, 0.859, 0.4014, 0.732, 0.9402, 0.5302, 0.6352, 0.2528, 0.4224, 0.5464, 0.7854, 0.3786, 0.4862, 0.4392, 0.7524, 0.6824, 0.7852, 0.4416, 0.4924, 0.9034, 0.8302, 0.8522, 0.779, 1.1684, 0.3416, 0.991, 0.728, 0.7612, 0.421, 0.796, 0.622, 0.6702, 0.3504, 0.5222, 0.4788, 0.6306, 0.541, 0.7512, 1.072, 0.4098, 0.6652, 0.2902, 0.8412, 0.716, 0.9194, 1.1188, 0.6078, 0.3336, 0.8592, 0.3268, 0.3844, 0.8128, 0.651, 0.3386, 0.3244, 0.8476, 0.1844, 0.9834, 0.47, 0.5084, 0.7804, 0.941, 0.8726, 0.2764, 0.5554, 0.8082, 0.5262, 0.782, 0.173, 0.4454, 0.4662, 1.0048, 0.34, 0.3416, 0.3612, 0.6466, 0.3728, 0.6364, 0.43, 0.5076, 1.0482, 0.9994, 0.4236, 0.837, 0.7634, 0.4248, 0.7916, 0.3238, 0.6952, 0.654, 0.8998, 0.6354, 0.7554, 0.8496, 0.2586, 0.2462, 0.554, 0.8716, 0.847, 0.3906, 0.639, 0.6272, 0.3426, 0.8568, 0.3766, 0.7688, 0.0748, 0.3222, 0.4726, 0.3404, 0.3694, 0.3288, 0.8026, 0.9912, 0.5742, 0.4924, 0.4048, 0.7684, 0.7316, 0.2322, 0.8022, 0.4536, 0.427, 0.7134, 0.8976, 0.1846, 0.0776, 0.3808, 0.492, 0.7622, 0.8554, 0.458, 0.2518, 0.341, 0.9086, 0.4538, 0.786, 0.4452, 0.3556, 0.128, 0.358, 0.8476, 0.2148, 0.6864, 0.3766, 0.668, 0.2556, 0.5344, 0.3252, 0.0706, 0.4672, 1.0744, 0.7252, 0.513, 0.523, 0.2216, 0.8154, 0.827, 0.6128, 0.7896, 0.3808, 0.7502, 0.3516, 0.4088, 0.974, 0.487, 0.6552, 0.2586, 0.3506, 0.7134, 0.7586, 0.76, 0.6514, 0.4282, 0.8258, 1.1054, 0.4416, 0.1616, 0.5572, 0.5624, 0.212, 0.7282, 0.2582, 0.385, 0.3068, 0.6092, 0.1136, 0.782, 0.7936, 0.5532, 0.7154, 1.2868, 0.285, 0.7642, 0.5368, 0.9034, 0.2746, 0.7428, 0.3542, 0.649, 0.7568, 0.4344, 0.892, 0.7794, 0.3838, 0.3438, 0.3306, 0.6424, 0.5542, 0.609, 0.5278, 0.4236, 0.8388, 0.7854, 0.7806, 0.535, 0.8598, 0.6056, 0.2604, 0.2592, 0.66, 0.377, 1.355, 0.6988, 0.7994, 0.0758, 0.4752, 1.0494, 0.5984, 0.557, 0.6098, 0.3358, 0.3, 0.3032, 0.4736, 0.695, 0.357, 0.47, 0.677, 0.6488, 0.4604, 0.3214, 0.2918, 0.3866, 0.8804, 0.7666, 0.3526, 0.4118, 0.8016, 0.3596, 0.3, 0.6718, 0.3842, 0.3374, 0.4542, 0.8744, 0.5558, 0.5126, 0.5484, 0.2358, 0.3642, 0.6106, 0.9378, 0.8334, 0.5356, 0.5068, 0.3342, 0.7762, 1.011, 0.1506, 0.6428, 0.9128, 0.6064, 0.672, 0.5976, 0.39, 0.495, 0.4388, 1.0598, 0.7518, 0.3606, 0.1826, 0.6512, 0.356, 0.499, 0.714, 0.8394, 0.6186, 0.6018, 0.8024, 0.5624, 0.6592, 0.2024, 0.751, 0.2742, 0.4004, 0.3836, 0.3094, 0.3976, 0.469, 0.5454, 0.4086, 0.7724, 0.5878, 0.4174, 0.7222, 0.3508, 0.4278, 0.5416, 0.8752, 0.4926, 0.8854, 0.9106, 0.3964, 0.7756, 0.1524, 0.8564, 0.9606, 0.3534, 0.6174, 0.5262, 0.6528, 0.8068, 0.4174, 0.446, 0.2408, 0.3686, 0.2398, 0.6908, 1.2562, 0.5076, 0.6432, 0.7394, 0.433, 0.7122, 0.7886, 0.332, 0.9894, 0.8974, 0.889, 0.58]

Once we have obtained the predicted density values, we need to determine the best possible threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier.

```python
Cpred_val = np.concatenate((Cpred_ones[:100], Cpred_zeros[:100]))
print(Cpred_val.shape)

Cy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Cy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Cy_val, Cpred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Cpred_val > t).astype(int)
    metric = f1_score(Cy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.504

We have obtained that our best threshold is at $t = 0.504$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Cpr_zeros = (np.array(Cpred_zeros) > T).astype(int)

Cpr_ones = (np.array(Cpred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(106), np.zeros(312))),
        np.concatenate((Cpr_ones[100:], Cpr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.7627    0.4327    0.5521       312
             1.0     0.2656    0.6038    0.3689       106

        accuracy                         0.4761       418
       macro avg     0.5141    0.5182    0.4605       418
    weighted avg     0.6366    0.4761    0.5057       418

## Cardio 8x8 Pure State

Now, we'll build a new training density matrix of size 8x8 using the Pure State approach.

```python
phi_train = np.sum(np.abs(Ctrain), axis=0)
phi_train = phi_train / np.linalg.norm(phi_train)

U_train = create_U_train(phi_train)

print(U_train)
```

    [[ 0.28684681  0.36163466 -0.64907043  0.3039476   0.29823376 -0.3628901
       0.15672877 -0.16752971]
     [ 0.43304774 -0.19150853  0.0425077   0.01946241 -0.66638594 -0.35659966
      -0.20048597 -0.40271614]
     [ 0.44413513 -0.01120385 -0.3346563   0.02332315 -0.35547746  0.59271958
       0.24672536  0.38926279]
     [ 0.30137368  0.50622778  0.63860468  0.26581483  0.02231948  0.06976633
       0.39028257 -0.1294136 ]
     [ 0.42656204  0.29150598  0.0298679  -0.72831994  0.27817334  0.09661608
      -0.32529554 -0.09588955]
     [ 0.39638647 -0.61313144  0.11383773  0.26809529  0.49785862  0.24052065
      -0.04173887 -0.27323611]
     [ 0.13336378  0.21047524  0.11205656  0.46037306  0.02482021  0.01655667
      -0.74680723  0.39345019]
     [ 0.29196429 -0.26635886  0.17516306 -0.14753714  0.1194335  -0.5634755
       0.23825873  0.6346844 ]]

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|000 \rangle$ obtained.

```python
backend = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 8x8
Cpred_ones = []

for i in range(len(Ctest_ones)):

    qc = QuantumCircuit(3, 3)
    qc.initialize(Ctest_ones[i], [0, 1, 2])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Cpred_ones.append((counts['000'])/5000)
    except:
        Cpred_ones.append(0)

print(Cpred_ones)
```

    [0.9972, 1.0788, 1.6072, 1.3666, 1.4166, 0.9676, 0.7016, 0.5316, 0.9614, 0.788, 0.8074, 0.7514, 0.0936, 0.184, 0.0048, 0.6736, 0.5016, 0.516, 0.614, 0.522, 0.5276, 0.4564, 0.519, 0.4862, 0.5398, 0.9204, 0.8346, 0.2472, 0.2198, 0.5188, 0.3886, 0.097, 2.6466, 2.114, 0.3388, 1.528, 1.5558, 1.627, 1.7316, 1.468, 0.961, 1.6716, 2.1504, 1.5992, 2.1858, 1.132, 2.0886, 1.8576, 0.5808, 0.6922, 0.7006, 0.8884, 1.0064, 1.476, 1.2724, 1.5806, 1.3642, 1.271, 0.9146, 1.2752, 1.0596, 1.128, 0.7844, 1.2136, 1.4796, 0.5912, 0.8188, 1.1646, 1.1686, 0.8986, 1.1284, 0.5898, 0.3636, 0.958, 0.764, 1.2312, 0.9598, 0.9942, 1.1646, 1.2284, 0.2284, 0.1846, 0.9286, 0.9622, 0.9002, 1.296, 0.9326, 0.9196, 0.9038, 1.9562, 1.5008, 1.647, 1.632, 2.831, 2.5038, 2.5202, 1.4688, 0.6234, 1.1932, 1.478, 1.0878, 1.7112, 1.8224, 1.3848, 2.2418, 2.0818, 2.0494, 1.3278, 0.5314, 0.712, 1.0108, 1.0268, 0.9248, 0.2974, 0.9328, 0.4178, 0.4338, 0.8582, 0.86, 1.1726, 0.3538, 0.3598, 0.4358, 0.67, 1.068, 0.9186, 0.9238, 0.9172, 0.9374, 1.0676, 0.8778, 1.113, 0.9098, 0.9856, 0.8238, 1.08, 0.4974, 0.5568, 0.5678, 0.5114, 0.479, 0.9854, 1.0278, 1.4844, 1.088, 1.0102, 1.0016, 1.0012, 1.0426, 0.9572, 1.1022, 0.9902, 1.0014, 0.9398, 1.2236, 1.1598, 1.064, 0.9924, 0.6866, 0.9618, 0.973, 1.0292, 1.1696, 1.0052, 1.3002, 1.1082, 1.2372, 0.9526, 1.0494, 1.1214, 0.7726, 0.8892, 0.8332, 1.0516, 1.131, 0.9908, 1.1398, 1.0302, 0.884, 0.9984, 0.9562, 0.9046, 1.0494, 1.243, 0.9392, 1.4234, 0.5516, 0.7022, 0.7122, 0.592, 0.5672, 0.5224, 0.6034, 0.7304, 0.8664, 0.9696, 0.7472, 1.023, 1.0146, 1.1114, 0.9542, 0.9542, 0.6338, 0.7766, 1.3034, 1.1794]

```python
# Pure state case
Cpred_zeros = []

for i in range(len(Ctest_zeros)):

    qc = QuantumCircuit(3, 3)
    qc.initialize(Ctest_zeros[i], [0, 1, 2])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Cpred_zeros.append((counts['000'])/5000)
    except:
        Cpred_zeros.append(0)

print(Cpred_zeros)
```

    [1.9668, 1.5754, 1.1734, 1.5554, 1.0302, 0.4776, 0.8382, 0.0148, 1.6616, 0.5102, 0.222, 2.0816, 0.2726, 0.9778, 1.1416, 0.0964, 0.0302, 0.5978, 0.0792, 0.7052, 1.4324, 1.0488, 1.2854, 0.901, 0.776, 0.6954, 1.4032, 1.3468, 0.4798, 0.7234, 1.0234, 0.952, 0.0068, 0.0656, 0.4422, 0.1028, 1.4892, 0.51, 0.6576, 0.7458, 1.1822, 0.9592, 1.1216, 1.0438, 0.5476, 1.132, 1.0804, 1.0708, 1.3552, 1.0736, 1.0506, 1.4146, 1.4974, 0.9296, 1.3532, 0.172, 1.105, 0.3474, 1.526, 0.9158, 1.3762, 0.5742, 1.0202, 1.0788, 0.1654, 0.0868, 1.5812, 0.972, 1.0764, 1.1818, 0.2366, 1.0306, 1.6814, 0.7872, 1.3044, 1.5446, 1.1526, 1.2164, 0.4124, 0.8394, 1.0416, 1.0838, 0.8034, 0.8376, 0.53, 1.3444, 1.3006, 1.3008, 0.876, 0.9544, 1.9064, 1.6878, 1.691, 1.4648, 1.7734, 0.6882, 1.8432, 1.1332, 1.4366, 0.8836, 0.9822, 1.285, 1.085, 0.6948, 0.931, 0.3902, 1.2404, 1.0554, 1.1688, 2.22, 0.0062, 1.1246, 0.5546, 1.2656, 1.4906, 1.9544, 1.7356, 1.0424, 0.3922, 1.6586, 0.7158, 0.8556, 1.6272, 1.3982, 0.2464, 0.6006, 1.7632, 0.0074, 1.9582, 0.889, 1.0736, 1.365, 1.9584, 1.6252, 0.4106, 0.6014, 1.3826, 0.512, 0.9616, 0.0004, 0.5252, 1.0072, 1.9448, 0.0724, 0.5974, 0.7428, 1.1614, 0.0398, 1.2352, 0.1408, 0.9544, 2.092, 2.1224, 0.6948, 1.6744, 1.5202, 0.833, 1.4254, 0.5178, 1.1952, 1.2486, 1.4852, 1.1636, 1.4892, 1.642, 0.484, 0.4798, 1.1692, 1.8624, 1.6892, 0.8048, 1.0536, 1.2184, 0.3318, 1.4992, 0.7668, 1.482, 0.0638, 0.6436, 0.6372, 0.6584, 0.2722, 0.6614, 1.6714, 1.9968, 1.239, 0.9, 0.8396, 1.0956, 1.3752, 0.4798, 1.3086, 0.0002, 0.5312, 1.5518, 1.1352, 0.281, 0.0624, 0.7856, 0.845, 1.5678, 1.6044, 0.0796, 0.373, 0.571, 1.5914, 0.535, 1.6286, 0.0146, 0.6108, 0.0936, 0.6556, 1.6504, 0.4688, 1.0664, 0.289, 1.1576, 0.5552, 0.9602, 0.6102, 0.1492, 0.9652, 2.2596, 1.426, 1.0718, 0.661, 0.039, 1.6692, 1.3994, 1.0148, 1.4412, 0.2754, 1.0042, 0.603, 0.0088, 1.4428, 0.569, 1.0342, 0.5894, 0.7224, 1.4658, 1.5772, 1.23, 1.3104, 0.7808, 1.2942, 2.2472, 0.935, 0.0486, 0.7588, 1.0054, 0.4578, 1.09, 0.233, 0.0042, 0.1038, 1.0596, 0.0262, 1.545, 1.6072, 1.0454, 1.0218, 1.9656, 0.1828, 0.6908, 0.8476, 1.6356, 0.139, 1.0926, 0.7516, 1.3734, 1.4588, 0.8648, 1.6214, 1.3306, 0.7652, 0.6098, 0.224, 1.1394, 0.901, 1.0946, 0.7586, 0.7864, 1.3926, 1.5264, 1.6694, 0.97, 1.4568, 1.0454, 0.0188, 0.1936, 1.3096, 0.4282, 1.8996, 1.4628, 1.5616, 0.1316, 0.9652, 2.1492, 1.247, 0.966, 1.3394, 0.5484, 0.31, 0.5976, 1.0012, 1.2876, 0.3944, 0.7664, 1.2812, 1.1862, 0.956, 0.629, 0.5334, 0.7408, 1.7868, 1.47, 0.0108, 0.0764, 1.5648, 0.4886, 0.3136, 1.1036, 0.6412, 0.6106, 0.9226, 1.8192, 1.0446, 1.04, 0.988, 0.266, 0.5478, 1.1944, 2.0062, 1.7438, 1.03, 0.803, 0.039, 1.6002, 2.1148, 0.0328, 1.13, 1.6508, 1.198, 1.1824, 1.1124, 0.0484, 0.9384, 0.0056, 2.1648, 1.131, 0.088, 0.1568, 1.3282, 0.6656, 0.0816, 1.3004, 1.7704, 1.3434, 1.0882, 1.6248, 1.028, 1.1628, 0.2606, 1.3812, 0.5218, 0.0036, 0.6882, 0.6344, 0.5224, 0.682, 0.961, 0.8598, 0.8678, 1.1548, 0.0006, 1.3094, 0.7386, 0.8896, 1.072, 1.2642, 0.9868, 1.7108, 1.5204, 0.415, 1.2326, 0.1842, 1.6628, 1.885, 0.4608, 1.1304, 0.8816, 1.2876, 1.7078, 0.9298, 0.9054, 0.0976, 0.7374, 0.2314, 1.2976, 1.6164, 0.599, 1.296, 1.3172, 0.9036, 1.4524, 1.649, 0.6514, 1.9218, 1.2328, 1.4358, 1.2834]

Once we have obtained the predicted density values, we need to determine the best possible threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier.

```python
Cpred_val = np.concatenate((Cpred_ones[:100], Cpred_zeros[:100]))
print(Cpred_val.shape)

Cy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Cy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Cy_val, Cpred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Cpred_val > t).astype(int)
    metric = f1_score(Cy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    1.128

We have obtained that our best threshold is at $t = 1.128$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Cpr_zeros = (np.array(Cpred_zeros) > T).astype(int)

Cpr_ones = (np.array(Cpred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(106), np.zeros(312))),
        np.concatenate((Cpr_ones[100:], Cpr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.6815    0.5897    0.6323       312
             1.0     0.1351    0.1887    0.1575       106

        accuracy                         0.4880       418
       macro avg     0.4083    0.3892    0.3949       418
    weighted avg     0.5429    0.4880    0.5119       418

## SpamBase 4x4 Mixed State

Now, we will work with the SpamBase dataset.

We will do the same work done previously, using both Pure and Mixed States and with both 4x4 and 8x8 density matrices.

Now we transform our data samples into arrays of length 4 and splitting them into training and test samples.

```python
# Create the RandomFourierFeature map
dim = 4
rm_state = 2
sigma = 1  #dataset dependant
gamma = 1 / (2 * sigma**2)

feature_map_fourier = RBFSampler(gamma=gamma, n_components=dim, random_state=rm_state)
X_feat_train = feature_map_fourier.fit(spam_X)

rffmap = QFeatureMap_rff(rbf_sampler=feature_map_fourier)

Srff = rffmap.batch2wf(spam_X)

print(Srff.shape)
```

    (3485, 4)

```python
Sones = []
Strain = []
Stest_zeros = []
Stest_ones = []

for i in range(Srff.shape[0]):
    if (spam_y[i] == 1):
        Sones.append(Srff[i] / np.linalg.norm(Srff[i]))
    elif (spam_y[i] == 0):
        Stest_zeros.append(Srff[i] / np.linalg.norm(Srff[i]))

Stest_ones = Sones[:len(Stest_zeros)//2]
Strain = Sones[len(Stest_zeros)//2:]

print(len(Strain))
print(len(Stest_zeros))
print(len(Stest_ones))
```

    2440
    697
    348

```python
print(Strain[0])
print(sum(Strain[0]**2))
```

    [ 0.35145319 -0.48431271 -0.78982361  0.13453817]
    1.0

We build our training density matrix with mixed states:

```python
Z_train = np.outer(np.abs(Strain[0]), np.abs(Strain[0]))
for i in range(1, len(Strain)):
    Z_train += np.outer(np.abs(Strain[i]), np.abs(Strain[i]))

Z_train *= 1/len(Strain)
print(Z_train)
```

    [[0.16023957 0.0855305  0.2371648  0.12718549]
     [0.0855305  0.08410164 0.15276087 0.08057598]
     [0.2371648  0.15276087 0.54511706 0.26604072]
     [0.12718549 0.08057598 0.26604072 0.21054174]]

```python
lambda_P1_temp, U_train = np.linalg.eigh(Z_train)
lambda_train = round_lambda(lambda_P1_temp)
print(lambda_train)
print(U_train)
```

    [0.02850307 0.05355448 0.06531519 0.85262726]
    [[-5.62804417e-01  6.84628757e-01 -2.68617322e-01 -3.77331933e-01]
     [ 8.26111187e-01  4.61515842e-01 -2.13519697e-01 -2.42801922e-01]
     [-3.94849885e-04 -5.47471817e-01 -2.93903994e-01 -7.83514452e-01]
     [ 2.81307491e-02  1.36239504e-01  8.92117994e-01 -4.29852234e-01]]

```python
lambda_train = lambda_train / np.linalg.norm(lambda_train)
print(lambda_train)
print((sum(lambda_train**2)))
```

    [0.03324847 0.06247062 0.07618934 0.99457888]
    1.0

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|00 \rangle$ obtained. This value will be proportional to the density estimated by the circuit.

```python
backend2 = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 4x4
Spred_ones = []

for i in range(len(Stest_ones)):

    qc = QuantumCircuit(4, 2)

    qc.initialize(Stest_ones[i], [0, 1])
    qc.initialize(lambda_train, [2, 3])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.cnot(3, 1)
    qc.cnot(2, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Spred_ones.append((counts['00'])/5000)

print(Spred_ones)
```

    [0.9764, 0.0214, 0.019, 0.9304, 0.735, 0.159, 0.4852, 0.4918, 0.7758, 0.5666, 0.1716, 0.9814, 2.4438, 0.5458, 0.3842, 1.1634, 2.5518, 0.1292, 1.6398, 0.028, 0.542, 0.0354, 0.8642, 1.4266, 1.5202, 0.2392, 0.8394, 0.6114, 0.7472, 0.0152, 0.131, 1.0214, 1.0054, 1.4484, 1.0926, 0.2736, 0.338, 1.7228, 0.67, 1.9012, 0.6558, 1.7678, 0.4706, 0.1602, 1.677, 0.3912, 1.0796, 0.1054, 1.0462, 1.4538, 1.3378, 1.7568, 1.2064, 0.6558, 1.713, 0.6394, 2.0798, 0.8506, 0.2676, 0.9722, 0.1546, 0.4394, 0.4306, 0.4436, 0.296, 0.3708, 0.6706, 0.0624, 2.025, 0.6492, 0.967, 0.0234, 0.2838, 0.5844, 0.7006, 0.0456, 0.5172, 0.8418, 0.4338, 1.0728, 1.182, 1.07, 0.3194, 0.767, 0.8324, 0.5714, 1.0416, 0.8434, 0.736, 1.06, 0.263, 1.7964, 1.3652, 1.1164, 0.083, 0.5552, 1.579, 0.6998, 1.526, 1.5936, 1.0404, 1.3176, 1.1704, 1.0238, 2.1232, 0.3124, 0.4736, 1.3384, 0.3258, 0.3482, 0.1952, 1.1976, 0.5344, 0.6128, 1.7738, 2.2356, 0.7102, 1.7312, 1.1268, 0.0436, 1.1164, 1.31, 0.5602, 1.2642, 1.745, 0.6834, 0.4036, 1.1582, 0.7006, 0.4982, 2.4386, 0.5636, 2.5492, 0.5078, 0.5662, 0.6384, 0.9124, 0.9446, 0.3488, 0.7118, 0.0862, 0.5024, 0.942, 0.3276, 0.0958, 0.3588, 0.8748, 0.7782, 0.5736, 2.2928, 0.8402, 0.9062, 0.0736, 0.355, 0.7582, 0.8672, 0.6278, 0.0402, 1.0134, 0.65, 0.8756, 0.7028, 0.6552, 0.1364, 2.4772, 0.329, 0.8864, 2.6026, 0.9024, 0.4672, 0.6804, 0.732, 0.9132, 0.3098, 0.2884, 0.942, 0.9478, 0.6656, 0.7548, 2.3272, 1.6174, 1.3198, 0.2862, 0.7836, 0.0974, 0.381, 0.9396, 1.52, 0.9852, 1.017, 0.599, 1.5026, 0.7042, 0.7302, 0.1356, 0.5104, 0.9018, 1.1978, 0.144, 0.2714, 0.918, 0.04, 1.9056, 0.678, 0.63, 0.2732, 1.4138, 0.052, 1.202, 0.6102, 0.9186, 0.562, 0.713, 0.8178, 2.815, 1.5776, 0.1252, 2.341, 0.2442, 1.0244, 2.255, 0.8096, 1.4394, 0.7258, 0.1974, 1.3602, 0.4338, 1.4854, 0.026, 0.9172, 2.09, 0.579, 2.127, 0.0054, 1.3688, 1.0256, 0.5474, 0.8552, 0.6414, 0.7808, 1.751, 1.3146, 0.1714, 0.2906, 1.729, 1.2918, 0.1862, 0.5132, 0.4364, 1.6604, 0.6816, 0.0152, 0.0738, 0.1734, 0.6306, 0.7098, 1.1454, 0.6528, 0.873, 0.9734, 0.3622, 0.7198, 3.4896, 0.1304, 0.3378, 1.0064, 1.9978, 0.9698, 1.0934, 0.399, 0.016, 0.986, 1.9002, 0.7026, 0.9206, 1.4218, 0.094, 0.0928, 0.584, 1.6022, 0.6838, 0.6828, 0.9034, 0.7676, 0.5012, 0.7348, 0.922, 0.2494, 0.9498, 0.6712, 0.3836, 2.0034, 0.3414, 0.8828, 0.8856, 2.072, 2.113, 0.9026, 0.1904, 0.2572, 1.018, 0.671, 0.6534, 0.2332, 0.8114, 1.0484, 0.894, 0.956, 0.8126, 1.2856, 1.3256, 2.1402, 0.1842, 0.9258, 0.0906, 0.2432, 0.0998, 0.9202, 2.0838, 0.7766, 1.4866, 0.285, 0.8894, 0.9338, 0.0106, 0.4918, 0.1708, 1.8026, 0.8846, 1.1008, 0.4518, 0.118, 0.6932, 0.6814, 0.3404, 0.0958, 1.1312, 0.0794, 1.6008, 0.4416, 0.0156, 0.4562, 1.7428, 0.0172, 0.8616, 0.88, 1.3046, 0.2586]

```python
# Mixed state case
Spred_zeros = []

for i in range(len(Stest_zeros)):

    qc = QuantumCircuit(4, 2)

    qc.initialize(Stest_zeros[i], [0, 1])
    qc.initialize(lambda_train, [2, 3])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.cnot(3, 1)
    qc.cnot(2, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Spred_zeros.append((counts['00'])/5000)

print(Spred_zeros)
```

    [0.2156, 0.3606, 0.0436, 0.1496, 0.2066, 0.2036, 0.0158, 3.1044, 0.1336, 0.3454, 0.3726, 0.3356, 0.2196, 0.4036, 0.0176, 0.7834, 0.0586, 0.5296, 0.44, 0.0194, 0.4462, 0.0468, 0.0192, 0.1288, 0.6674, 0.0754, 0.7868, 0.5258, 0.0568, 0.064, 0.8208, 0.2622, 0.0216, 0.3928, 0.4178, 0.291, 0.3442, 0.6596, 0.0646, 0.0186, 1.8216, 0.7712, 0.0188, 0.0132, 1.0964, 0.392, 1.0282, 1.3628, 0.0494, 0.0918, 0.0474, 0.0542, 0.1338, 0.7372, 0.4646, 0.4912, 0.0204, 1.3506, 0.1338, 1.1238, 0.0084, 0.3286, 0.0878, 0.0334, 0.5476, 0.3642, 0.9862, 0.2754, 0.1064, 0.083, 0.2706, 0.3434, 0.932, 0.4802, 0.8014, 0.3824, 0.5466, 0.6966, 0.9994, 1.233, 0.885, 0.932, 0.0476, 0.5266, 0.1754, 0.07, 0.1778, 0.0358, 0.1724, 0.42, 0.034, 0.0142, 0.3766, 0.0072, 0.1166, 0.026, 0.5254, 0.0242, 0.9968, 1.101, 0.077, 0.1932, 0.2958, 0.3598, 0.3786, 0.0328, 0.068, 0.4258, 1.2466, 0.5896, 0.4102, 0.4264, 0.0924, 0.0162, 0.6212, 0.047, 0.0212, 0.0316, 0.0086, 0.1494, 0.5156, 0.3574, 0.3154, 0.0262, 0.102, 0.3564, 0.3788, 0.0238, 0.1832, 0.1344, 0.157, 0.5126, 0.0474, 0.033, 0.4604, 0.2472, 0.4806, 0.4198, 0.132, 0.0696, 0.0334, 0.105, 0.9998, 0.9804, 0.6642, 0.0308, 0.2958, 0.2872, 0.1138, 0.7438, 0.066, 0.3818, 0.0762, 0.2436, 0.0914, 0.049, 0.2932, 0.0314, 1.638, 0.9062, 0.2984, 1.5118, 0.202, 0.0202, 0.3316, 0.5566, 0.5676, 0.0602, 0.235, 0.0054, 0.1536, 0.6838, 0.8664, 0.962, 0.1014, 0.0716, 0.6018, 0.0082, 0.1248, 0.645, 0.4396, 0.2804, 0.0974, 1.1742, 0.3054, 0.3822, 0.087, 0.108, 0.113, 0.0194, 0.3366, 0.5904, 0.0056, 0.286, 0.009, 0.049, 0.3088, 0.0244, 0.203, 0.2668, 0.027, 0.98, 0.2848, 0.0998, 0.2466, 0.2196, 0.118, 0.1022, 0.6776, 0.0844, 0.7462, 0.2166, 0.0206, 0.1306, 0.0688, 0.0424, 0.0076, 0.2778, 0.2186, 0.1326, 0.3372, 0.0332, 0.9, 0.3582, 0.4224, 0.2128, 0.3252, 0.3642, 0.3056, 0.063, 0.0722, 0.4688, 0.1008, 0.2708, 0.1156, 0.1742, 0.0538, 0.2748, 0.277, 0.2436, 0.0276, 0.4358, 0.069, 0.6324, 0.6612, 0.1412, 0.3642, 0.4874, 0.3866, 0.491, 0.319, 0.1388, 0.0298, 0.6326, 0.4836, 0.167, 0.1772, 0.0222, 0.521, 0.209, 0.651, 0.5662, 0.1866, 0.1008, 0.0144, 1.8934, 0.7526, 0.1264, 0.7276, 0.1154, 0.0068, 0.1502, 0.4794, 0.6578, 0.54, 0.0666, 0.5816, 0.4332, 0.0202, 0.414, 0.7006, 0.7174, 0.617, 0.0426, 0.6668, 0.5602, 0.1822, 0.0224, 0.1636, 0.7072, 0.3788, 0.0312, 0.1272, 0.0866, 0.6892, 0.7356, 0.0128, 0.1324, 0.3924, 0.0068, 0.8224, 0.504, 0.0264, 0.0172, 0.2986, 0.1826, 0.304, 0.0966, 1.0188, 0.3628, 0.393, 0.0576, 0.1, 0.6124, 0.3962, 0.1368, 0.2126, 0.1106, 0.1038, 0.5604, 0.7682, 0.0536, 1.0064, 0.1864, 0.1864, 0.325, 0.01, 0.475, 0.0112, 0.1954, 0.027, 0.0326, 0.2508, 0.4338, 0.457, 0.217, 0.7734, 0.466, 0.0472, 0.2888, 0.6048, 0.9792, 0.2098, 0.3958, 0.1528, 0.2694, 0.268, 0.7556, 0.2738, 2.225, 0.2516, 0.3812, 1.5512, 0.3668, 0.339, 0.2726, 0.7724, 0.6194, 0.2214, 0.776, 0.4344, 0.2006, 0.6826, 0.1342, 0.254, 0.5472, 0.7026, 0.0156, 1.1416, 0.0084, 0.1848, 0.0654, 0.0166, 0.0204, 0.7232, 0.3972, 0.213, 1.0368, 0.4212, 0.0734, 0.2316, 0.5944, 1.8152, 0.6028, 0.0806, 0.0354, 0.5166, 0.036, 0.0434, 0.3112, 0.0316, 0.262, 0.9878, 0.2112, 0.1378, 0.3436, 0.2868, 0.0698, 0.5784, 0.49, 0.4426, 0.1964, 0.0182, 0.0172, 0.2706, 0.131, 0.0204, 0.4258, 0.7348, 0.1004, 0.7334, 0.1942, 0.5226, 0.3538, 0.0216, 0.437, 0.8664, 0.0196, 0.0126, 0.079, 0.3446, 0.0948, 0.7624, 0.1486, 0.1576, 0.779, 0.0576, 1.1208, 0.9362, 0.2092, 0.7548, 0.1022, 0.6714, 1.0154, 0.6598, 0.2168, 0.062, 0.173, 1.6516, 0.3852, 0.5098, 0.4562, 0.9708, 0.1932, 0.013, 0.0482, 0.6218, 0.8404, 0.511, 0.043, 0.0468, 0.1146, 0.377, 0.6298, 0.5364, 0.0516, 0.092, 0.0332, 0.106, 0.1556, 0.3672, 0.7546, 0.4118, 0.1766, 0.4782, 0.3898, 0.5346, 0.4898, 0.5632, 0.3582, 0.0208, 0.3524, 0.4036, 0.357, 0.0792, 0.799, 0.1844, 1.0774, 0.0766, 1.817, 0.2038, 0.1592, 0.333, 0.2772, 0.0496, 0.1576, 0.993, 0.0594, 0.0946, 1.0996, 0.0766, 0.2286, 0.069, 0.4184, 0.054, 0.4232, 0.4924, 0.0446, 0.0202, 0.0604, 0.0282, 0.8476, 0.382, 0.0932, 0.0144, 0.3232, 0.2962, 0.3282, 0.1682, 0.9428, 0.0188, 0.891, 0.6346, 0.26, 0.6504, 0.227, 0.4316, 0.7552, 0.1946, 0.5492, 0.5926, 0.0374, 0.2354, 0.772, 0.9212, 0.062, 0.013, 0.5914, 0.488, 0.7624, 0.7648, 1.3108, 0.2238, 0.6872, 0.3454, 0.0112, 0.2136, 0.6058, 0.0118, 0.7064, 0.0694, 0.496, 0.1062, 0.1186, 0.6946, 0.388, 0.7528, 0.4506, 0.2032, 0.0478, 0.013, 1.0378, 0.4174, 0.0192, 0.8634, 0.606, 0.0534, 0.9192, 0.729, 0.4068, 0.0318, 0.04, 0.1012, 0.1156, 0.0496, 0.445, 0.5604, 0.0244, 0.6218, 0.191, 1.0666, 0.0304, 0.0906, 0.0944, 0.2092, 0.2112, 0.2762, 0.0438, 0.2248, 0.023, 0.1106, 0.1164, 0.7594, 0.0402, 0.146, 0.0644, 0.0142, 0.203, 0.937, 0.3722, 0.5886, 0.661, 0.322, 0.3916, 0.1832, 0.0196, 0.053, 0.3874, 0.642, 0.0116, 0.2962, 0.0312, 0.8524, 0.0822, 0.1666, 0.545, 0.2176, 0.3676, 0.2268, 0.8184, 0.1754, 0.521, 0.3766, 0.099, 0.466, 0.1968, 0.0184, 0.8654, 0.378, 0.2864, 0.563, 0.3496, 0.1696, 0.2038, 0.5134, 0.6188, 0.0336, 0.0786, 0.2378, 0.2626, 0.1802, 0.784, 1.1284, 0.0182, 0.2242, 0.6056, 0.007, 0.3762, 0.7028, 1.402, 0.844, 0.7072, 0.5876, 0.1394, 0.48, 0.2458, 0.7028, 0.0344, 0.1034, 0.476, 0.485, 0.3972, 0.1888, 0.2754, 0.0198, 0.2016, 0.4054, 0.029, 0.6174, 0.618, 0.425, 1.0652, 0.594, 0.4958, 0.165, 0.3552, 1.419, 1.1248, 0.4384, 0.0962, 0.764, 0.0262, 0.3228, 0.0096, 0.2332, 1.6268, 0.801, 0.4854, 0.2436, 0.6838, 0.6534, 0.4814, 0.105, 0.7158, 0.0126, 0.2228, 0.0184, 0.1922, 0.2906, 0.6778, 0.3006, 0.5656, 0.5986, 0.3702, 0.0322, 0.0174, 0.017]

We need to determine a threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier.

```python
Spred_val = np.concatenate((Spred_ones[:100], Spred_zeros[:100]))
print(Spred_val.shape)

Sy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Sy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Sy_val, Spred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Spred_val > t).astype(int)
    metric = f1_score(Sy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.542

We have obtained that our best threshold is at $t = 0.542$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Spr_zeros = (np.array(Spred_zeros) > T).astype(int)

Spr_ones = (np.array(Spred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(248), np.zeros(597))),
        np.concatenate((Spr_ones[100:], Spr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.8476    0.7454    0.7932       597
             1.0     0.5250    0.6774    0.5915       248

        accuracy                         0.7254       845
       macro avg     0.6863    0.7114    0.6924       845
    weighted avg     0.7529    0.7254    0.7340       845

## SpamBase 4x4 Pure State

We build our training density matrix using Pure States.

```python
phi_train = np.sum(np.abs(Strain), axis=0)
phi_train = phi_train / np.linalg.norm(phi_train)

U_train = create_U_train(phi_train)

print(U_train)
```

    [[ 0.37950892  0.47081098  0.63630529 -0.47898392]
     [ 0.24651561 -0.12507789 -0.6121573  -0.74084345]
     [ 0.77958893 -0.56123548  0.13372207  0.24366829]
     [ 0.43294818  0.66910932 -0.44999715  0.40292823]]

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|00 \rangle$ obtained.

```python
backend = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 4x4
Spred_ones = []

for i in range(len(Stest_ones)):

    qc = QuantumCircuit(2, 2)
    qc.initialize(Stest_ones[i], [0, 1])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Spred_ones.append((counts['00'])/5000)
    except:
        Spred_ones.append(0)

print(Spred_ones)
```

    [0.991, 0.0094, 0.0022, 0.916, 0.7104, 0.1416, 0.4866, 0.4696, 0.8048, 0.5458, 0.138, 0.9858, 2.4346, 0.543, 0.3864, 1.1886, 2.581, 0.112, 1.6326, 0.0204, 0.5448, 0.021, 0.8048, 1.4324, 1.5402, 0.2096, 0.8318, 0.6118, 0.7392, 0.0012, 0.1092, 1.016, 1.011, 1.4412, 1.0946, 0.269, 0.3152, 1.7308, 0.6536, 1.937, 0.6486, 1.751, 0.451, 0.1568, 1.6696, 0.406, 1.0796, 0.091, 1.0418, 1.4466, 1.3448, 1.805, 1.23, 0.6428, 1.7196, 0.6314, 2.059, 0.8502, 0.2448, 0.9524, 0.1436, 0.4496, 0.4296, 0.4412, 0.2778, 0.3632, 0.6756, 0.0486, 2.0302, 0.6384, 0.9434, 0.0086, 0.2664, 0.5624, 0.7064, 0.0372, 0.5082, 0.8278, 0.4392, 1.0662, 1.1432, 1.0602, 0.2974, 0.7516, 0.7966, 0.5804, 1.069, 0.8214, 0.6772, 1.0696, 0.2428, 1.7422, 1.3192, 1.0834, 0.0614, 0.5364, 1.5728, 0.6894, 1.5338, 1.5774, 1.0842, 1.2854, 1.1794, 1.0286, 2.1188, 0.2938, 0.4496, 1.3242, 0.2938, 0.3458, 0.1844, 1.1556, 0.5318, 0.5716, 1.7914, 2.2006, 0.6592, 1.7126, 1.1364, 0.0302, 1.0844, 1.2914, 0.5622, 1.278, 1.7476, 0.6552, 0.377, 1.1386, 0.6536, 0.4846, 2.43, 0.5384, 2.5622, 0.5022, 0.5536, 0.6198, 0.9432, 0.931, 0.3312, 0.6808, 0.0818, 0.4922, 0.948, 0.3086, 0.077, 0.3394, 0.8604, 0.763, 0.5362, 2.2836, 0.8196, 0.8898, 0.0536, 0.3444, 0.7524, 0.858, 0.6042, 0.0416, 0.9952, 0.6148, 0.858, 0.689, 0.643, 0.1384, 2.4698, 0.3074, 0.9044, 2.606, 0.8926, 0.4366, 0.6598, 0.7248, 0.9276, 0.2744, 0.2646, 0.9004, 0.9524, 0.672, 0.7264, 2.3324, 1.5968, 1.3402, 0.2904, 0.7662, 0.077, 0.3774, 0.91, 1.5356, 0.9772, 1.011, 0.5764, 1.5458, 0.6818, 0.7164, 0.1092, 0.5086, 0.8968, 1.1552, 0.1334, 0.2772, 0.9348, 0.025, 1.9036, 0.689, 0.6224, 0.2558, 1.4212, 0.0392, 1.1766, 0.5928, 0.942, 0.5452, 0.6878, 0.8388, 2.8424, 1.6088, 0.1136, 2.3256, 0.2308, 1.0154, 2.272, 0.8352, 1.4524, 0.7134, 0.1868, 1.354, 0.4242, 1.4804, 0.0116, 0.9158, 2.102, 0.5514, 2.12, 0, 1.3468, 1.0416, 0.5194, 0.8266, 0.6386, 0.7966, 1.7682, 1.3428, 0.1566, 0.2808, 1.7074, 1.2766, 0.1674, 0.4952, 0.4336, 1.6294, 0.6566, 0.001, 0.0638, 0.1406, 0.6224, 0.7096, 1.1388, 0.637, 0.8808, 0.963, 0.3426, 0.693, 3.5508, 0.1208, 0.3208, 0.9916, 2.0044, 0.9546, 1.0792, 0.376, 0.005, 0.9838, 1.9076, 0.6862, 0.9138, 1.4484, 0.0774, 0.0852, 0.5542, 1.6226, 0.6764, 0.6556, 0.8816, 0.7508, 0.501, 0.7092, 0.9174, 0.2426, 0.9238, 0.6598, 0.356, 2.0132, 0.3118, 0.8728, 0.8804, 2.0612, 2.0868, 0.9034, 0.168, 0.2218, 1.0228, 0.6544, 0.6488, 0.2146, 0.7542, 1.0448, 0.8796, 0.9386, 0.8, 1.2494, 1.3304, 2.1298, 0.1634, 0.9408, 0.0708, 0.2316, 0.0938, 0.9312, 2.08, 0.7444, 1.5094, 0.2756, 0.8768, 0.9194, 0, 0.4964, 0.1606, 1.7966, 0.8688, 1.122, 0.4446, 0.1124, 0.68, 0.693, 0.3428, 0.0712, 1.1396, 0.07, 1.5778, 0.4064, 0.002, 0.4308, 1.7226, 0.0004, 0.8434, 0.8982, 1.2962, 0.2642]

```python
# Pure state case
Spred_zeros = []

for i in range(len(Stest_zeros)):

    qc = QuantumCircuit(2, 2)
    qc.initialize(Stest_zeros[i], [0, 1])
    qc.isometry(U_train.T, [], [0, 1]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Spred_zeros.append((counts['00'])/5000)
    except:
        Spred_zeros.append(0)

print(Spred_zeros)
```

    [0.1994, 0.3466, 0.039, 0.1376, 0.2048, 0.1986, 0, 3.1662, 0.1142, 0.3322, 0.3788, 0.3246, 0.2044, 0.3908, 0.0108, 0.7788, 0.0512, 0.5198, 0.4514, 0.0054, 0.4592, 0.0468, 0.0078, 0.1198, 0.6582, 0.0654, 0.7822, 0.5348, 0.0454, 0.0518, 0.8216, 0.2322, 0.0078, 0.383, 0.4154, 0.2672, 0.3358, 0.6744, 0.0556, 0.0108, 1.8162, 0.7796, 0.0116, 0.0024, 1.0976, 0.3788, 1.0236, 1.3672, 0.0376, 0.0674, 0.0382, 0.0462, 0.114, 0.7316, 0.4502, 0.4874, 0.0038, 1.3668, 0.1206, 1.1388, 0.0008, 0.3284, 0.0844, 0.0236, 0.5538, 0.348, 0.9748, 0.2622, 0.0938, 0.0684, 0.2568, 0.3398, 0.9408, 0.468, 0.7904, 0.3754, 0.5616, 0.6876, 1.0208, 1.2268, 0.8996, 0.9942, 0.0446, 0.4912, 0.1658, 0.0508, 0.1764, 0.025, 0.1712, 0.3882, 0.0278, 0.0058, 0.3816, 0, 0.1056, 0.0056, 0.505, 0.004, 0.9772, 1.1118, 0.0572, 0.19, 0.2826, 0.353, 0.3726, 0.0214, 0.0558, 0.4222, 1.244, 0.593, 0.4036, 0.4236, 0.0794, 0.0062, 0.6442, 0.0396, 0.0096, 0.0204, 0.0014, 0.1398, 0.5142, 0.3516, 0.2936, 0.011, 0.082, 0.3366, 0.3892, 0.0172, 0.1678, 0.11, 0.156, 0.5228, 0.0348, 0.0166, 0.4528, 0.2138, 0.4876, 0.4214, 0.1236, 0.0608, 0.0294, 0.0934, 1.0162, 0.9828, 0.6468, 0.0264, 0.269, 0.2732, 0.1018, 0.7224, 0.0486, 0.3818, 0.0602, 0.2412, 0.0786, 0.0296, 0.2772, 0.0224, 1.6624, 0.936, 0.303, 1.4934, 0.192, 0.0022, 0.3204, 0.5602, 0.5738, 0.053, 0.2222, 0.0008, 0.1304, 0.6752, 0.871, 0.9716, 0.1018, 0.0458, 0.5826, 0, 0.1088, 0.6188, 0.4432, 0.2616, 0.0786, 1.1834, 0.3118, 0.3774, 0.079, 0.1076, 0.0996, 0.01, 0.3336, 0.5962, 0.0014, 0.2756, 0.0032, 0.0404, 0.2958, 0.0178, 0.195, 0.2638, 0.0122, 0.9562, 0.274, 0.087, 0.2252, 0.2136, 0.1108, 0.0908, 0.6512, 0.0792, 0.7268, 0.2012, 0.0036, 0.127, 0.0526, 0.0264, 0.0002, 0.2826, 0.2026, 0.1158, 0.3414, 0.027, 0.876, 0.3304, 0.405, 0.1878, 0.313, 0.3562, 0.2798, 0.0442, 0.0686, 0.479, 0.0964, 0.2678, 0.1012, 0.165, 0.0506, 0.2772, 0.2614, 0.2314, 0.0078, 0.4104, 0.069, 0.6418, 0.6554, 0.131, 0.354, 0.4544, 0.403, 0.51, 0.2976, 0.1052, 0.028, 0.6248, 0.464, 0.1438, 0.1672, 0.0112, 0.5086, 0.2038, 0.662, 0.567, 0.1898, 0.0944, 0.006, 1.9194, 0.734, 0.1212, 0.7104, 0.0922, 0, 0.1288, 0.4802, 0.6464, 0.5038, 0.07, 0.5722, 0.4364, 0.0062, 0.4064, 0.6828, 0.703, 0.5994, 0.0236, 0.6536, 0.5528, 0.19, 0.0068, 0.1546, 0.7128, 0.3574, 0.023, 0.1038, 0.0792, 0.7334, 0.729, 0.0008, 0.1218, 0.3968, 0, 0.828, 0.467, 0.0182, 0.0076, 0.3074, 0.1704, 0.2976, 0.0898, 0.9956, 0.3612, 0.3788, 0.0556, 0.0876, 0.609, 0.4016, 0.134, 0.2014, 0.097, 0.092, 0.5764, 0.7472, 0.0574, 1.0314, 0.1854, 0.1608, 0.3444, 0, 0.4782, 0.0002, 0.1934, 0.0178, 0.0124, 0.2348, 0.42, 0.4336, 0.2218, 0.7776, 0.449, 0.034, 0.282, 0.5654, 0.979, 0.2062, 0.3878, 0.1406, 0.2848, 0.2508, 0.7604, 0.2902, 2.217, 0.2268, 0.3634, 1.5268, 0.3644, 0.3362, 0.2556, 0.7626, 0.6114, 0.218, 0.7666, 0.3956, 0.193, 0.6816, 0.1364, 0.2512, 0.5258, 0.7086, 0.0038, 1.1434, 0, 0.1632, 0.0456, 0.0038, 0.0026, 0.7204, 0.3982, 0.1844, 1.0382, 0.3948, 0.0606, 0.2326, 0.5784, 1.849, 0.598, 0.0688, 0.0258, 0.537, 0.0232, 0.0306, 0.3094, 0.0168, 0.2768, 1.0158, 0.1964, 0.1292, 0.366, 0.2822, 0.0536, 0.5686, 0.4816, 0.4302, 0.1798, 0.0074, 0, 0.2572, 0.114, 0, 0.4184, 0.7112, 0.107, 0.7376, 0.1862, 0.5482, 0.3348, 0.0084, 0.4254, 0.8562, 0.0114, 0.0034, 0.0586, 0.3378, 0.0804, 0.743, 0.1468, 0.1532, 0.7854, 0.0448, 1.111, 0.9512, 0.195, 0.7444, 0.0932, 0.687, 1.039, 0.6564, 0.1982, 0.0498, 0.1628, 1.6878, 0.3958, 0.4988, 0.4478, 0.9624, 0.1636, 0.001, 0.0426, 0.5962, 0.8272, 0.4804, 0.0274, 0.0384, 0.114, 0.3758, 0.6338, 0.4992, 0.0414, 0.0722, 0.0184, 0.0916, 0.1438, 0.3572, 0.7286, 0.409, 0.1762, 0.4732, 0.3938, 0.5288, 0.4962, 0.5376, 0.3504, 0.0082, 0.358, 0.3956, 0.3372, 0.072, 0.7862, 0.1644, 1.104, 0.0762, 1.8624, 0.2034, 0.1468, 0.355, 0.2826, 0.0408, 0.1442, 0.977, 0.0506, 0.088, 1.0714, 0.0722, 0.2254, 0.056, 0.4002, 0.0388, 0.4098, 0.469, 0.0322, 0.017, 0.05, 0.0172, 0.87, 0.376, 0.0856, 0, 0.3098, 0.2944, 0.3032, 0.148, 0.9212, 0.0036, 0.8988, 0.6496, 0.2444, 0.6354, 0.1864, 0.4462, 0.755, 0.191, 0.5374, 0.5904, 0.029, 0.2402, 0.7896, 0.9054, 0.0434, 0.008, 0.5778, 0.4782, 0.7558, 0.776, 1.301, 0.2184, 0.7186, 0.3328, 0.0066, 0.1934, 0.6226, 0.0012, 0.685, 0.0618, 0.5004, 0.099, 0.1036, 0.6744, 0.3812, 0.7404, 0.4586, 0.2044, 0.0244, 0.0004, 1.046, 0.413, 0.0056, 0.8932, 0.6148, 0.0346, 0.9264, 0.7208, 0.3714, 0.015, 0.0354, 0.088, 0.1104, 0.0392, 0.4464, 0.5586, 0.0082, 0.6124, 0.1902, 1.0614, 0.0098, 0.0816, 0.0736, 0.2062, 0.2072, 0.2904, 0.0272, 0.2108, 0.0016, 0.1034, 0.114, 0.7266, 0.034, 0.1464, 0.0566, 0.0036, 0.1994, 0.9388, 0.352, 0.5734, 0.6814, 0.2892, 0.3612, 0.182, 0.0086, 0.035, 0.361, 0.615, 0.0072, 0.2778, 0.0122, 0.8662, 0.0746, 0.162, 0.5224, 0.2042, 0.3486, 0.218, 0.798, 0.1648, 0.5182, 0.365, 0.0834, 0.4634, 0.1882, 0.0016, 0.8738, 0.3754, 0.2834, 0.577, 0.3526, 0.1542, 0.2036, 0.5284, 0.6168, 0.0174, 0.0726, 0.2404, 0.2412, 0.1902, 0.7956, 1.1502, 0.0076, 0.2036, 0.598, 0, 0.3646, 0.708, 1.4422, 0.8168, 0.7056, 0.5746, 0.1282, 0.474, 0.2436, 0.6896, 0.0156, 0.087, 0.4626, 0.5016, 0.3972, 0.175, 0.2632, 0.0102, 0.1924, 0.4072, 0.0114, 0.604, 0.6148, 0.405, 1.0408, 0.5826, 0.477, 0.1522, 0.3466, 1.4396, 1.1322, 0.4482, 0.081, 0.7884, 0.0154, 0.3234, 0.0002, 0.246, 1.6592, 0.8104, 0.4702, 0.2286, 0.6734, 0.645, 0.5036, 0.1054, 0.7114, 0.0038, 0.2184, 0.0078, 0.1752, 0.269, 0.6658, 0.2856, 0.5452, 0.6142, 0.3508, 0.0202, 0.0076, 0.01]

We need to determine a threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier.

```python
Spred_val = np.concatenate((Spred_ones[:100], Spred_zeros[:100]))
print(Spred_val.shape)

Sy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Sy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Sy_val, Spred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Spred_val > t).astype(int)
    metric = f1_score(Sy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.5198

We have obtained that our best threshold is at $t = 0.5198$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Spr_zeros = (np.array(Spred_zeros) > T).astype(int)

Spr_ones = (np.array(Spred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(248), np.zeros(597))),
        np.concatenate((Spr_ones[100:], Spr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.8462    0.7370    0.7878       597
             1.0     0.5169    0.6774    0.5864       248

        accuracy                         0.7195       845
       macro avg     0.6815    0.7072    0.6871       845
    weighted avg     0.7495    0.7195    0.7287       845

## SpamBase 8x8 Mixed State

We use a new RFF sampler to reduce the data to arrays of lenght 8. Then we split our data as before.

```python
# Create the RandomFourierFeature map
dim = 8
rm_state = 2
sigma = 1  #dataset dependant
gamma = 1 / (2 * sigma**2)

feature_map_fourier = RBFSampler(gamma=gamma, n_components=dim, random_state=rm_state)
X_feat_train = feature_map_fourier.fit(spam_X)

rffmap = QFeatureMap_rff(rbf_sampler=feature_map_fourier)

Srff = rffmap.batch2wf(spam_X)

print(Srff.shape)
```

    (3485, 8)

```python
Sones = []
Strain = []
Stest_zeros = []
Stest_ones = []

for i in range(Srff.shape[0]):
    if (spam_y[i] == 1):
        Sones.append(Srff[i] / np.linalg.norm(Srff[i]))
    elif (spam_y[i] == 0):
        Stest_zeros.append(Srff[i] / np.linalg.norm(Srff[i]))

Stest_ones = Sones[:len(Stest_zeros)//2]
Strain = Sones[len(Stest_zeros)//2:]

print(len(Strain))
print(len(Stest_zeros))
print(len(Stest_ones))
```

    2440
    697
    348

```python
print(Strain[0])
print(sum(Strain[0]**2))
```

    [-0.34027012 -0.40001974 -0.05008387 -0.25346561  0.53922711  0.19674345
      0.2569611  -0.51180502]
    1.0

We build our training density matrix using Mixed States.

```python
Z_train = np.outer(np.abs(Strain[0]), np.abs(Strain[0]))
for i in range(1, len(Strain)):
    Z_train += np.outer(np.abs(Strain[i]), np.abs(Strain[i]))

Z_train *= 1/len(Strain)
print(Z_train)
```

    [[0.06931959 0.08300347 0.04808967 0.04941927 0.09431502 0.07784292
      0.08019314 0.10130139]
     [0.08300347 0.15444103 0.07054571 0.07575444 0.15514609 0.13318578
      0.12967983 0.17259092]
     [0.04808967 0.07054571 0.05230964 0.042946   0.07962232 0.06815874
      0.06656479 0.08509469]
     [0.04941927 0.07575444 0.042946   0.06060161 0.08260872 0.07388889
      0.07581426 0.09218323]
     [0.09431502 0.15514609 0.07962232 0.08260872 0.17952542 0.14453548
      0.14385772 0.18738437]
     [0.07784292 0.13318578 0.06815874 0.07388889 0.14453548 0.13756255
      0.12806829 0.15965701]
     [0.08019314 0.12967983 0.06656479 0.07581426 0.14385772 0.12806829
      0.13534703 0.15830969]
     [0.10130139 0.17259092 0.08509469 0.09218323 0.18738437 0.15965701
      0.15830969 0.21089312]]

```python
lambda_P1_temp, U_train = np.linalg.eigh(Z_train)
lambda_train = round_lambda(lambda_P1_temp)
print(lambda_train)
print(U_train)
```

    [0.00549852 0.00671934 0.00996441 0.97781773 0.01365931 0.01958209
     0.02544983 0.90645963]
    [[ 0.00771802  0.15422668  0.42740079 -0.35756528 -0.30143132 -0.48259314
      -0.53267092  0.24111181]
     [-0.38476255 -0.19726171  0.43203748 -0.1121561   0.51696029 -0.16943225
       0.40003892  0.39727051]
     [ 0.07079513 -0.30775459 -0.04294226  0.73889489  0.23821596 -0.24075563
      -0.44276533  0.20424057]
     [-0.05922746  0.22554943 -0.22932215 -0.31234088  0.5380915   0.44017573
      -0.51343091  0.22037617]
     [-0.39890327  0.27235547 -0.64369821  0.03250565 -0.19428076 -0.32603283
       0.13665694  0.4342547 ]
     [ 0.00397229  0.54380302  0.37682723  0.41728872 -0.2362154   0.42731713
       0.09596151  0.37487011]
     [-0.17092392 -0.64697815 -0.03151477 -0.13364449 -0.44248959  0.43475375
      -0.10042811  0.37209755]
     [ 0.8093354  -0.05689074 -0.1374779  -0.15165173  0.07913371 -0.09365077
       0.24209212  0.47560492]]

```python
lambda_train = lambda_train / np.linalg.norm(lambda_train)
print(lambda_train)
print((sum(lambda_train**2)))
```

    [0.00412226 0.00503751 0.00747034 0.7330724  0.01024042 0.01468074
     0.0190798  0.67957505]
    1.0

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|000 \rangle$ obtained. This value will be proportional to the density estimated by the circuit.

```python
backend2 = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 8x8
Spred_ones = []

for i in range(len(Stest_ones)):

    qc = QuantumCircuit(6, 3)

    qc.initialize(Stest_ones[i], [0, 1, 2])
    qc.initialize(lambda_train, [3, 4, 5])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.cnot(5, 2)
    qc.cnot(4, 1)
    qc.cnot(3, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend2, shots=20000).result().get_counts()
    Spred_ones.append((counts['000'])/5000)

print(Spred_ones)
```

    [0.0282, 0.0442, 1.0824, 0.3134, 0.0394, 0.126, 0.137, 0.1578, 0.0274, 0.0282, 0.553, 0.1374, 0.1772, 0.1662, 0.0214, 0.2392, 0.2206, 0.1682, 0.0674, 0.0444, 0.238, 0.7854, 0.0962, 0.0734, 0.1704, 0.3772, 0.0156, 0.1218, 0.0378, 0.5754, 0.0912, 0.0276, 0.034, 0.0236, 0.0616, 0.3986, 0.139, 0.2224, 0.0428, 0.132, 0.8388, 0.0458, 0.1152, 0.3714, 0.1868, 0.0022, 0.4374, 0.4178, 0.4038, 0.27, 0.0116, 0.0064, 0.0864, 0.0776, 0.0524, 0.0398, 0.2716, 0.1828, 0.2048, 0.13, 0.847, 0.0418, 0.038, 0.1248, 0.1804, 0.6152, 0.245, 0.8274, 0.0466, 0.497, 0.099, 0.129, 0.3906, 0.0474, 0.1092, 0.4198, 0.1526, 0.2362, 0.0628, 0.1794, 0.0762, 0.0686, 0.1648, 0.0262, 0.0398, 0.0228, 0.4664, 0.081, 0.184, 0.4494, 0.3212, 0.0416, 0.1544, 0.877, 1.1342, 0.1304, 0.0298, 0.251, 0.0438, 0.2578, 0.451, 0.0122, 0.1348, 0.0422, 0.0086, 0.1138, 0.264, 0.2352, 0.6444, 0.5942, 0.8058, 0.011, 0.1658, 0.0556, 0.0556, 0.0246, 0.0632, 0.0284, 0.1278, 0.9456, 0.142, 0.041, 0.0566, 0.0658, 0.1738, 0.018, 0.0608, 0.0828, 0.1304, 0.016, 0.01, 0.1442, 0.0656, 0.0326, 0.0822, 0.1206, 0.1232, 0.114, 0.1474, 0.055, 0.4898, 0.1606, 0.3142, 0.9244, 0.3712, 0.6982, 0.1474, 0.0646, 0.2356, 0.4102, 0.059, 0.7512, 1.1028, 0.0706, 0.2292, 0.2616, 0.0566, 0.3858, 0.1088, 0.0508, 0.0316, 0.0394, 0.0374, 0.4084, 0.3148, 0.2196, 0.1604, 0.0106, 0.2616, 0.3498, 0.3068, 0.295, 0.0178, 0.0346, 0.3948, 0.0342, 0.049, 0.3588, 0.2312, 0.0312, 0.0468, 0.1546, 0.4462, 0.0818, 0.579, 0.0394, 0.2866, 0.0144, 0.1314, 0.042, 0.1404, 0.0478, 0.6724, 0.1064, 0.2234, 0.3452, 0.5964, 0.0358, 0.3972, 0.4494, 0.1008, 0.7524, 0.1246, 0.012, 0.1172, 0.2178, 0.186, 0.517, 0.0924, 0.144, 0.1872, 0.071, 0.0632, 0.1252, 0.1108, 0.0706, 0.1986, 0.2354, 0.9144, 0.3138, 0.0004, 0.1802, 0.0886, 0.1232, 0.5194, 0.0594, 0.1738, 0.1402, 0.203, 0.1018, 0.0448, 0.0084, 0.4224, 0.2304, 0.125, 0.0588, 0.43, 0.1482, 0.0352, 0.7156, 0.0954, 0.059, 1.0126, 0.4568, 0.0556, 0.0298, 0.2428, 0.187, 0.17, 0.0068, 0.1378, 0.061, 0.1578, 0.3568, 0.1238, 0.0214, 0.1464, 0.1384, 0.0426, 0.0324, 0.3826, 0.0132, 0.1598, 0.4828, 1.0894, 0.139, 0.0668, 0.0826, 0.1332, 0.2096, 0.0712, 0.1758, 0.0012, 0.1184, 0.1308, 0.0786, 0.7426, 0.1784, 0.059, 0.0262, 0.1618, 0.0556, 0.0046, 0.396, 0.0936, 0.1262, 0.0606, 0.8924, 0.0048, 0.0954, 0.3484, 0.1242, 0.3128, 0.1786, 0.1732, 0.0594, 0.4594, 0.0512, 0.9916, 0.937, 0.0774, 0.0656, 0.062, 0.2054, 0.0396, 0.0484, 0.0938, 0.1518, 0.1348, 0.06, 0.2054, 0.0712, 0.9556, 0.0452, 0.2058, 0.0542, 0.1894, 0.0816, 0.0378, 0.4576, 0.0404, 0.2628, 0.1418, 0.26, 0.0166, 0.5378, 0.444, 0.1744, 0.0604, 0.0708, 0.038, 0.2218, 0.1358, 0.1324, 0.0568, 1.052, 0.0832, 0.6196, 0.739, 0.3312, 0.4332, 0.527, 1.1672, 0.6988, 0.456, 0.1866, 0.1778, 0.0596]

```python
# Mixed state case
Spred_zeros = []

for i in range(len(Stest_zeros)):

    qc = QuantumCircuit(6, 3)

    qc.initialize(Stest_zeros[i], [0, 1, 2])
    qc.initialize(lambda_train, [3, 4, 5])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.cnot(5, 2)
    qc.cnot(4, 1)
    qc.cnot(3, 0)

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)
    counts = execute(qc, backend2, shots=20000).result().get_counts()
    try:
        Spred_zeros.append((counts['000'])/5000)
    except:
        Spred_zeros.append(0)

print(Spred_zeros)
```

    [0.0616, 0.02, 0.0596, 0.1126, 0.1856, 0.0788, 0.8884, 0.144, 0.043, 0.0278, 0.232, 0.0136, 0.014, 0.2666, 0.1744, 0.1472, 0.4238, 0.0456, 0.2086, 0.4572, 0.0384, 0.3562, 0.21, 0.1102, 0.1384, 0.201, 0.0196, 0.0486, 0.3134, 0.2972, 0.0154, 0.0126, 0.8714, 0.0606, 0.0458, 0.0186, 0.4808, 0.1086, 0.1412, 0.3704, 0.0092, 0.261, 0.0056, 0.116, 0.0346, 0.0004, 0.649, 0.057, 0.016, 0.6096, 0.0406, 0.1456, 0.119, 0.3538, 0.0214, 0.1672, 0.0782, 0.0222, 0.3208, 0.2108, 0.3198, 0.4384, 0.0044, 0.005, 0.0086, 0.6886, 0.2306, 0.0202, 0.1014, 0.0908, 0.1164, 0.0602, 0.1032, 0.1862, 0.0034, 0.0544, 0.0064, 0.0208, 0.8406, 0.029, 0.1106, 0.4398, 0.096, 0.036, 0.2546, 0.0826, 0.0166, 0.223, 0.2656, 0.1222, 0.1074, 0.1356, 0.044, 0.1108, 0.0002, 0.0316, 0.0894, 0.5982, 0.7574, 0.1582, 0.1666, 0.102, 0.0444, 0.1542, 0.1554, 0.0612, 0.346, 0.1168, 0.1824, 0.02, 0.0638, 0.0582, 0.0248, 0.0448, 0.0314, 0.095, 0.4242, 0.0614, 0.0744, 0.0632, 0.0806, 0.115, 0.0314, 0.0638, 0.1242, 0.4748, 0.006, 0.022, 0.2298, 0.0278, 0.026, 0.048, 0.6556, 0.5888, 0.079, 0.0044, 0.0466, 0.033, 0.0772, 0.0562, 0.8336, 0.0292, 0.0478, 0.4602, 0.2634, 0.008, 0.6098, 0.1838, 0.0528, 0.2418, 0.7274, 0.0018, 0.6328, 0.1916, 0.0274, 0.0298, 0.0676, 0.1418, 0.0018, 0.6862, 0.134, 0.0878, 0.0938, 0.3954, 0.0172, 0.0166, 0.05, 0.1684, 0.0034, 0.0432, 0.0728, 0.0278, 0.8872, 0.3374, 0.0016, 1.1834, 0.0478, 0.0206, 0.026, 0.016, 0.4316, 0.1544, 0.0832, 0.6896, 0.2216, 0.0222, 0.3654, 0.0918, 0.1186, 0.6982, 0.133, 0.0366, 0.0476, 0.016, 0.1564, 0.248, 0.4862, 0.0286, 0.011, 0.0342, 0.3144, 0.0324, 0.1576, 0.0436, 0.2858, 0.0432, 0.1176, 0.0724, 0.0882, 0.0484, 0.015, 0.7558, 0.0488, 0.1148, 0.0514, 0.1798, 0.5156, 0.6274, 0.02, 0.4944, 0.1696, 0.0076, 0.047, 1.4586, 0.025, 0.0204, 0.0108, 1.2458, 0.0904, 0.4012, 0.6454, 0.1872, 0.1232, 0.0064, 0.0494, 0.789, 0.0492, 0.0234, 0.0372, 0.0474, 0.273, 0.0768, 0.0526, 0.0998, 0.4206, 0.1428, 0.0544, 0.0296, 0.066, 0.0542, 0.0562, 0.7636, 0.0718, 0.0098, 0.0608, 0.0388, 0.011, 0.0332, 0.8718, 0.1594, 0.0764, 0.02, 0.0474, 0.08, 0.1722, 0.3356, 0.0088, 0.5034, 0.1084, 0.4442, 0.0236, 0.1566, 0.0606, 0.1554, 0.8778, 0.0392, 0.0316, 0.0082, 0.7082, 0.002, 0.0862, 0.05, 0.971, 0.1796, 0.2576, 0.0428, 0.2412, 0.1818, 0.3768, 0.2836, 0.758, 0.0226, 0.0414, 0.3534, 0.036, 0.0076, 0.0584, 0.1076, 0.5112, 0.0206, 0.0178, 0.0184, 0.0236, 0.0264, 0.0404, 0.1688, 0.3232, 0.1468, 0.384, 0.5764, 0.0018, 0.144, 0.1078, 0.0918, 0.0986, 0.0716, 0.2476, 0.0544, 0.0628, 0.3384, 0.1354, 0.0866, 0.3804, 0.1172, 0.7774, 0.095, 0.0254, 0.022, 0.1454, 0.7444, 0.0552, 0.091, 0.1046, 0.0256, 0.054, 0.117, 0.1334, 0.0688, 0.4224, 0.0132, 0.3922, 0.973, 0.0212, 0.0028, 0.0286, 0.0326, 0.4862, 0.446, 0.1728, 0.0188, 0.2982, 0.7738, 0.0784, 0.1666, 0.23, 0.2616, 0.6162, 0.9596, 0.0042, 0.2426, 0.0028, 0.0916, 0.099, 0.0814, 0.0154, 0.1824, 0.1454, 0.1686, 0.7528, 0.198, 0.4002, 0.6602, 0.0874, 0.1004, 0.0746, 0.0238, 0.0158, 0.5918, 0.0474, 0.127, 0.1228, 0.1056, 0.1754, 0.2392, 0.2918, 0.3436, 0.0702, 0.146, 0.0886, 0.1536, 0.3586, 1.2944, 0.0042, 0.2052, 0.086, 0.2306, 0.024, 0.14, 0.0398, 0.0924, 0.0054, 0.1176, 0.028, 0.0954, 0.0446, 0.8098, 0.4628, 0.0296, 0.0262, 0.0302, 0.022, 0.1086, 0.5516, 0.1644, 0.1092, 0.024, 0.1426, 0.151, 0.131, 0.05, 0.7566, 0.0494, 0.126, 0.1196, 0.1652, 0.0054, 0.2576, 0.3914, 0.1264, 0.0232, 0.0678, 0.1284, 0.0224, 0.2864, 0.0066, 0.4198, 0.0448, 0.1144, 0.0718, 0.5752, 0.1046, 0.0398, 0.3438, 0.382, 0.1728, 0.0028, 0.0072, 0.1404, 0.0638, 0.2164, 0.2206, 0.1242, 0.0834, 0.0302, 0.094, 0.1622, 0.03, 0.0546, 0.177, 0.0438, 0.1058, 0.029, 0.2538, 0.0828, 0.0216, 0.069, 0.0648, 0.1358, 0.032, 0.7592, 0.0084, 0.114, 0.102, 0.1742, 0.641, 0.0224, 0.0458, 0.8056, 0.0856, 0.1628, 0.0624, 0.1886, 0.173, 0.0418, 0.0676, 0.0656, 0.6562, 0.2684, 0.0772, 0.1598, 0.0072, 0.102, 0.1782, 0.064, 0.042, 0.0928, 0.0798, 0.371, 0.0298, 0.0472, 0.249, 0.0052, 0.2572, 0.6134, 0.0722, 0.1132, 0.049, 0.0374, 0.175, 0.138, 0.089, 0.71, 0.0116, 0.145, 0.0074, 0.5212, 0.0098, 0.0288, 0.7808, 0.109, 0.103, 0.8254, 0.0048, 0.0036, 0.2904, 0.311, 0.4154, 0.0762, 0.05, 0.3338, 1.0782, 0.6684, 0.546, 0.3188, 0.0598, 0.0316, 0.2176, 0.0244, 0.0596, 0.4664, 0.4048, 0.1188, 0.5724, 0.111, 0.0724, 0.0188, 0.015, 0.5298, 0.728, 0.5982, 0.2492, 0.837, 0.256, 0.183, 0.0108, 0.0866, 0.0488, 0.0844, 0.0272, 0.031, 0.1076, 0.1596, 0.1382, 0.06, 0.0602, 0.0316, 0.0076, 0.098, 0.0858, 0.072, 0.3988, 0.279, 0.0466, 0.6766, 0.0952, 0.247, 0.0864, 0.202, 0.2078, 0.1982, 0.083, 0.3998, 0.0692, 0.017, 0.017, 0.6566, 0.0112, 0.0206, 0.0706, 0.0704, 0.0484, 0.0318, 0.4798, 0.0814, 0.2676, 0.302, 0.1058, 0.0386, 0.157, 0.011, 0.0904, 0.2666, 0.0038, 0.523, 0.34, 0.1016, 0.0168, 0.058, 0.0476, 0.0248, 0.5648, 0.0258, 0.0712, 0.0484, 0.13, 0.7426, 0.0924, 0.1422, 0.0818, 0.0154, 0.037, 0.08, 0.0324, 0.0496, 0.0406, 0.0182, 0.11, 0.021, 0.0504, 0.094, 0.0104, 0.4076, 0.0834, 0.0514, 0.0334, 0.0132, 0.1814, 0.1548, 0.012, 0.284, 0.1742, 0.7278, 0.0334, 0.1842, 0.0334, 0.0616, 0.0786, 0.3566, 1.1394, 0.046, 0.4704, 0.0056, 0.071, 0.012, 0.1658, 0.1848, 0.0472, 0.5916, 0.1052, 0.979, 0.2968, 0.2938, 0.0368, 0.0148, 0.0662, 0.1234, 0.0588, 0.1544, 0.01, 0.0974, 0.1832, 0.5994, 0.0502, 0.5574, 0.0036, 0.223, 0.1022, 0.1754, 0.1104, 0.617, 0.0608, 0.042, 0.3984, 0.0016, 0.2024, 0.0022, 0.1346, 0.1024, 0.0778, 0.1328, 0.0402, 1.2386, 0.1444, 0.0938, 0.0132, 0.1068, 0.0328]

We need to determine a threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier.

```python
Spred_val = np.concatenate((Spred_ones[:100], Spred_zeros[:100]))
print(Spred_val.shape)

Sy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Sy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Sy_val, Spred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Spred_val > t).astype(int)
    metric = f1_score(Sy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.1222

We have obtained that our best threshold is at $t = 0.1222$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Spr_zeros = (np.array(Spred_zeros) > T).astype(int)

Spr_ones = (np.array(Spred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(248), np.zeros(597))),
        np.concatenate((Spr_ones[100:], Spr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.7620    0.5846    0.6616       597
             1.0     0.3592    0.5605    0.4378       248

        accuracy                         0.5775       845
       macro avg     0.5606    0.5725    0.5497       845
    weighted avg     0.6438    0.5775    0.5959       845

## SpamBase 8x8 Pure State

We build our training density matrix using Pure States.

```python
phi_train = np.sum(np.abs(Strain), axis=0)
phi_train = phi_train / np.linalg.norm(phi_train)

U_train = create_U_train(phi_train)

print(U_train)
```

    [[ 2.42533676e-01  3.92465306e-01 -6.70307607e-01  2.91091225e-01
       2.67895241e-01 -4.20590156e-01 -6.43884790e-02 -1.70937208e-02]
     [ 3.96977281e-01 -1.22555550e-01  3.64045805e-02  2.60429951e-02
      -6.95863502e-01 -2.91149292e-01 -5.06351380e-01 -3.63535790e-05]
     [ 2.06555201e-01  1.73205406e-01 -2.28882531e-01  7.00712557e-02
      -2.51969816e-01  5.40217153e-01  1.42867681e-01 -7.03066929e-01]
     [ 2.22028386e-01  5.51803480e-01  6.94026834e-01  2.93379612e-01
       1.23616632e-01 -1.80470956e-01  3.93990677e-02 -1.70494422e-01]
     [ 4.33649605e-01  3.06048557e-01 -5.08853786e-03 -7.41394147e-01
       2.34198061e-01  2.05822626e-01 -2.12803105e-01  1.61537237e-01]
     [ 3.73541690e-01 -5.31680987e-01  1.05928774e-01  2.79501890e-01
       5.36466451e-01  1.51244397e-01 -3.80668763e-01 -1.81272982e-01]
     [ 3.71143602e-01  6.25581780e-02 -4.59237958e-02  3.98627595e-01
      -1.48527718e-01  4.65894247e-01  2.29219143e-01  6.36919617e-01]
     [ 4.75701431e-01 -3.40881226e-01  4.41153768e-02 -1.92134164e-01
      -2.30390559e-02 -3.62817311e-01  6.89030956e-01 -1.08238517e-01]]

We will need a different circuit for each sample $|x \rangle$ in the test partition. To obtain a more accurate predicted value, we will run the circuit many shots, and then we will store the amount of $|00 \rangle$ obtained.

```python
backend = Aer.get_backend('qasm_simulator')
np.random.seed(1234)
```

```python
## Density Estimation Approach matrices 8x8
Spred_ones = []

for i in range(len(Stest_ones)):

    qc = QuantumCircuit(3, 3)
    qc.initialize(Stest_ones[i], [0, 1, 2])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Spred_ones.append((counts['000'])/5000)
    except:
        Spred_ones.append(0)

print(Spred_ones)
```

    [0.0246, 0, 0.0604, 0.0026, 0.0012, 0.0626, 0.0152, 0.0104, 0.0012, 0.017, 0.0066, 0.0002, 0.0928, 0.0296, 0.022, 0.156, 0.4428, 0.0486, 0.1302, 0.0822, 0.4706, 1.615, 0.002, 0.1304, 0.0062, 0.0094, 0.0214, 0.0068, 0.0134, 0.761, 0.018, 0.0686, 0.0194, 0.0054, 0.0092, 0.0022, 0.0002, 0.0164, 0.0904, 0.2304, 0.1412, 0.004, 0.0536, 0.0046, 0.0874, 0.0092, 0.7992, 0.5146, 0.074, 0.4646, 0.0022, 0, 0.0226, 0.0256, 0.0626, 0.003, 0.2378, 0.0028, 0.0052, 0.2408, 1.519, 0.0218, 0.0236, 0.0006, 0, 1.071, 0.0366, 1.5064, 0.083, 0.0568, 0.0044, 0.01, 0.0156, 0.0148, 0.0052, 0.0696, 0.0064, 0.2972, 0.0094, 0.0152, 0.0012, 0.0002, 0.0208, 0.0536, 0.0152, 0.0046, 0.8544, 0.1378, 0.0344, 0.8454, 0.0252, 0.016, 0.3432, 0.0092, 1.8166, 0.0074, 0.0126, 0.0324, 0.0058, 0.064, 0.8298, 0, 0.0738, 0.0066, 0.0076, 0.0038, 0.0014, 0.0712, 0.533, 1.054, 1.2136, 0.0076, 0.2522, 0.0856, 0.013, 0.046, 0.027, 0.0356, 0.1044, 1.2682, 0.0572, 0.0178, 0.001, 0.0276, 0.0032, 0.0034, 0.0162, 0.0976, 0.0054, 0.0098, 0.0224, 0.0082, 0.0012, 0.0606, 0.0168, 0.0084, 0.0042, 0.0482, 0.0186, 0.0022, 0.0176, 0.001, 0.0004, 1.6846, 0.0052, 0.5186, 0.0018, 0.0024, 0.1316, 0.0596, 0.0022, 0.5572, 0.0658, 0.005, 0.1224, 0.025, 0.1126, 0.0228, 0.0028, 0.016, 0.0002, 0.0214, 0.002, 0.0278, 0.3356, 0.0068, 0.3236, 0.007, 0.0724, 0.1932, 0.5092, 0.009, 0.0012, 0.0556, 0.0002, 0.0298, 0.0018, 0.054, 0.076, 0.0364, 0.0004, 0.0024, 0.2236, 0.0094, 0.841, 0.0014, 0.014, 0.0028, 0.0032, 0, 0.0054, 0.0726, 0.4644, 0.01, 0.4314, 0.0006, 0.3162, 0.0004, 0.6902, 0.2372, 0.0838, 0.0058, 0.0004, 0.0096, 0.204, 0.1736, 0.0094, 0.0632, 0.0274, 0.0054, 0.009, 0.0006, 0.0114, 0.0034, 0.1464, 0.021, 0.0252, 0.1558, 0.049, 0.0196, 0, 0, 0.005, 0.0038, 0.5328, 0.0022, 0.0396, 0.0002, 0.1692, 0, 0.0018, 0.0094, 0.004, 0.1794, 0.0748, 0.0156, 0.1252, 0.0032, 0.0132, 0.261, 0.124, 0.0506, 0.0692, 0.1264, 0.0038, 0.0604, 0.2442, 0.0004, 0.0302, 0.0106, 0.0038, 0.1268, 0.009, 0.0772, 0.0016, 0.0154, 0.038, 0.004, 0.0746, 0.0186, 0.1328, 0.0268, 0.3376, 0.08, 0.0006, 0.0232, 0, 0, 0.0034, 0.196, 0.0664, 0.0012, 0.0014, 0.0004, 0.01, 0.0256, 0.0116, 0.0452, 0.1218, 0.036, 0.0334, 0.0314, 0.0088, 0, 0.0118, 0, 0.0012, 0.052, 0.0086, 0.0182, 0.694, 0.1352, 0.0112, 0.0036, 0.0052, 0, 0.294, 0.1134, 0.062, 0.051, 0.0068, 0.0154, 0.0236, 0.0348, 0.0054, 0.0038, 0.1772, 0.1942, 0.004, 0.0584, 0.0158, 0.0266, 0.0618, 0.0022, 0.0292, 0.1082, 0.2184, 0.0016, 0.0792, 0.818, 0.0146, 0.1024, 0.0046, 0.0048, 0.015, 0.0016, 0.0318, 0.0306, 0, 0.0136, 0.0438, 0.1616, 0.0038, 0.0036, 0.0492, 0.07, 0.0002, 0.5644, 0.1556, 0.0024, 0.0666, 0.8726, 1.4558, 0.034, 0.01, 0.3438, 0.094, 0.0196]

```python
# Pure state case
Spred_zeros = []

for i in range(len(Stest_zeros)):

    qc = QuantumCircuit(3, 3)
    qc.initialize(Stest_zeros[i], [0, 1, 2])
    qc.isometry(U_train.T, [], [0, 1, 2]) # ArbRot as a isometry

    qc.measure(0, 0)
    qc.measure(1, 1)
    qc.measure(2, 2)

    counts = execute(qc, backend, shots=20000).result().get_counts()
    try:
        Spred_zeros.append((counts['000'])/5000)
    except:
        Spred_zeros.append(0)

print(Spred_zeros)
```

    [0.0636, 0.023, 0.036, 0.0396, 0.0112, 0.041, 0.0174, 0.1252, 0.0438, 0.0006, 0.0216, 0.0002, 0.003, 0.2998, 0.0648, 0.0756, 0.2608, 0.0772, 0.0364, 0.0424, 0.0684, 0.0008, 0.0062, 0.0604, 0.086, 0.0948, 0.0302, 0.0756, 0.007, 0, 0.0002, 0.025, 0.001, 0.1122, 0.0026, 0.0274, 0.1362, 0.0386, 0.0016, 0.057, 0.007, 0.0724, 0.0094, 0.0014, 0, 0, 0.1166, 0.0278, 0.015, 0.1096, 0.0026, 0.0008, 0.0004, 0.1416, 0.002, 0, 0.0194, 0.001, 0.055, 0.447, 0.0544, 0.1132, 0.0042, 0.0004, 0, 0.0724, 0.0346, 0.009, 0.001, 0.067, 0.1576, 0.0238, 0.2234, 0.0322, 0.0076, 0.111, 0.0118, 0.0376, 0.0478, 0.0006, 0.0262, 0.0474, 0.0154, 0.0616, 0.0252, 0.0334, 0.0014, 0.0342, 0.0194, 0.0012, 0.026, 0.0138, 0.004, 0.2384, 0, 0.0604, 0.1152, 0.285, 0.0242, 0.2776, 0.0604, 0.171, 0, 0.0454, 0.053, 0.079, 0.0182, 0.0002, 0.291, 0.0052, 0.1194, 0.112, 0.0028, 0.0824, 0.0208, 0.0724, 0.5996, 0.0584, 0.0632, 0.0004, 0.0036, 0.0126, 0.0038, 0.0278, 0.0212, 0.99, 0.001, 0.0092, 0.1216, 0.0172, 0.0038, 0.0948, 0.0184, 0.2664, 0.0582, 0.0044, 0.0282, 0.0356, 0.0066, 0.1224, 0.1072, 0.0546, 0.0882, 0.1486, 0.5692, 0.0102, 0.3012, 0.0126, 0.101, 0.0004, 1.221, 0.0002, 0.194, 0.059, 0.0024, 0.0476, 0.0008, 0.0534, 0.002, 0.0286, 0, 0.1198, 0.0578, 0.0682, 0.0352, 0.0092, 0, 0.0378, 0.0064, 0.0566, 0.0278, 0.053, 0.028, 0.3984, 0.0004, 0.0054, 0.0304, 0.0362, 0.0436, 0.0134, 0.2012, 0.0466, 0.0054, 0.0292, 0.0432, 0.0266, 0.0716, 0.0084, 0.0146, 0.0166, 0.0134, 0.0078, 0.022, 0.002, 0.3488, 0.4134, 0.01, 0.0146, 0.0112, 0.0144, 0.3296, 0.0288, 0.0526, 0.055, 0.017, 0.0912, 0.008, 0.0018, 0.1742, 0.0674, 0.0268, 0.1864, 0.015, 0.0634, 0.0222, 0.0096, 0.002, 0.112, 0.026, 0.2306, 0, 0.0048, 0.0712, 0.0902, 0, 0.0366, 0.0142, 0.7466, 0.0788, 0.272, 0.1988, 0.0248, 0.0006, 0.0014, 0.049, 1.0328, 0.0148, 0.035, 0.081, 0.0432, 0.1168, 0.0046, 0.102, 0.025, 0.0002, 0.02, 0.021, 0.0546, 0.0298, 0.0034, 0.1286, 0.1768, 0.0208, 0.0042, 0.1192, 0.0062, 0.017, 0.0032, 0.003, 0.0064, 0, 0, 0.014, 0.036, 0, 0.2522, 0.0116, 0.2238, 0.0476, 0.0456, 0.0414, 0.042, 0, 0.2918, 0.0098, 0.0194, 0.0258, 0.012, 0.001, 0, 0.1732, 0.0848, 0.003, 0.0462, 0.5658, 0.0938, 0.0124, 0.176, 0.0014, 0.403, 0.4128, 0.0204, 0.0784, 0.002, 0.0458, 0.0018, 0.0192, 0.0638, 0.5398, 0.0408, 0.0298, 0.0014, 0.0302, 0.0142, 0.0008, 0.0068, 0.0148, 0.0142, 0.1638, 0.023, 0, 0.042, 0.2142, 0.1632, 0.0606, 0.0918, 0.058, 0.0532, 0.0108, 0.0792, 0.2692, 0, 0.1424, 0.033, 0.1526, 0.0892, 0.0408, 0.0112, 0.0592, 0.1264, 0.0392, 0.036, 0.025, 0.0094, 0.0698, 0.0018, 0.2834, 0.1076, 0.8934, 0.0268, 0.8322, 0.0386, 0.0454, 0, 0.0002, 0.0002, 0.1034, 0.0248, 0.0464, 0.0314, 0.6478, 0.4158, 0.1508, 0.002, 0.0066, 0.2712, 0.0326, 0.0022, 0.0076, 0.466, 0.007, 0.0304, 0.1892, 0.12, 0.0222, 0.0742, 0.0126, 0, 0.0002, 0.074, 0.0596, 0.1782, 0.0038, 0.0254, 0.0706, 0.006, 0.024, 0.1226, 0.0106, 0.0618, 0.001, 0.137, 0.126, 0.0056, 0.0002, 0.1124, 0.075, 0.1692, 0.0586, 0.1556, 0.0502, 0.0716, 0.0052, 0.0164, 0.0452, 0.0494, 0.0002, 0.1812, 0.0846, 0.0614, 0.0054, 0.0354, 0.0154, 0.19, 0.0816, 0.1006, 0.802, 0.0426, 0.0042, 0.0292, 0.0526, 0.036, 0.107, 0.001, 0.104, 0.0032, 0.304, 0.0008, 0.255, 0.0244, 0.4556, 0.1152, 0.0042, 0.081, 0.06, 0.0024, 0.0032, 0.009, 0.223, 0.0052, 0.117, 0.132, 0.0492, 0.1128, 0.0064, 0.1814, 0.0042, 0.0584, 0.0056, 0.0048, 0, 0.0574, 0.3994, 0.0082, 0.0044, 0.0052, 0.0174, 0.0068, 0.1182, 0.0064, 0.0034, 0.014, 0.025, 0.0412, 0.1218, 0.0776, 0.049, 0.0652, 0.0466, 0.017, 0, 0, 0.0696, 0, 0, 0.145, 0.1198, 0, 0.0622, 0.4522, 0.013, 0.017, 0.0318, 0, 0.2092, 0.0132, 0.0132, 0.095, 0.054, 0.2292, 0.0134, 0.1004, 0, 0.0836, 0.0328, 0.0568, 0.0078, 0, 0.0248, 0.1346, 0, 0.0534, 0.0516, 0.0472, 0.1096, 0.0226, 0.0034, 0.3028, 0.0504, 0.0392, 0.004, 0.0134, 0.0614, 0.149, 0.1352, 0.1672, 0.0108, 0.0002, 0.012, 0.2962, 0.1646, 0.0052, 0.0002, 0, 0.0184, 0.1324, 0.015, 0.04, 0.0798, 0.0378, 0.0012, 0.11, 0.0074, 0.0076, 0.035, 0.0088, 0.0144, 0.0142, 0.0074, 0.1978, 0.2592, 0.0464, 0.0598, 0.0012, 0.1186, 0.016, 0.3314, 0.0502, 0.022, 0.9616, 0.6108, 0.0304, 0.0026, 0.032, 0.071, 0.0338, 0.035, 0.544, 0.139, 0.1454, 0.033, 0.0142, 0.0392, 0.0762, 0.0186, 0.152, 0.0014, 0.102, 0.0532, 0.004, 0.0036, 0.0008, 0.111, 0.0936, 0.031, 0.0154, 0.012, 0.147, 0.0132, 0.0632, 0, 0.1514, 0.0278, 0.4252, 0.0968, 0.054, 0.0856, 0.0056, 0.0208, 0.0752, 0.038, 0.3008, 0.1382, 0.0284, 0.0354, 0.1844, 0.0152, 0.003, 0.1124, 0.111, 0.0338, 0.0198, 0.1328, 0.045, 0.1748, 0.0072, 0.1886, 0.086, 0.0034, 0.005, 0.0506, 0.096, 0.0102, 0.0734, 0.4372, 0.1308, 0.031, 0.0206, 0.098, 0.0322, 0, 0.014, 0.0378, 0.04, 0.0598, 0.1302, 0.0286, 0.3008, 0, 0.0238, 0, 0.0214, 0.0046, 0.0142, 0.0032, 0.0414, 0.0036, 0.0376, 0.0036, 0.1052, 0.0134, 0.1526, 0.1256, 0.0994, 0.0018, 0.0134, 0.0344, 0.0006, 0.0058, 0.5004, 0.1302, 0.0456, 0.0794, 0.0262, 0.009, 0.0254, 0.0002, 0.0482, 1.822, 0.007, 0.037, 0.0088, 0.0018, 0.0286, 0.036, 0.0114, 0.0964, 0.329, 0.1672, 0.002, 0.3964, 0.1324, 0.0128, 0.0168, 0.1216, 0.0022, 0.0256, 0.2836, 0.0074, 0.0026, 0.0138, 0.0046, 0.0466, 0.3966, 0.0054, 0.156, 0.2022, 0.0346, 0.0108, 0.3216, 0.131, 0.0072, 0, 0.0014, 0.0008, 0.0086, 0.0006, 0.0928, 0.0296, 0.2824, 0.0304, 0.4542, 0.0492, 0.006, 0.0204, 0.178, 0.0034]

Once we have obtained the predicted density values, we need to determine a threshold $t$ such that if a sample has a density lower than $t$, it is classified as an outlier. For this, we will define a validation partition from our test groups, and we'll try to find the best possible value for $t$ using only validation samples. After that, we'll use this threshold to compare with all the remaining test samples.

```python
Spred_val = np.concatenate((Spred_ones[:100], Spred_zeros[:100]))
print(Spred_val.shape)

Sy_val = np.concatenate((np.ones(100), np.zeros(100)))
print(Sy_val.shape)
```

    (200,)
    (200,)

```python
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(Sy_val, Spred_val, pos_label=1)

plt.figure()
lw = 2
plt.plot(fpr, tpr, color="darkorange",
    lw=lw, label="ROC curve")
plt.plot([0, 1], [0, 1], color="navy", lw=lw, linestyle="--")
print(auc(fpr, tpr))
```

```python
from sklearn.metrics import f1_score

m, T = 0,0
for t in thresholds:
    preds = (Spred_val > t).astype(int)
    metric = f1_score(Sy_val, preds, average='weighted')
    if (metric > m):
        m = metric
        T = t

print(T)
```

    0.0616

We have obtained that our best threshold is at $t = 0.0616$. So we will use this threshold to compare all test samples and to calculate some metrics for our estimator.

```python
Spr_zeros = (np.array(Spred_zeros) > T).astype(int)

Spr_ones = (np.array(Spred_ones) > T).astype(int)
```

```python
from sklearn.metrics import classification_report

print(classification_report(
        np.concatenate((np.ones(248), np.zeros(597))),
        np.concatenate((Spr_ones[100:], Spr_zeros[100:])), digits=4))
```

                  precision    recall  f1-score   support

             0.0     0.6988    0.6566    0.6770       597
             1.0     0.2782    0.3185    0.2970       248

        accuracy                         0.5574       845
       macro avg     0.4885    0.4876    0.4870       845
    weighted avg     0.5753    0.5574    0.5655       845