{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# QBoost for QUBO\n", "\n", "#### Device: Dirac-1\n", "\n", "\n", "## Introduction\n", "\n", "In what follows, a QUBO-based binary classifier, namely QBoost, is discussed. The proposed algorithm can be solved using our Dirac-1 technology. Here we show an implementation of QBoost and test it on a simple binary classification problem using the IRIS dataset." ] }, { "cell_type": "markdown", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "## Methodology\n", "\n", "The idea is based on the concept of boosting. Let us assume that we have a collection of $N$ \"weak\" classifiers $h_i$ where $i=1, 2,...,N$. The goal is to construct a \"strong\" classifier as a linear superposition of these weak classifiers, that is,\n", "\n", "$y = \\sum_{i=1}^{N} w_i h_i({\\bf{x}})$\n", "\n", "where ${\\bf{x}}$ is a vector of input features and $y \\in \\{-1, 1\\}$. The goal is to find $w_i$, weights associated with the weak classifiers. \n", "\n", "Let us have a training set $\\{({\\bf{x_s}}, y_s) | s = 1, 2,...,S\\}$ of size $S$. We can determine optimal weights $w_i$ by minimizing,\n", "\n", "$\\min_{w_i} \\sum_{s=1}^{S} |\\sum_{i=1}^{N} w_i h_i({\\bf{x_s}}) - y_s|^2 + \\lambda \\sum_{i=1}^{N} (w_i)^0$\n", "\n", "where the regularization term $\\lambda \\sum_{i=1}^{N} (w_i)^0$ penalizes non-zero weights; $\\lambda$ is the regularization coefficient. Re-arranging the above equation yields,\n", "\n", "$\\min_{{\\bf{w}}} \\frac{1}{N^2} \\sum_{i=1}^{N} \\sum_{j=1}^{N} w_i w_j \\sum_{s=1}^{S} h_i({\\bf{x_s}}) h_j({\\bf{x_s}}) + \\frac{1}{N} \\sum_{i=1}^{N} \\sum_{s=1}^{S} -2 y_s h_i({\\bf{x_s}}) w_i + \\lambda \\sum_{i=1}^{N} (w_i)^0$\n", "\n", "where here we assume that $w_i$ weights are integers. Each weight can be constructed using $D$ qubits as\n", "\n", "$w_i = \\sum_{d=0}^{D-1} 2^d x_{i,d}$\n", "\n", "where $x_{i,d}$ are binary variables. Navin et. al. (https://arxiv.org/abs/0811.0416) reported that using $D=1$ yields similar or improved generalized errors compared to $D > 1$. The regularization term $\\lambda \\sum_{i=1}^{N} (w_i)^0$ only works when $D = 1$ that is when the weights are binary. The corresponding QUBO is then,\n", "\n", "$\\min_{{\\bf{x}}} {\\bf{x}^T} (Q + P) {\\bf{x}}$\n", "\n", "where \n", "\n", "$Q_{ij} = \\frac{1}{N^2} \\sum_{s=1}^{S} h_i({{\\bf{x_s}}}) h_j({{\\bf{x_s}}})$\n", "\n", "and\n", "\n", "$P_{ij} = \\delta_{ij} (\\lambda - \\frac{2}{N} \\sum_{s=1}^{S} h_i({{\\bf{x_s}}}) y_s)$\n", "\n", "Note that the regularization term is designed to push many weights to zero, so a subset of the weak classifiers are chosen.\n", "\n", "In the implementation that follows, we have used decision tree classifiers based on one, two, or three of the features as the weak classifiers. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data\n", "\n", "We halved the IRIS dataset to build a binary classifier using QBoost. The reader can refer to \n", "\n", "https://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html\n", "\n", "and \n", "\n", "https://en.wikipedia.org/wiki/Iris_flower_data_set\n", "\n", "for more information on the IRIS dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementation QBoost Algorithm\n", "\n", "We have implemented the QBoost algorithm that was explained above as a class in Python." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from qci_client import QciClient\n", "token = \"your_token\"\n", "api_url = \"https://api.qci-prod.com\"\n", "qci = QciClient(api_token=token, url=api_url)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Import libs\n", "import os\n", "import sys\n", "import time\n", "import datetime\n", "import json\n", "from functools import wraps\n", "import numpy as np\n", "import pandas as pd\n", "from scipy.optimize import minimize\n", "import matplotlib.pyplot as plt\n", "from sklearn.tree import DecisionTreeClassifier\n", "from sklearn.naive_bayes import GaussianNB\n", "from sklearn.ensemble import RandomForestClassifier\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.gaussian_process import GaussianProcessClassifier\n", "from sklearn.gaussian_process.kernels import RBF\n", "from sklearn.metrics import (\n", " confusion_matrix,\n", " precision_score,\n", " recall_score,\n", " accuracy_score,\n", " f1_score,\n", ")\n", "\n", "\n", "\n", "PLOT_FLAG = False\n", "\n", "\n", "def timer(func):\n", " @wraps(func)\n", " def wrapper(*args, **kwargs):\n", " beg_time = time.time()\n", " val = func(*args, **kwargs)\n", " end_time = time.time()\n", " tot_time = end_time - beg_time\n", "\n", " print(\"Runtime of %s: %0.2f seconds!\" % (func.__name__, tot_time,))\n", "\n", " return val\n", "\n", " return wrapper\n", "\n", "class WeakClassifierDct:\n", " def __init__(self, fea_ind_list, X_train, y_train):\n", "\n", " assert X_train.shape[0] == len(y_train)\n", "\n", " self.fea_ind_list = fea_ind_list\n", " self.X_train = X_train\n", " self.y_train = y_train\n", " self.clf = DecisionTreeClassifier(random_state=0)\n", "\n", " def train(self):\n", "\n", " X_tmp = self.X_train.transpose()[self.fea_ind_list].transpose()\n", "\n", " self.clf.fit(X_tmp, self.y_train)\n", "\n", " def predict(self, X):\n", "\n", " X_tmp = X.transpose()[self.fea_ind_list].transpose()\n", "\n", " return self.clf.predict(X_tmp)\n", "\n", "\n", "class QBoost:\n", " def __init__(\n", " self,\n", " lambda_coef,\n", " num_eqc_samples=10,\n", " alpha=1.0,\n", " theta=0.0,\n", " mode=\"dct\",\n", " ):\n", "\n", " self.lambda_coef = lambda_coef\n", " self.num_eqc_samples = num_eqc_samples\n", " self.alpha = alpha\n", " self.theta = theta\n", " self.mode = mode\n", " self.weights = None\n", " self.h_list = None\n", "\n", "\n", " @timer\n", " def _build_weak_classifiers_dct(self, X, y):\n", "\n", " S = X.shape[0]\n", " M = X.shape[1]\n", "\n", " assert len(y) == S\n", "\n", " h_list = []\n", "\n", " for l in range(M):\n", " weak_classifier = WeakClassifierDct([l], X, y)\n", " weak_classifier.train()\n", "\n", " h_list.append(weak_classifier)\n", "\n", " for i in range(M):\n", " for j in range(i + 1, M):\n", " weak_classifier = WeakClassifierDct([i, j], X, y)\n", " weak_classifier.train()\n", " h_list.append(weak_classifier)\n", "\n", " for i in range(M):\n", " for j in range(i + 1, M):\n", " for k in range(j + 1, M): \n", " weak_classifier = WeakClassifierDct([i, j, k], X, y)\n", " weak_classifier.train()\n", " h_list.append(weak_classifier)\n", " \n", " return h_list\n", " \n", " \n", " @timer\n", " def _get_hamiltonian(self, X, y):\n", "\n", " S = X.shape[0]\n", " M = X.shape[1]\n", "\n", " if self.mode == \"dct\":\n", " h_list = self._build_weak_classifiers_dct(X, y) \n", " else:\n", " assert False, \"Incorrect mode <%s>!\" % self.mode\n", "\n", " self.h_list = h_list\n", "\n", " N = len(h_list)\n", "\n", " Q = np.zeros(shape=(N, N), dtype=\"d\")\n", " P = np.zeros(shape=(N, N), dtype=\"d\")\n", "\n", " h_vals = np.array([h_list[i].predict(X) for i in range(N)])\n", "\n", " assert h_vals.shape[0] == N\n", " assert h_vals.shape[1] == S\n", "\n", " for i in range(N):\n", " P[i][i] = self.lambda_coef - (2.0 / N) * np.sum(h_vals[i] * y)\n", " for j in range(N):\n", " Q[i][j] = (1.0 / N ** 2) * np.sum(h_vals[i] * h_vals[j])\n", "\n", " # Calculate the Hamiltonian\n", " H = Q + P\n", "\n", " # make sure H is symmetric up to machine precision\n", " H = 0.5 * (H + H.transpose())\n", "\n", " print(\"The size of the hamiltonian is %d by %d\" % (N, N))\n", " \n", " return H\n", "\n", " def set_weights(self, weights):\n", " self.weights = weights\n", "\n", " @timer\n", " def train(self, X, y):\n", "\n", " H = self._get_hamiltonian(X, y)\n", "\n", " N = H.shape[0]\n", "\n", " qubo_json = {\n", " \"file_name\": \"qboost.json\",\n", " \"file_config\": {\n", " \"qubo\": {\"data\": H, \"num_variables\": N},\n", " } \n", " }\n", " \n", " job_json = {\n", " \"job_name\": \"qboost_classifier\",\n", " \"job_tags\": [\"qboost\"],\n", " \"params\": {\n", " \"device_type\": \"eqc1\", \n", " \"num_samples\": self.num_eqc_samples,\n", " \"alpha\": self.alpha,\n", " },\n", " }\n", "\n", " # Solve the optimization problem\n", " #qci = QciClient()\n", "\n", " response_json = qci.upload_file(file=qubo_json)\n", " qubo_file_id = response_json[\"file_id\"]\n", " \n", " # Setup job json\n", " job_params = {\n", " \"device_type\": \"dirac-1\", \n", " \"alpha\": self.alpha, \n", " \"num_samples\": self.num_eqc_samples,\n", " \n", " }\n", " job_json = qci.build_job_body(\n", " job_type=\"sample-qubo\", \n", " job_params=job_params,\n", " qubo_file_id=qubo_file_id,\n", " job_name=\"tutorial_eqc1\",\n", " job_tags=[\"tutorial_eqc1\"],\n", " )\n", " print(job_json)\n", "\n", " # Run the job\n", " job_response_json = qci.process_job(\n", " job_body=job_json,\n", " )\n", "\n", " print(job_response_json)\n", "\n", " results = job_response_json[\"results\"]\n", " energies = results[\"energies\"]\n", " samples = results[\"solutions\"]\n", " \n", " if True:\n", " print(\"Energies:\", energies)\n", "\n", " # The sample solutions are sorted by energy\n", " sol = samples[0]\n", "\n", " assert len(sol) == N, \"Inconsistent solution size!\"\n", "\n", " self.weights = np.array(sol)\n", "\n", " return\n", "\n", " def predict(self, X):\n", "\n", " assert self.weights is not None, \"Model is not trained!\"\n", " assert self.h_list is not None, \"Model is not trained!\"\n", "\n", " assert len(self.weights) == len(self.h_list), \"Inconsisent sizes!\"\n", "\n", " N = len(self.weights)\n", " tmp_vals = np.zeros(shape=(X.shape[0]), dtype=\"d\")\n", "\n", " fct = sum(self.weights)\n", " if fct > 0:\n", " fct = 1.0 / fct\n", "\n", " for i in range(N):\n", " tmp_vals += self.weights[i] * self.h_list[i].predict(X)\n", "\n", " tmp_vals = fct * tmp_vals\n", "\n", " pred_vals = np.sign(tmp_vals - self.theta)\n", "\n", " for i in range(len(pred_vals)):\n", " if pred_vals[i] == 0:\n", " pred_vals[i] = -1.0\n", "\n", " return pred_vals\n", "\n", " def save_weights(self, file_name):\n", " np.save(file_name, self.weights)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above class can then be used to build a classifier using the IRIS dataset. We have used 80\\% of the data for training and the rest is used for testing." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Runtime of _build_weak_classifiers_dct: 0.01 seconds!\n", "The size of the hamiltonian is 14 by 14\n", "Runtime of _get_hamiltonian: 0.01 seconds!\n", "{'job_submission': {'problem_config': {'quadratic_unconstrained_binary_optimization': {'qubo_file_id': '663bdc3698263204a3657540'}}, 'device_config': {'dirac-1': {'num_samples': 10}}, 'job_name': 'tutorial_eqc1', 'job_tags': ['tutorial_eqc1']}}\n", "2024-05-08 13:10:30 - Dirac allocation balance = 0 s (unmetered)\n", "2024-05-08 13:10:30 - Job submitted: job_id='663bdc36d448b017e54f94c1'\n", "2024-05-08 13:10:30 - QUEUED\n", "2024-05-08 13:10:33 - RUNNING\n", "2024-05-08 13:13:26 - COMPLETED\n", "2024-05-08 13:13:29 - Dirac allocation balance = 0 s (unmetered)\n", "{'job_info': {'job_id': '663bdc36d448b017e54f94c1', 'job_submission': {'job_name': 'tutorial_eqc1', 'job_tags': ['tutorial_eqc1'], 'problem_config': {'quadratic_unconstrained_binary_optimization': {'qubo_file_id': '663bdc3698263204a3657540'}}, 'device_config': {'dirac-1': {'num_samples': 10}}}, 'job_status': {'submitted_at_rfc3339nano': '2024-05-08T20:10:30.733Z', 'queued_at_rfc3339nano': '2024-05-08T20:10:30.734Z', 'running_at_rfc3339nano': '2024-05-08T20:10:31.447Z', 'completed_at_rfc3339nano': '2024-05-08T20:13:24.922Z'}, 'job_result': {'file_id': '663bdce498263204a3657542', 'device_usage_s': 136}}, 'status': 'COMPLETED', 'results': {'counts': [10], 'energies': [-105.97958903409997], 'solutions': [[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}}\n", "Energies: [-105.97958903409997]\n", "Runtime of train: 219.14 seconds!\n" ] } ], "source": [ "import sys\n", "from collections import Counter\n", "import numpy as np\n", "import pandas as pd\n", "from sklearn import datasets\n", "from sklearn.model_selection import train_test_split\n", "\n", "# Some parameters\n", "TEST_SIZE = 0.2\n", "LAMBDA_COEF = 1.0\n", "\n", "# Read dataset\n", "iris = datasets.load_iris()\n", "X = iris.data\n", "y = iris.target\n", "\n", "for i in range(len(y)):\n", " if y[i] == 0:\n", " y[i] = -1\n", " elif y[i] == 2:\n", " y[i] = 1\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(\n", " X, y, test_size=TEST_SIZE, random_state=42,\n", ")\n", "\n", "obj = QBoost(lambda_coef=LAMBDA_COEF, num_eqc_samples=10, alpha=1.0, mode=\"dct\")\n", "\n", "obj.train(X_train, y_train)\n", "\n", "y_train_prd = obj.predict(X_train)\n", "y_test_prd = obj.predict(X_test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results show a 100\\% accuracy, recall, and precision of the classifier." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Train precision: 1.0\n", "Train recall: 1.0\n", "Train accuracy: 1.0\n" ] }, { "data": { "image/png": "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K2lnAAAAM1QiAAAwQTuDEAEAgBFCBO0MAABghkoEAAAGHA4qEYQIAABM0M4gRAAAYIQQwZ4IAABghkoEAAAGeHcGIQIAADOECNoZAADADJUIAABM8OoMQgQAACbYE0E7AwAAGKISAQCACSoRhAgAAIywJ4J2BgAAMEMlAgAAAy1lY+Wbb76pRx55pMHxt99+WzExMZKkiooKLV68WCtXrlR+fr4CAwM1bNgwzZw5U2FhYS7PTYgAAMBEC2ln7Nq1S5J08803KzAwsN74ueeeK0my2WyaPn26Nm7cqN/97neKj4/Xnj17lJaWpo8++khpaWkKDw93aW5CBAAABlpKJeKrr76Sr6+v7r33Xnl6ejZ4XVpamjZu3KgxY8boscceq/1+2bJleuCBB/Too4/q+eefd2lu9kQAANBKWa1W7d69Wz179jxpgJCklJQUeXh46M4776zzfUJCgnr27KmMjAwVFha6ND8hAgAAE/ZG+vwK3377rSorK9WnT5+TXvf999/ru+++U8+ePdW+fft643FxcbLb7dq6datL89POAADAgKMF7Imo2Q9hsVh055136tNPP9XRo0fVpUsXjR8/XomJifLw8NDevXslSV26dHF6n86dO0uScnNzXZqfEAEAgBvFx8efdDwzM7PBsa+++kqStHTpUg0aNEjXXHONDh8+rI8++kgPP/ywsrOz9cwzz6ioqEiSFBwc7PQ+Nd+XlJS4tHZCBAAAJlpAJcJisejcc89VUlKSRo8eXfv94cOHNXnyZK1Zs0YXX3yxfHx8JKn2//6vmu8rKipcmp8QAQCAgcZqZ5ys0nAqDzzwgB544IF637dv31733XefpkyZonfffVc333yzpOqNmM7UfB8QEODS/GysBADgDNSvXz9JUl5e3inbFceOHZMkBQUFuTQHlQgAAEy4uZ1RWVmpr776ShUVFRo4cGC98fLyckmSr6+vunfvLqk6UDizf/9+SVJkZKRLayBEAABgwN2nMyorK3XjjTfK4XAoKytL7dq1qzP+ySefSJL69++vsLAwde3aVTk5OTpy5Ei9a7OysuTh4aEBAwa4tAbaGQAAtEIBAQG6/PLLZbfb9cQTT8hu/znV5OXl6amnnpKHh4cmT54sqfqhUjabTU8++aQcjp+ftrls2TJ98803GjlypMvvz6ASAQCAAXdXIiRpzpw52rFjh1at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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Test precision: 1.0\n", "Test recall: 1.0\n", "Test accuracy: 1.0\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from sklearn.metrics import confusion_matrix, precision_score, recall_score, accuracy_score\n", "import matplotlib.pyplot as plt\n", "import seaborn as sn\n", "\n", "print(\n", " \"Train precision:\",\n", " precision_score(y_train, y_train_prd, labels=[-1, 1], pos_label=1),\n", ")\n", "print(\n", " \"Train recall:\",\n", " recall_score(y_train, y_train_prd, labels=[-1, 1], pos_label=1),\n", ")\n", "print(\n", " \"Train accuracy:\",\n", " accuracy_score(y_train, y_train_prd),\n", ")\n", "\n", "sn.set(font_scale=1.4)\n", "train_conf_mat = confusion_matrix(y_train, y_train_prd, labels=[-1, 1])\n", "sn.heatmap(train_conf_mat, annot=True, annot_kws={\"size\": 16})\n", "plt.show()\n", "\n", "print(\n", " \"Test precision:\",\n", " precision_score(y_test, y_test_prd, labels=[-1, 1], pos_label=1),\n", ")\n", "print(\n", " \"Test recall:\",\n", " recall_score(y_test, y_test_prd, labels=[-1, 1], pos_label=1),\n", ")\n", "print(\n", " \"Test accuracy:\",\n", " accuracy_score(y_test, y_test_prd),\n", ")\n", "\n", "test_conf_mat = confusion_matrix(y_test, y_test_prd, labels=[-1, 1])\n", "sn.heatmap(test_conf_mat, annot=True, annot_kws={\"size\": 16})\n", "plt.show()" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.7" } }, "nbformat": 4, "nbformat_minor": 4 }