Repository: dscc-admin-ch/lomas (language: python)

Issue:
Wip 260 dpl pca
Thanks @lamd91 for the fix of version 


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index f6a67b4a..c6cfc265 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
 server/lomas_server/tests/test_data/local_db_file_*
 server/lomas_server/.streamlit/*
+server/venv/*
 client/notebooks/personal_notebooks/*
+client/configs/lab/*
+client/venv/*
 
 # Byte-compiled / optimized / DLL files
 __pycache__/
diff --git a/client/Dockerfile b/client/Dockerfile
index 9a7f09e5..979f4875 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -5,7 +5,7 @@ WORKDIR /code
 COPY ./requirements.txt /code/requirements.txt
 RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
 
-COPY ./lomas_client/ /code/lomas_client
+COPY ./lomas_client/ /code/lomas_client/
 COPY ./setup.cfg /code/setup.cfg
 COPY ./setup.py /code/setup.py
 COPY ./README.md /code/README.md
@@ -19,7 +19,8 @@ FROM lomas_client_base AS lomas_client
 COPY ./notebooks/images/ /code/notebooks/images/
 COPY ./LICENSE /code/LICENSE
 COPY ./notebooks/Demo_Client_Notebook.ipynb /code/notebooks/Demo_Client_Notebook.ipynb
+COPY ./notebooks/Demo_Client_Notebook_DiffPrivLib.ipynb /code/notebooks/Demo_Client_Notebook_DiffPrivLib.ipynb
 
-RUN pip install /code/
+RUN pip install -e /code/
 
 CMD ["jupyter", "notebook", "--ip", "0.0.0.0", "--no-browser", "--allow-root"]
\ No newline at end of file
diff --git a/client/lomas_client/client.py b/client/lomas_client/client.py
index 2c937c99..f136b9a9 100644
--- a/client/lomas_client/client.py
+++ b/client/lomas_client/client.py
@@ -1,3 +1,4 @@
+import base64
 import json
 from enum import StrEnum
 from io import StringIO
@@ -6,8 +7,10 @@
 import opendp as dp
 import pandas as pd
 import requests
+from diffprivlib_logger import serialise_pipeline
 from opendp.mod import enable_features
 from opendp_logger import enable_logging, make_load_json
+from sklearn.pipeline import Pipeline
 
 # Opendp_logger
 enable_logging()
@@ -16,6 +19,7 @@
 # Client constants: may be modified
 DUMMY_NB_ROWS = 100
 DUMMY_SEED = 42
+HTTP_200_OK = 200
 
 
 class DPLibraries(StrEnum):
@@ -25,6 +29,7 @@ class DPLibraries(StrEnum):
 
     SMARTNOISE_SQL = "smartnoise_sql"
     OPENDP = "opendp"
+    DIFFPRIVLIB = "diffprivlib"
 
 
 def error_message(res: requests.Response) -> str:
@@ -70,7 +75,7 @@ def get_dataset_metadata(
         res = self._exec(
             "get_dataset_metadata", {"dataset_name": self.dataset_name}
         )
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             data = res.content.decode("utf8")
             metadata = json.loads(data)
             return metadata
@@ -106,7 +111,7 @@ def get_dummy_dataset(
             },
         )
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             data = res.content.decode("utf8")
             df = pd.read_csv(StringIO(data))
             return df
@@ -171,7 +176,7 @@ def smartnoise_query(
 
         res = self._exec(endpoint, body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             data = res.content.decode("utf8")
             response_dict = json.loads(data)
             response_dict["query_response"] = pd.DataFrame.from_dict(
@@ -213,7 +218,7 @@ def estimate_smartnoise_cost(
         }
         res = self._exec("estimate_smartnoise_cost", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             return json.loads(res.content.decode("utf8"))
         print(error_message(res))
         return None
@@ -263,20 +268,9 @@ def opendp_query(
             endpoint = "opendp_query"
 
         res = self._exec(endpoint, body_json)
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             data = res.content.decode("utf8")
             response_dict = json.loads(data)
-
-            # Opendp outputs can be single numbers or dataframes,
-            # we handle the latter here.
-            # This is a hack for now, maybe use parquet to send results over.
-            # if isinstance(response_dict["query_response"], str):
-            #     raise Exception("Not implemented: should not return dataframes")
-            # Note: leaving this here. Support for opendp_polars
-            # response_dict["query_response"] = polars.read_json(
-            #    StringIO(response_dict["query_response"])
-            # )
-
             return response_dict
 
         print(error_message(res))
@@ -311,12 +305,142 @@ def estimate_opendp_cost(
         }
         res = self._exec("estimate_opendp_cost", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             return json.loads(res.content.decode("utf8"))
 
         print(error_message(res))
         return None
 
+    def diffprivlib_query(
+        self,
+        pipeline: Pipeline,
+        feature_columns: List[str],
+        target_columns: Optional[List[str]] = None,
+        test_size: float = 0.2,
+        test_train_split_seed: int = 1,
+        imputer_strategy: str = "drop",
+        dummy: bool = False,
+        nb_rows: int = DUMMY_NB_ROWS,
+        seed: int = DUMMY_SEED,
+    ) -> Pipeline:
+        """This function trains a DiffPrivLib pipeline on the sensitive data
+        and return a trained Pipeline.
+
+        Args:
+            pipeline (sklearn.pipeline): DiffPrivLib pipeline with three conditions:
+                - The pipeline MUST start with a `models.StandardScaler`.
+                  Otherwise a PrivacyLeakWarning is raised by DiffPrivLib library and
+                  is treated as an error in lomas server.
+                - `random_state` fields can only be int (`RandomState` will not work).
+                - `accountant` fields must be None.
+                Note: as in DiffPrivLib, avoid any DiffprivlibCompatibilityWarning
+                to ensure that the pipeline does what is intended.
+            feature_columns (list[str]): the list of feature column to train
+            target_columns (list[str], optional): the list of target column to predict \
+                May be None for certain models.
+            test_size (float, optional): proportion of the test set \
+                Defaults to 0.2.
+            test_train_split_seed (int, optional): seed for random train test split \
+                Defaults to 1.
+            imputer_strategy (str, optional): imputation strategy. Defaults to "drop".
+                "drop": will drop all rows with missing values
+                "mean": will replace values by the mean of the column values
+                "median": will replace values by the median of the column values
+                "most_frequent": : will replace values by the most frequent values
+            dummy (bool, optional): Whether to use a dummy dataset. Defaults to False.
+            nb_rows (int, optional): The number of rows in the dummy dataset.\
+                Defaults to DUMMY_NB_ROWS.
+            seed (int, optional): The random seed for generating the dummy dataset.\
+                Defaults to DUMMY_SEED.
+
+        Returns:
+            Optional[Pipeline]: A trained DiffPrivLip pipeline
+        """
+        dpl_json = serialise_pipeline(pipeline)
+        body_json = {
+            "dataset_name": self.dataset_name,
+            "diffprivlib_json": dpl_json,
+            "feature_columns": feature_columns,
+            "target_columns": target_columns,
+            "test_size": test_size,
+            "test_train_split_seed": test_train_split_seed,
+            "imputer_strategy": imputer_strategy,
+        }
+        if dummy:
+            endpoint = "dummy_diffprivlib_query"
+            body_json["dummy_nb_rows"] = nb_rows
+            body_json["dummy_seed"] = seed
+        else:
+            endpoint = "diffprivlib_query"
+
+        res = self._exec(endpoint, body_json)
+        if res.status_code == HTTP_200_OK:
+            response = res.json()
+            model = base64.b64decode(response["query_response"]["model"])
+            response["query_response"]["model"] = json.loads(model)
+            return response
+        print(
+            f"Error while processing DiffPrivLib request in server \
+                status code: {res.status_code} message: {res.text}"
+        )
+        return res.text
+
+    def estimate_diffprivlib_cost(
+        self,
+        pipeline: Pipeline,
+        feature_columns: List[str] = [""],
+        target_columns: List[str] = [""],
+        test_size: float = 0.2,
+        test_train_split_seed: int = 1,
+        imputer_strategy: str = "drop",
+    ) -> dict:
+        """This function estimates the cost of executing a DiffPrivLib query.
+
+        Args:
+            pipeline (sklearn.pipeline): DiffPrivLib pipeline with three conditions:
+                - The pipeline MUST start with a `models.StandardScaler`.
+                  Otherwise a PrivacyLeakWarning is raised by DiffPrivLib library and
+                  is treated as an error in lomas server.
+                - `random_state` fields can only be int (`RandomState` will not work).
+                - `accountant` fields must be None.
+                Note: as in DiffPrivLib, avoid any DiffprivlibCompatibilityWarning
+                to ensure that the pipeline does what is intended.
+            feature_columns (list[str]): the list of feature column to train
+            target_columns (list[str], optional): the list of target column to predict \
+                May be None for certain models.
+            test_size (float, optional): proportion of the test set \
+                Defaults to 0.2.
+            test_train_split_seed (int, optional): seed for random train test split \
+                Defaults to 1.
+            imputer_strategy (str, optional): imputation strategy. Defaults to "drop".
+                "drop": will drop all rows with missing values
+                "mean": will replace values by the mean of the column values
+                "median": will replace values by the median of the column values
+                "most_frequent": : will replace values by the most frequent values
+
+        Returns:
+            Optional[dict[str, float]]: A dictionary containing the estimated cost.
+        """
+        dpl_json = serialise_pipeline(pipeline)
+        body_json = {
+            "dataset_name": self.dataset_name,
+            "diffprivlib_json": dpl_json,
+            "feature_columns": feature_columns,
+            "target_columns": target_columns,
+            "test_size": test_size,
+            "test_train_split_seed": test_train_split_seed,
+            "imputer_strategy": imputer_strategy,
+        }
+        res = self._exec("estimate_diffprivlib_cost", body_json)
+
+        if res.status_code == HTTP_200_OK:
+            return json.loads(res.content.decode("utf8"))
+        print(
+            f"Error while executing provided query in server:\n"
+            f"status code: {res.status_code} message: {res.text}"
+        )
+        return res.text
+
     def get_initial_budget(self) -> Optional[dict[str, float]]:
         """This function retrieves the initial budget.
 
@@ -328,7 +452,7 @@ def get_initial_budget(self) -> Optional[dict[str, float]]:
         }
         res = self._exec("get_initial_budget", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             return json.loads(res.content.decode("utf8"))
 
         print(error_message(res))
@@ -345,7 +469,7 @@ def get_total_spent_budget(self) -> Optional[dict[str, float]]:
         }
         res = self._exec("get_total_spent_budget", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             return json.loads(res.content.decode("utf8"))
 
         print(error_message(res))
@@ -362,7 +486,7 @@ def get_remaining_budget(self) -> Optional[dict[str, float]]:
         }
         res = self._exec("get_remaining_budget", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             return json.loads(res.content.decode("utf8"))
 
         print(error_message(res))
@@ -383,7 +507,7 @@ def get_previous_queries(self) -> Optional[List[dict]]:
         }
         res = self._exec("get_previous_queries", body_json)
 
-        if res.status_code == 200:
+        if res.status_code == HTTP_200_OK:
             queries = json.loads(res.content.decode("utf8"))[
                 "previous_queries"
             ]
@@ -401,6 +525,13 @@ def get_previous_queries(self) -> Optional[List[dict]]:
                             query["client_input"]["opendp_json"]
                         )
                         query["client_input"]["opendp_json"] = opdp_query
+                    case DPLibraries.DIFFPRIVLIB:
+                        model = base64.b64decode(
+                            query["response"]["query_response"]["model"]
+                        )
+                        query["response"]["query_response"]["model"] = (
+                            json.loads(model)
+                        )
                     case _:
                         raise ValueError(
                             "Cannot deserialise unknown query type:"
diff --git a/client/notebooks/Demo_Client_Notebook.ipynb b/client/notebooks/Demo_Client_Notebook.ipynb
index 46d87c0c..af501f3d 100644
--- a/client/notebooks/Demo_Client_Notebook.ipynb
+++ b/client/notebooks/Demo_Client_Notebook.ipynb
@@ -74,17 +74,53 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 4,
    "id": "28fbdd79-8c15-49a9-bcf9-fcdeac09d2b5",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Collecting lomas-client\n",
+      "  Downloading lomas_client-0.1.0.tar.gz (8.0 kB)\n",
+      "  Installing build dependencies ... \u001b[?25ldone\n",
+      "\u001b[?25h  Getting requirements to build wheel ... \u001b[?25ldone\n",
+      "\u001b[?25h  Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n",
+      "\u001b[?25hRequirement already satisfied: opendp==0.8.0 in /usr/local/lib/python3.11/site-packages (from lomas-client) (0.8.0)\n",
+      "Requirement already satisfied: opendp-logger==0.3.0 in /usr/local/lib/python3.11/site-packages (from lomas-client) (0.3.0)\n",
+      "Requirement already satisfied: numpy==1.26.4 in /usr/local/lib/python3.11/site-packages (from lomas-client) (1.26.4)\n",
+      "Requirement already satisfied: requests==2.32.0 in /usr/local/lib/python3.11/site-packages (from lomas-client) (2.32.0)\n",
+      "Requirement already satisfied: pandas==2.2.2 in /usr/local/lib/python3.11/site-packages (from lomas-client) (2.2.2)\n",
+      "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/site-packages (from pandas==2.2.2->lomas-client) (2.9.0.post0)\n",
+      "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/site-packages (from pandas==2.2.2->lomas-client) (2024.1)\n",
+      "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/site-packages (from pandas==2.2.2->lomas-client) (2024.1)\n",
+      "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests==2.32.0->lomas-client) (3.3.2)\n",
+      "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests==2.32.0->lomas-client) (3.7)\n",
+      "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests==2.32.0->lomas-client) (2.2.2)\n",
+      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests==2.32.0->lomas-client) (2024.6.2)\n",
+      "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas==2.2.2->lomas-client) (1.16.0)\n",
+      "Building wheels for collected packages: lomas-client\n",
+      "  Building wheel for lomas-client (pyproject.toml) ... \u001b[?25ldone\n",
+      "\u001b[?25h  Created wheel for lomas-client: filename=lomas_client-0.1.0-py3-none-any.whl size=7982 sha256=81a9dd4be09f50b39028e877a467f2ba48accfb55ffeea655a491dc1f0d28ccb\n",
+      "  Stored in directory: /root/.cache/pip/wheels/36/0b/91/29d8725d273c025f2a67ea520ca72b23e56eb47ee032bdfdeb\n",
+      "Successfully built lomas-client\n",
+      "Installing collected packages: lomas-client\n",
+      "Successfully installed lomas-client-0.1.0\n",
+      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
+      "\u001b[0m\n",
+      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.1\u001b[0m\n",
+      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
+     ]
+    }
+   ],
    "source": [
-    "#!pip install lomas-client"
+    "!pip install lomas-client"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 13,
    "id": "6fb569fc",
    "metadata": {},
    "outputs": [],
@@ -112,7 +148,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 6,
    "id": "941991f7",
    "metadata": {},
    "outputs": [],
@@ -153,7 +189,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 7,
    "id": "0fdebac9-57fc-4410-878b-5a77425af634",
    "metadata": {},
    "outputs": [
@@ -178,7 +214,7 @@
        "   'categories': ['MALE', 'FEMALE']}}}"
       ]
      },
-     "execution_count": 5,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -213,7 +249,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 8,
    "id": "01f4365a",
    "metadata": {},
    "outputs": [],
@@ -224,7 +260,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 9,
    "id": "3f553b29",
    "metadata": {},
    "outputs": [
@@ -321,8 +357,8 @@
        "</div>"
       ],
       "text/plain": [
-       "     species     island  bill_length_mm  bill_depth_mm  flipper_length_mm   \n",
-       "0     Gentoo     Biscoe       49.208473      16.117959         190.125950  \\\n",
+       "     species     island  bill_length_mm  bill_depth_mm  flipper_length_mm  \\\n",
+       "0     Gentoo     Biscoe       49.208473      16.117959         190.125950   \n",
        "1     Gentoo  Torgersen       55.031628      19.963435         242.929142   \n",
        "2  Chinstrap  Torgersen       51.096718      16.777518         159.961493   \n",
        "3     Adelie     Biscoe       49.070911      14.796037         244.530153   \n",
@@ -336,7 +372,7 @@
        "4  5036.246870  FEMALE  "
       ]
      },
-     "execution_count": 7,
+     "execution_count": 9,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -391,7 +427,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 10,
    "id": "3946425d",
    "metadata": {},
    "outputs": [],
@@ -404,7 +440,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 11,
    "id": "90cf2a6d",
    "metadata": {},
    "outputs": [],
@@ -422,7 +458,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 12,
    "id": "a30f277e",
    "metadata": {},
    "outputs": [
@@ -430,7 +466,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Average bill length in remote dummy: 47.52mm.\n",
+      "Average bill length in remote dummy: 47.51mm.\n",
       "Number of rows in remote dummy: 199.\n"
      ]
     }
diff --git a/client/notebooks/Demo_Client_Notebook_DiffPrivLib.ipynb b/client/notebooks/Demo_Client_Notebook_DiffPrivLib.ipynb
new file mode 100644
index 00000000..0c4c779b
--- /dev/null
+++ b/client/notebooks/Demo_Client_Notebook_DiffPrivLib.ipynb
@@ -0,0 +1,3257 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "3f18d338",
+   "metadata": {},
+   "source": [
+    "# Secure Data Disclosure: Client side"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1582a2ae",
+   "metadata": {},
+   "source": [
+    "This notebook showcases how researcher could use the Secure Data Disclosure system. It explains the different functionnalities provided by the `fso-sdd-demo` client library to interact with the secure server.\n",
+    "\n",
+    "The secure data are never visible by researchers. They can only access to differentially private responses via queries to the server.\n",
+    "\n",
+    "Each user has access to one or multiple projects and for each dataset has a limited budget with $\\epsilon$ and $\\delta$ values."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "23bb4f13-7800-41b2-b429-68c2d02243d0",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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P/i1WfzOMjsFhZBuOCe6juo8qnMqAABR0XeGX/z8D3YMQDEioEOr+fPfsEOJXnlloR1CsXhr4VLnJj5fIZ1+nYsHc012CoBC+MtfnkqozpoOBUAP/flJO4Z8Uxiabjjn9ecHn8j7DysAACSj7/b8YAQULwI6OJ5/boFzkxdEJTlU/bWcqAixqlO6f1FUb82WbuLvueePdgxAOXh70bt2KE5V2z/xiQ8lnK++dPvN5ripE+0z4nSephRdYSx6K3EfqcrQNde833z7O//Suo++9n8/by686Ez7jDhdY59/foEdAwAge/qe4P2O4P3TdWnOB893mlrwWvT2UjsEoNjQBl0BlEIbdKoyqVJZLt38ecd1o3HVVZfYsY4rlrbfikW620PPW7QouNRGPtosKNR+og264kIbdOEKdR73fyb05TqsDdB//ep3EkrbJTsX6DM9b97LZtmy1a0/xtTXD4md6yeZc889PWU7o25nCMuXrUp4T81j3PjR5uyzZwV2hJBuGyupzgXex3U++sAHzzN/e3xe63VKy3HBBbPN6WfMdMZFpdWeeur52HOWtC6zblLGjx9pZs8+qcPnTf86KUD1vq+XvypssmuptvGjj8519pFbelnhn5b34ovPCu1owr88Ombuv/9hszC2bbSv3XmoiQSXttfXv/FFO5ZIP9j98pdtVaD0feALX7jRjrXp6DHVkX2ZD7RBB5Q+2qBL7zu6/1qU7DUdvRaJ+9pknVulao8122u3e21qbNyUUBPIXYewtrz921XX62FDB5u//W1e63LoenjBhbMT2vDL5fej0aNHtFt3zWf27Fl522733/+IefyxZ+1YfDlWrFhj5se+M2j7aR5TY+t91VWXtl7Xw7axlrW+oa7g7aVnqtjboCOgK4BiD+j0gf7SF79px+InH32QvScpndS+852v2LHk9OV+wSsLW79suydEfbBXrVqT9KISdHLUF/1UJyv3pOSeTETzPiP2BT/oS346Fzf/TYQed09I3hsSd3t15ESUznJ4qYTMt+/8mR2LO2nW8YEdOLjbZMXy1QkXANH7BPXm5F+eIP5lDLsYuxeFZBfjoJtyraNu1txt7F4YZp95UtJGbTuyvl7F8KUiagR04Qp1Hle7j94vOzq/XHHFhVn1AvrjH/82IZzx0zF67TWXhQYiqgqj6pnuOS+MSpH5PyP5COh0/hfv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",
+      "text/plain": [
+       "<IPython.core.display.Image object>"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {
+      "image/png": {
+       "width": 800
+      }
+     },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from IPython.display import Image\n",
+    "Image(filename=\"images/image_demo_client.png\", width=800)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5b73135c",
+   "metadata": {},
+   "source": [
+    "🐧🐧🐧\n",
+    "In this notebook the researcher is a penguin researcher named Dr. Antarctica. She aims to do a grounbdbreaking research on various penguins dimensions.\n",
+    "\n",
+    "Therefore, the powerful queen Icerbegina 👑 had the data collected. But in order to get the penguins to agree to participate she promised them that no one would be able to look at the data and that no one would be able to guess the bill width of any specific penguin (which is very sensitive information) from the data. Nobody! Not even the researchers. The queen hence stored the data on the Secure Data Disclosure Server and only gave a small budget to Dr. Antarctica.\n",
+    "\n",
+    "This is not a problem for Dr. Antarctica as she does not need to see the data to make statistics thanks to the Secure Data Disclosure Client library `fso-sdd-demo`. \n",
+    "🐧🐧🐧"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "01ae30d2",
+   "metadata": {},
+   "source": [
+    "## Step 1: Install the library\n",
+    "To interact with the secure server on which the data is stored, Dr.Antartica first needs to install the library `fso-sdd-demo` on her local developping environment. \n",
+    "\n",
+    "It can be installed via the pip command:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "36d508bf-6cc3-4034-8e11-fffe858552f9",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import sys\n",
+    "import os\n",
+    "sys.path.append(os.path.abspath(os.path.join('..')))\n",
+    "# !pip install lomas_client"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "9535e92e-620e-4df4-92dd-4ea2c653e4ab",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from lomas_client import Client\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9c63718b",
+   "metadata": {},
+   "source": [
+    "## Step 2: Initialise the client\n",
+    "\n",
+    "Once the library is installed, a Client object must be created. It is responsible for sending sending requests to the server and processing responses in the local environment. It enables a seamless interaction with the server. \n",
+    "\n",
+    "To create the client, Dr. Antartica needs to give it a few parameters:\n",
+    "- a url: the root application endpoint to the remote secure server.\n",
+    "- user_name: her name as registered in the database (Dr. Alice Antartica)\n",
+    "- dataset_name: the name of the dataset that she wants to query (PENGUIN)\n",
+    "\n",
+    "She will only be able to query on the real dataset if the queen Icergina has previously made her an account in the database, given her access to the PENGUIN dataset and has given her some epsilon and delta credit (as is done in the Admin Notebook for Users and Datasets management)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "941991f7",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "APP_URL = \"http://lomas_server\"\n",
+    "USER_NAME = \"Dr. Antartica\"\n",
+    "DATASET_NAME = \"PENGUIN\"\n",
+    "client = Client(url=APP_URL, user_name = USER_NAME, dataset_name = DATASET_NAME)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0ec400c8",
+   "metadata": {},
+   "source": [
+    "And that's it for the preparation. She is now ready to use the various functionnalities offered by `fso_sdd_demo`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9b9a5f13",
+   "metadata": {},
+   "source": [
+    "## Step 3: Metadata and dummy dataset"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c7cb5531",
+   "metadata": {},
+   "source": [
+    "### Getting dataset metadata\n",
+    "\n",
+    "Dr. Antartica has never seen the data and as a first step to understand what is available to her, she would like to check the metadata of the dataset. Therefore, she just needs to call the `get_dataset_metadata()` function of the client. As this is public information, this does not cost any budget.\n",
+    "\n",
+    "This function returns metadata information in a format based on [SmartnoiseSQL dictionary format](https://docs.smartnoise.org/sql/metadata.html#dictionary-format), where among other, there is information about all the available columns, their type, bound values (see Smartnoise page for more details). Any metadata is required for Smartnoise-SQL is also required here and additional information such that the different categories in a string type column column can be added."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "0fdebac9-57fc-4410-878b-5a77425af634",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'max_ids': 1,\n",
+       " 'row_privacy': True,\n",
+       " 'censor_dims': False,\n",
+       " 'columns': {'species': {'type': 'string',\n",
+       "   'cardinality': 3,\n",
+       "   'categories': ['Adelie', 'Chinstrap', 'Gentoo']},\n",
+       "  'island': {'type': 'string',\n",
+       "   'cardinality': 3,\n",
+       "   'categories': ['Torgersen', 'Biscoe', 'Dream']},\n",
+       "  'bill_length_mm': {'type': 'float', 'lower': 30.0, 'upper': 65.0},\n",
+       "  'bill_depth_mm': {'type': 'float', 'lower': 13.0, 'upper': 23.0},\n",
+       "  'flipper_length_mm': {'type': 'float', 'lower': 150.0, 'upper': 250.0},\n",
+       "  'body_mass_g': {'type': 'float', 'lower': 2000.0, 'upper': 7000.0},\n",
+       "  'sex': {'type': 'string',\n",
+       "   'cardinality': 2,\n",
+       "   'categories': ['MALE', 'FEMALE']}}}"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "penguin_metadata = client.get_dataset_metadata()\n",
+    "penguin_metadata"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d338ed96",
+   "metadata": {},
+   "source": [
+    "Based on this Dr. Antartica knows that there are 7 columns, 3 of string type (species, island, sex) with their associated categories (i.e. the species column has 3 possibilities: 'Adelie', 'Chinstrap', 'Gentoo') and 4 of float type (bill length, bill depth, flipper length and body mass) with their associated bounds (i.e. the body mass of penguin ranges from 2000 to 7000 gramms). She also knows based on the field `max_ids: 1` that each penguin can only be once in the dataset and on the field `row_privacy: True` that each row represents a single penguin. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5a3c899d",
+   "metadata": {},
+   "source": [
+    "### Get a dummy dataset\n",
+    "\n",
+    "Now, that she has seen and understood the metadata, she wants to get an even better understanding of the dataset (but is still not able to see it). A solution to have an idea of what the dataset looks like it to create a dummy dataset. \n",
+    "\n",
+    "Based on the public metadata of the dataset, a random dataframe can be created created. By default, there will be 100 rows and the seed is set to 42 to ensure reproducibility, but these 2 variables can be changed to obtain different dummy datasets.\n",
+    "Getting a dummy dataset does not affect the budget as there is no differential privacy here. It is not a synthetic dataset and all that could be learn here is already present in the public metadata (it is created randomly on the fly based on the metadata).\n",
+    "\n",
+    "Dr. Antartica first create a dummy dataset with 200 rows and chooses a seed of 0."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "01f4365a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NB_ROWS = 200\n",
+    "SEED = 0"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "3f553b29",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "(200, 7)\n"
+     ]
+    },
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>species</th>\n",
+       "      <th>island</th>\n",
+       "      <th>bill_length_mm</th>\n",
+       "      <th>bill_depth_mm</th>\n",
+       "      <th>flipper_length_mm</th>\n",
+       "      <th>body_mass_g</th>\n",
+       "      <th>sex</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>Gentoo</td>\n",
+       "      <td>Biscoe</td>\n",
+       "      <td>49.208473</td>\n",
+       "      <td>16.117959</td>\n",
+       "      <td>190.125950</td>\n",
+       "      <td>2873.291927</td>\n",
+       "      <td>FEMALE</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>Gentoo</td>\n",
+       "      <td>Torgersen</td>\n",
+       "      <td>55.031628</td>\n",
+       "      <td>19.963435</td>\n",
+       "      <td>242.929142</td>\n",
+       "      <td>3639.940005</td>\n",
+       "      <td>FEMALE</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>Chinstrap</td>\n",
+       "      <td>Torgersen</td>\n",
+       "      <td>51.096718</td>\n",
+       "      <td>16.777518</td>\n",
+       "      <td>159.961493</td>\n",
+       "      <td>5401.743330</td>\n",
+       "      <td>MALE</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>Adelie</td>\n",
+       "      <td>Biscoe</td>\n",
+       "      <td>49.070911</td>\n",
+       "      <td>14.796037</td>\n",
+       "      <td>244.530153</td>\n",
+       "      <td>2316.038092</td>\n",
+       "      <td>MALE</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>Chinstrap</td>\n",
+       "      <td>Biscoe</td>\n",
+       "      <td>44.827918</td>\n",
+       "      <td>13.246787</td>\n",
+       "      <td>236.948853</td>\n",
+       "      <td>5036.246870</td>\n",
+       "      <td>FEMALE</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "     species     island  bill_length_mm  bill_depth_mm  flipper_length_mm  \\\n",
+       "0     Gentoo     Biscoe       49.208473      16.117959         190.125950   \n",
+       "1     Gentoo  Torgersen       55.031628      19.963435         242.929142   \n",
+       "2  Chinstrap  Torgersen       51.096718      16.777518         159.961493   \n",
+       "3     Adelie     Biscoe       49.070911      14.796037         244.530153   \n",
+       "4  Chinstrap     Biscoe       44.827918      13.246787         236.948853   \n",
+       "\n",
+       "   body_mass_g     sex  \n",
+       "0  2873.291927  FEMALE  \n",
+       "1  3639.940005  FEMALE  \n",
+       "2  5401.743330    MALE  \n",
+       "3  2316.038092    MALE  \n",
+       "4  5036.246870  FEMALE  "
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df_dummy = client.get_dummy_dataset(\n",
+    "    nb_rows = NB_ROWS, \n",
+    "    seed = SEED\n",
+    ")\n",
+    "\n",
+    "print(df_dummy.shape)\n",
+    "df_dummy.head()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9e7ca7ae-bf17-40c8-aa75-2d72fcdd3088",
+   "metadata": {},
+   "source": [
+    "## Step 4: Train Logistic Regression model with DiffPrivLib"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2de1389c-53a7-4098-bc3c-397c12a4b869",
+   "metadata": {},
+   "source": [
+    "We want to train an ML model to guess the species of penguins based on their bill length and depth, flipper length and body mass.\n",
+    "\n",
+    "Therefore, we use a DiffPrivLib pipeline which:\n",
+    "- standard scales the dimensions between the metadata bounds\n",
+    "- and then performs a logistic regression\n",
+    "to predict the species of penguins."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "2864729f-2ce4-4d81-a446-8e3f2c1493b3",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from sklearn.pipeline import Pipeline\n",
+    "from diffprivlib import models\n",
+    "import pandas as pd"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a06365e9-4076-4592-871a-31af91d6a05d",
+   "metadata": {},
+   "source": [
+    "### Classification: Logistic Regression"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "32e81639-2a52-4a92-aead-785a30e5483f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import warnings\n",
+    "from diffprivlib import models\n",
+    "from diffprivlib.utils import (\n",
+    "    DiffprivlibCompatibilityWarning,\n",
+    "    PrivacyLeakWarning,\n",
+    ")\n",
+    "warnings.simplefilter(\"error\", DiffprivlibCompatibilityWarning)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eead3541-66e5-4c0f-aa5b-7b97821afe39",
+   "metadata": {},
+   "source": [
+    "#### DiffprivlibCompatibilityWarning will not work"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "804f31cd-f277-47d4-9648-a51872eccf29",
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "DiffprivlibCompatibilityWarning",
+     "evalue": "Parameter 'svd_solver' is not functional in diffprivlib.  Remove this parameter to suppress this warning.",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mDiffprivlibCompatibilityWarning\u001b[0m           Traceback (most recent call last)",
+      "Cell \u001b[0;32mIn[10], line 3\u001b[0m\n\u001b[1;32m      1\u001b[0m dpl_pipeline \u001b[38;5;241m=\u001b[39m Pipeline([\n\u001b[1;32m      2\u001b[0m     (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mscaler\u001b[39m\u001b[38;5;124m'\u001b[39m, models\u001b[38;5;241m.\u001b[39mStandardScaler(epsilon \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.5\u001b[39m)),\n\u001b[0;32m----> 3\u001b[0m     (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mclassifier\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[43mmodels\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mLogisticRegression\u001b[49m\u001b[43m(\u001b[49m\u001b[43mepsilon\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msvd_solver\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mfull\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m      4\u001b[0m ])\n",
+      "File \u001b[0;32m/usr/local/lib/python3.11/site-packages/diffprivlib/models/logistic_regression.py:194\u001b[0m, in \u001b[0;36mLogisticRegression.__init__\u001b[0;34m(self, epsilon, data_norm, tol, C, fit_intercept, max_iter, verbose, warm_start, n_jobs, random_state, accountant, **unused_args)\u001b[0m\n\u001b[1;32m    191\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclasses_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m    192\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maccountant \u001b[38;5;241m=\u001b[39m BudgetAccountant\u001b[38;5;241m.\u001b[39mload_default(accountant)\n\u001b[0;32m--> 194\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_warn_unused_args\u001b[49m\u001b[43m(\u001b[49m\u001b[43munused_args\u001b[49m\u001b[43m)\u001b[49m\n",
+      "File \u001b[0;32m/usr/local/lib/python3.11/site-packages/diffprivlib/utils.py:71\u001b[0m, in \u001b[0;36mwarn_unused_args\u001b[0;34m(args)\u001b[0m\n\u001b[1;32m     68\u001b[0m     args \u001b[38;5;241m=\u001b[39m [args]\n\u001b[1;32m     70\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m arg \u001b[38;5;129;01min\u001b[39;00m args:\n\u001b[0;32m---> 71\u001b[0m     \u001b[43mwarnings\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwarn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mParameter \u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43marg\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m is not functional in diffprivlib.  Remove this parameter to suppress this \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n\u001b[1;32m     72\u001b[0m \u001b[43m                  \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mwarning.\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mDiffprivlibCompatibilityWarning\u001b[49m\u001b[43m)\u001b[49m\n",
+      "\u001b[0;31mDiffprivlibCompatibilityWarning\u001b[0m: Parameter 'svd_solver' is not functional in diffprivlib.  Remove this parameter to suppress this warning."
+     ]
+    }
+   ],
+   "source": [
+    "# DiffprivlibCompatibilityWarning Error expected\n",
+    "dpl_pipeline = Pipeline([\n",
+    "    ('scaler', models.StandardScaler(epsilon = 0.5)),\n",
+    "    ('classifier', models.LogisticRegression(epsilon = 1.0, svd_solver='full'))\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "15dd0c42-3e20-4b27-95b3-9b55622b4bfd",
+   "metadata": {},
+   "source": [
+    "To resolve the DiffprivlibCompatibilityWarning issue, the svd_solver should not be set as it i incompatible with diffprivlib."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9c5d7fa4-cfe6-4a4f-88ff-8e88ca31dfed",
+   "metadata": {},
+   "source": [
+    "#### PrivacyLeakWarning will not be processed within server"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "id": "f1c9cffb-8327-400d-ab9d-35c5450fd4d6",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('scaler', models.StandardScaler(epsilon = 0.5, bounds=BOUNDS)),\n",
+    "    ('classifier', models.LogisticRegression(epsilon = 1.0))\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "id": "db1ddfbc-de3e-43fe-9958-49f9e6dad89f",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Error while processing DiffPrivLib request in server                 status code: 422 message: {\"ExternalLibraryException\":\"PrivacyLeakWarning: Data norm has not been specified and will be calculated on the data provided.  This will result in additional privacy leakage. To ensure differential privacy and no additional privacy leakage, specify `data_norm` at initialisation.. Lomas server cannot fit pipeline on data, PrivacyLeakWarning is a blocker.\",\"library\":\"diffprivlib\"}\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Expect PrivacyLeakWarning Error\n",
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1,\n",
+    "    dummy = True\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c9d58006-89d5-4110-9657-641256bafaf9",
+   "metadata": {},
+   "source": [
+    "Diffprivlib requests that have **PrivacyLeakWarning** will not be processed in the server. \n",
+    "In lomas, **the standard scaler must always be used as a first step** and filled based on the metadata values."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "id": "12d1faa1-f88a-49bb-911a-83f879ca10b6",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "bl_l = penguin_metadata['columns']['bill_length_mm']['lower']\n",
+    "bl_u = penguin_metadata['columns']['bill_length_mm']['upper']\n",
+    "bd_l = penguin_metadata['columns']['bill_depth_mm']['lower']\n",
+    "bd_u = penguin_metadata['columns']['bill_depth_mm']['upper']\n",
+    "fl_l = penguin_metadata['columns']['flipper_length_mm']['lower']\n",
+    "fl_u = penguin_metadata['columns']['flipper_length_mm']['upper']\n",
+    "bm_l = penguin_metadata['columns']['body_mass_g']['lower']\n",
+    "bm_u = penguin_metadata['columns']['body_mass_g']['upper']\n",
+    "\n",
+    "BOUNDS = ([bl_l, bd_l, fl_l, bm_l], [bl_u, bd_u, fl_u, bm_u])\n",
+    "BOUNDS"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c47aec3e-15ea-476d-9dcb-5b2f4888e355",
+   "metadata": {},
+   "source": [
+    "For the same reason, the data_norm should be computed based on metadata and given as argument."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "id": "25a72ad9-d3f3-478a-8d33-c07bedbf4f66",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "83.69469642643347"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# The max l2 norm of any row of the data. This defines the spread of data that will be protected by differential privacy.\n",
+    "data_norm = np.sqrt(np.linalg.norm([bl_u, bd_u, fl_u, bm_u]))\n",
+    "data_norm"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "id": "828a743e-960f-4713-a6ed-a0bd243a13e3",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('scaler', models.StandardScaler(epsilon = 0.5, bounds=([bl_l, bd_l, fl_l, bm_l], [bl_u, bd_u, fl_u, bm_u]))),\n",
+    "    ('classifier', models.LogisticRegression(epsilon = 1.0, data_norm = data_norm))\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "id": "c9c13dfe-0266-4cd8-b126-2accee6c1136",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1,\n",
+    "    dummy = True\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "b3e7ceae-2813-47fe-b96f-dcc0d6bb207e",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "The accuracy score of the dummy_response is 0.3.\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(f\"The accuracy score of the dummy_response is {dummy_response['query_response']['score']}.\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "id": "e036b55f-6c3d-4212-8d91-9ece8223cf69",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'epsilon_cost': 1.5, 'delta_cost': 0.0}"
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "res = client.estimate_diffprivlib_cost(\n",
+    "    dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1\n",
+    ")\n",
+    "res"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "id": "4398755f-348f-47e2-9329-80c34897e16b",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<style>#sk-container-id-1 {\n",
+       "  /* Definition of color scheme common for light and dark mode */\n",
+       "  --sklearn-color-text: black;\n",
+       "  --sklearn-color-line: gray;\n",
+       "  /* Definition of color scheme for unfitted estimators */\n",
+       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
+       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
+       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
+       "  --sklearn-color-unfitted-level-3: chocolate;\n",
+       "  /* Definition of color scheme for fitted estimators */\n",
+       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
+       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
+       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
+       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
+       "\n",
+       "  /* Specific color for light theme */\n",
+       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
+       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-icon: #696969;\n",
+       "\n",
+       "  @media (prefers-color-scheme: dark) {\n",
+       "    /* Redefinition of color scheme for dark theme */\n",
+       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
+       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-icon: #878787;\n",
+       "  }\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 pre {\n",
+       "  padding: 0;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 input.sk-hidden--visually {\n",
+       "  border: 0;\n",
+       "  clip: rect(1px 1px 1px 1px);\n",
+       "  clip: rect(1px, 1px, 1px, 1px);\n",
+       "  height: 1px;\n",
+       "  margin: -1px;\n",
+       "  overflow: hidden;\n",
+       "  padding: 0;\n",
+       "  position: absolute;\n",
+       "  width: 1px;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
+       "  border: 1px dashed var(--sklearn-color-line);\n",
+       "  margin: 0 0.4em 0.5em 0.4em;\n",
+       "  box-sizing: border-box;\n",
+       "  padding-bottom: 0.4em;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-container {\n",
+       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
+       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
+       "     so we also need the `!important` here to be able to override the\n",
+       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
+       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
+       "  display: inline-block !important;\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-parallel-item,\n",
+       "div.sk-serial,\n",
+       "div.sk-item {\n",
+       "  /* draw centered vertical line to link estimators */\n",
+       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
+       "  background-size: 2px 100%;\n",
+       "  background-repeat: no-repeat;\n",
+       "  background-position: center center;\n",
+       "}\n",
+       "\n",
+       "/* Parallel-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel-item::after {\n",
+       "  content: \"\";\n",
+       "  width: 100%;\n",
+       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
+       "  flex-grow: 1;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel {\n",
+       "  display: flex;\n",
+       "  align-items: stretch;\n",
+       "  justify-content: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel-item {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
+       "  align-self: flex-end;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
+       "  align-self: flex-start;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
+       "  width: 0;\n",
+       "}\n",
+       "\n",
+       "/* Serial-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-serial {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "  align-items: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  padding-right: 1em;\n",
+       "  padding-left: 1em;\n",
+       "}\n",
+       "\n",
+       "\n",
+       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
+       "clickable and can be expanded/collapsed.\n",
+       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
+       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
+       "*/\n",
+       "\n",
+       "/* Pipeline and ColumnTransformer style (default) */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-toggleable {\n",
+       "  /* Default theme specific background. It is overwritten whether we have a\n",
+       "  specific estimator or a Pipeline/ColumnTransformer */\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable label */\n",
+       "#sk-container-id-1 label.sk-toggleable__label {\n",
+       "  cursor: pointer;\n",
+       "  display: block;\n",
+       "  width: 100%;\n",
+       "  margin-bottom: 0;\n",
+       "  padding: 0.5em;\n",
+       "  box-sizing: border-box;\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
+       "  /* Arrow on the left of the label */\n",
+       "  content: \"▸\";\n",
+       "  float: left;\n",
+       "  margin-right: 0.25em;\n",
+       "  color: var(--sklearn-color-icon);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable content - dropdown */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-toggleable__content {\n",
+       "  max-height: 0;\n",
+       "  max-width: 0;\n",
+       "  overflow: hidden;\n",
+       "  text-align: left;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
+       "  margin: 0.2em;\n",
+       "  border-radius: 0.25em;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
+       "  /* Expand drop-down */\n",
+       "  max-height: 200px;\n",
+       "  max-width: 100%;\n",
+       "  overflow: auto;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
+       "  content: \"▾\";\n",
+       "}\n",
+       "\n",
+       "/* Pipeline/ColumnTransformer-specific style */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific style */\n",
+       "\n",
+       "/* Colorize estimator box */\n",
+       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
+       "#sk-container-id-1 div.sk-label label {\n",
+       "  /* The background is the default theme color */\n",
+       "  color: var(--sklearn-color-text-on-default-background);\n",
+       "}\n",
+       "\n",
+       "/* On hover, darken the color of the background */\n",
+       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Label box, darken color on hover, fitted */\n",
+       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator label */\n",
+       "\n",
+       "#sk-container-id-1 div.sk-label label {\n",
+       "  font-family: monospace;\n",
+       "  font-weight: bold;\n",
+       "  display: inline-block;\n",
+       "  line-height: 1.2em;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-label-container {\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific */\n",
+       "#sk-container-id-1 div.sk-estimator {\n",
+       "  font-family: monospace;\n",
+       "  border: 1px dotted var(--sklearn-color-border-box);\n",
+       "  border-radius: 0.25em;\n",
+       "  box-sizing: border-box;\n",
+       "  margin-bottom: 0.5em;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-estimator.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "/* on hover */\n",
+       "#sk-container-id-1 div.sk-estimator:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
+       "\n",
+       "/* Common style for \"i\" and \"?\" */\n",
+       "\n",
+       ".sk-estimator-doc-link,\n",
+       "a:link.sk-estimator-doc-link,\n",
+       "a:visited.sk-estimator-doc-link {\n",
+       "  float: right;\n",
+       "  font-size: smaller;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1em;\n",
+       "  height: 1em;\n",
+       "  width: 1em;\n",
+       "  text-decoration: none !important;\n",
+       "  margin-left: 1ex;\n",
+       "  /* unfitted */\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted,\n",
+       "a:link.sk-estimator-doc-link.fitted,\n",
+       "a:visited.sk-estimator-doc-link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "/* Span, style for the box shown on hovering the info icon */\n",
+       ".sk-estimator-doc-link span {\n",
+       "  display: none;\n",
+       "  z-index: 9999;\n",
+       "  position: relative;\n",
+       "  font-weight: normal;\n",
+       "  right: .2ex;\n",
+       "  padding: .5ex;\n",
+       "  margin: .5ex;\n",
+       "  width: min-content;\n",
+       "  min-width: 20ex;\n",
+       "  max-width: 50ex;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  box-shadow: 2pt 2pt 4pt #999;\n",
+       "  /* unfitted */\n",
+       "  background: var(--sklearn-color-unfitted-level-0);\n",
+       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted span {\n",
+       "  /* fitted */\n",
+       "  background: var(--sklearn-color-fitted-level-0);\n",
+       "  border: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link:hover span {\n",
+       "  display: block;\n",
+       "}\n",
+       "\n",
+       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
+       "\n",
+       "#sk-container-id-1 a.estimator_doc_link {\n",
+       "  float: right;\n",
+       "  font-size: 1rem;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1rem;\n",
+       "  height: 1rem;\n",
+       "  width: 1rem;\n",
+       "  text-decoration: none;\n",
+       "  /* unfitted */\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;scaler&#x27;,\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                (&#x27;classifier&#x27;,\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;scaler&#x27;,\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                (&#x27;classifier&#x27;,\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">StandardScaler</label><div class=\"sk-toggleable__content fitted\"><pre>StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "               bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                       array([  65.,   23.,  250., 7000.])),\n",
+       "               epsilon=0.5)</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">LogisticRegression</label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                   data_norm=83.69469642643347)</pre></div> </div></div></div></div></div></div>"
+      ],
+      "text/plain": [
+       "Pipeline(steps=[('scaler',\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                ('classifier',\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "dummy_response['query_response'][\"model\"]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8aad5f86-20f7-42fe-8efd-56c96f6beb41",
+   "metadata": {},
+   "source": [
+    "Now we train the same pipeline on the real dataset."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "id": "b823db85-d993-4a36-9dea-bf5989c543f8",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'requested_by': 'Dr. Antartica',\n",
+       " 'query_response': {'score': 0.13432835820895522,\n",
+       "  'model': Pipeline(steps=[('scaler',\n",
+       "                   StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                  bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                          array([  65.,   23.,  250., 7000.])),\n",
+       "                                  epsilon=0.5)),\n",
+       "                  ('classifier',\n",
+       "                   LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                      data_norm=83.69469642643347))])},\n",
+       " 'spent_epsilon': 1.5,\n",
+       " 'spent_delta': 0.0}"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "res = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1\n",
+    ")\n",
+    "res"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "id": "6c071425-5364-4d79-8ccf-4f46018ac849",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "The accuracy score of the model trained on real data is 0.13432835820895522.\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(f\"The accuracy score of the model trained on real data is {res['query_response']['score']}.\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9cea3fc7-4331-4387-a19f-6afcaa21b6bc",
+   "metadata": {},
+   "source": [
+    "The model is of course the same but with different trained parameters."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "id": "b646cd08-504b-4846-88ba-a3557ee4b2fd",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = res['query_response']['model']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a581f47d-f1c7-4121-9f8f-e496e9023d58",
+   "metadata": {},
+   "source": [
+    "We predict what would be the specy of the smallest possible penguin in all dimension versus to biggest possible penguin in all dimensions."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "id": "8cd3c335-f3b3-4f64-b708-737ae4623a5f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x_to_predict = pd.DataFrame({\n",
+    "    'bill_length_mm': [bl_l, bl_u], \n",
+    "    'bill_depth_mm': [bd_l, bd_u] , \n",
+    "    'flipper_length_mm': [fl_l, fl_u], \n",
+    "    'body_mass_g': [bm_l, bm_u]\n",
+    "})"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "id": "65413ef0-317b-431d-9ed8-6ffabe85c1b2",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array(['Chinstrap', 'Gentoo'], dtype=object)"
+      ]
+     },
+     "execution_count": 28,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c1e968c0-f319-48c5-89cb-b8a255e822cb",
+   "metadata": {},
+   "source": [
+    "Indeed, Chinstrap penguins are typically smaller than Gentoo penguins."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "839ba199-3014-4e39-8f0a-aecf15241b11",
+   "metadata": {},
+   "source": [
+    "## Step 5: Train other models with DiffPrivLib"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fef35343-cdf6-4e86-a54a-4bcf6568f2ac",
+   "metadata": {},
+   "source": [
+    "### Classification: Gaussian Naive Bayes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "id": "9dd37857-0ef2-44d5-b206-092262c9cea1",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('scaler', models.StandardScaler(epsilon = 0.5, bounds=BOUNDS)),\n",
+    "    ('gaussian', models.GaussianNB(epsilon = 1.0, bounds=BOUNDS, priors = (0.3, 0.3, 0.4))),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "id": "8e2c07c6-20f4-473c-80f6-b4b558191530",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    dummy = True\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "id": "550868df-5476-496e-849d-8787da9eb27e",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'epsilon_cost': 1.5, 'delta_cost': 0.0}"
+      ]
+     },
+     "execution_count": 27,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cost_res = client.estimate_diffprivlib_cost(\n",
+    "    dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1\n",
+    ")\n",
+    "cost_res"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "id": "934a1237-68d1-4796-838e-e8c370068f5d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "id": "ec3c98d3-cb5d-4a96-9ada-ea9904326aec",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array(['Adelie', 'Chinstrap'], dtype=object)"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "model = res['query_response']['model']\n",
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c1224c94-d995-40f1-967d-b7afb21bce48",
+   "metadata": {},
+   "source": [
+    "### Random Forest"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "id": "d4f036a7-0405-4f4b-a4a0-3fe1b0b8d4d0",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('rf', models.RandomForestClassifier(n_estimators=10, epsilon = 2.0, bounds=BOUNDS, classes=['Adelie', 'Chinstrap', 'Gentoo'])),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "id": "c969d488-e1a2-445a-a269-681d96343b9f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    dummy = True\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "id": "de94f0a5-6e9d-4eac-aa77-b98892ed41fc",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'epsilon_cost': 2.0, 'delta_cost': 0.0}"
+      ]
+     },
+     "execution_count": 32,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cost_res = client.estimate_diffprivlib_cost(\n",
+    "    dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1\n",
+    ")\n",
+    "cost_res"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "id": "40f25e33-a644-4938-8adb-a7bbe5306ee2",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "4f5fad30-991e-45f3-a7e3-1ae04bedd8a1",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<style>#sk-container-id-2 {\n",
+       "  /* Definition of color scheme common for light and dark mode */\n",
+       "  --sklearn-color-text: black;\n",
+       "  --sklearn-color-line: gray;\n",
+       "  /* Definition of color scheme for unfitted estimators */\n",
+       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
+       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
+       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
+       "  --sklearn-color-unfitted-level-3: chocolate;\n",
+       "  /* Definition of color scheme for fitted estimators */\n",
+       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
+       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
+       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
+       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
+       "\n",
+       "  /* Specific color for light theme */\n",
+       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
+       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-icon: #696969;\n",
+       "\n",
+       "  @media (prefers-color-scheme: dark) {\n",
+       "    /* Redefinition of color scheme for dark theme */\n",
+       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
+       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-icon: #878787;\n",
+       "  }\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 pre {\n",
+       "  padding: 0;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 input.sk-hidden--visually {\n",
+       "  border: 0;\n",
+       "  clip: rect(1px 1px 1px 1px);\n",
+       "  clip: rect(1px, 1px, 1px, 1px);\n",
+       "  height: 1px;\n",
+       "  margin: -1px;\n",
+       "  overflow: hidden;\n",
+       "  padding: 0;\n",
+       "  position: absolute;\n",
+       "  width: 1px;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-dashed-wrapped {\n",
+       "  border: 1px dashed var(--sklearn-color-line);\n",
+       "  margin: 0 0.4em 0.5em 0.4em;\n",
+       "  box-sizing: border-box;\n",
+       "  padding-bottom: 0.4em;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-container {\n",
+       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
+       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
+       "     so we also need the `!important` here to be able to override the\n",
+       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
+       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
+       "  display: inline-block !important;\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-text-repr-fallback {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-parallel-item,\n",
+       "div.sk-serial,\n",
+       "div.sk-item {\n",
+       "  /* draw centered vertical line to link estimators */\n",
+       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
+       "  background-size: 2px 100%;\n",
+       "  background-repeat: no-repeat;\n",
+       "  background-position: center center;\n",
+       "}\n",
+       "\n",
+       "/* Parallel-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel-item::after {\n",
+       "  content: \"\";\n",
+       "  width: 100%;\n",
+       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
+       "  flex-grow: 1;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel {\n",
+       "  display: flex;\n",
+       "  align-items: stretch;\n",
+       "  justify-content: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel-item {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel-item:first-child::after {\n",
+       "  align-self: flex-end;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel-item:last-child::after {\n",
+       "  align-self: flex-start;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-parallel-item:only-child::after {\n",
+       "  width: 0;\n",
+       "}\n",
+       "\n",
+       "/* Serial-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-serial {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "  align-items: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  padding-right: 1em;\n",
+       "  padding-left: 1em;\n",
+       "}\n",
+       "\n",
+       "\n",
+       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
+       "clickable and can be expanded/collapsed.\n",
+       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
+       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
+       "*/\n",
+       "\n",
+       "/* Pipeline and ColumnTransformer style (default) */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-toggleable {\n",
+       "  /* Default theme specific background. It is overwritten whether we have a\n",
+       "  specific estimator or a Pipeline/ColumnTransformer */\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable label */\n",
+       "#sk-container-id-2 label.sk-toggleable__label {\n",
+       "  cursor: pointer;\n",
+       "  display: block;\n",
+       "  width: 100%;\n",
+       "  margin-bottom: 0;\n",
+       "  padding: 0.5em;\n",
+       "  box-sizing: border-box;\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 label.sk-toggleable__label-arrow:before {\n",
+       "  /* Arrow on the left of the label */\n",
+       "  content: \"▸\";\n",
+       "  float: left;\n",
+       "  margin-right: 0.25em;\n",
+       "  color: var(--sklearn-color-icon);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable content - dropdown */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-toggleable__content {\n",
+       "  max-height: 0;\n",
+       "  max-width: 0;\n",
+       "  overflow: hidden;\n",
+       "  text-align: left;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-toggleable__content.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-toggleable__content pre {\n",
+       "  margin: 0.2em;\n",
+       "  border-radius: 0.25em;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-toggleable__content.fitted pre {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
+       "  /* Expand drop-down */\n",
+       "  max-height: 200px;\n",
+       "  max-width: 100%;\n",
+       "  overflow: auto;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
+       "  content: \"▾\";\n",
+       "}\n",
+       "\n",
+       "/* Pipeline/ColumnTransformer-specific style */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific style */\n",
+       "\n",
+       "/* Colorize estimator box */\n",
+       "#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-label label.sk-toggleable__label,\n",
+       "#sk-container-id-2 div.sk-label label {\n",
+       "  /* The background is the default theme color */\n",
+       "  color: var(--sklearn-color-text-on-default-background);\n",
+       "}\n",
+       "\n",
+       "/* On hover, darken the color of the background */\n",
+       "#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Label box, darken color on hover, fitted */\n",
+       "#sk-container-id-2 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator label */\n",
+       "\n",
+       "#sk-container-id-2 div.sk-label label {\n",
+       "  font-family: monospace;\n",
+       "  font-weight: bold;\n",
+       "  display: inline-block;\n",
+       "  line-height: 1.2em;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-label-container {\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific */\n",
+       "#sk-container-id-2 div.sk-estimator {\n",
+       "  font-family: monospace;\n",
+       "  border: 1px dotted var(--sklearn-color-border-box);\n",
+       "  border-radius: 0.25em;\n",
+       "  box-sizing: border-box;\n",
+       "  margin-bottom: 0.5em;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-estimator.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "/* on hover */\n",
+       "#sk-container-id-2 div.sk-estimator:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 div.sk-estimator.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
+       "\n",
+       "/* Common style for \"i\" and \"?\" */\n",
+       "\n",
+       ".sk-estimator-doc-link,\n",
+       "a:link.sk-estimator-doc-link,\n",
+       "a:visited.sk-estimator-doc-link {\n",
+       "  float: right;\n",
+       "  font-size: smaller;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1em;\n",
+       "  height: 1em;\n",
+       "  width: 1em;\n",
+       "  text-decoration: none !important;\n",
+       "  margin-left: 1ex;\n",
+       "  /* unfitted */\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted,\n",
+       "a:link.sk-estimator-doc-link.fitted,\n",
+       "a:visited.sk-estimator-doc-link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "/* Span, style for the box shown on hovering the info icon */\n",
+       ".sk-estimator-doc-link span {\n",
+       "  display: none;\n",
+       "  z-index: 9999;\n",
+       "  position: relative;\n",
+       "  font-weight: normal;\n",
+       "  right: .2ex;\n",
+       "  padding: .5ex;\n",
+       "  margin: .5ex;\n",
+       "  width: min-content;\n",
+       "  min-width: 20ex;\n",
+       "  max-width: 50ex;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  box-shadow: 2pt 2pt 4pt #999;\n",
+       "  /* unfitted */\n",
+       "  background: var(--sklearn-color-unfitted-level-0);\n",
+       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted span {\n",
+       "  /* fitted */\n",
+       "  background: var(--sklearn-color-fitted-level-0);\n",
+       "  border: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link:hover span {\n",
+       "  display: block;\n",
+       "}\n",
+       "\n",
+       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
+       "\n",
+       "#sk-container-id-2 a.estimator_doc_link {\n",
+       "  float: right;\n",
+       "  font-size: 1rem;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1rem;\n",
+       "  height: 1rem;\n",
+       "  width: 1rem;\n",
+       "  text-decoration: none;\n",
+       "  /* unfitted */\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 a.estimator_doc_link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "#sk-container-id-2 a.estimator_doc_link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-2 a.estimator_doc_link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "</style><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;scaler&#x27;,\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                (&#x27;classifier&#x27;,\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-4\" type=\"checkbox\" ><label for=\"sk-estimator-id-4\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;scaler&#x27;,\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                (&#x27;classifier&#x27;,\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-5\" type=\"checkbox\" ><label for=\"sk-estimator-id-5\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">StandardScaler</label><div class=\"sk-toggleable__content fitted\"><pre>StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "               bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                       array([  65.,   23.,  250., 7000.])),\n",
+       "               epsilon=0.5)</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-6\" type=\"checkbox\" ><label for=\"sk-estimator-id-6\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">LogisticRegression</label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                   data_norm=83.69469642643347)</pre></div> </div></div></div></div></div></div>"
+      ],
+      "text/plain": [
+       "Pipeline(steps=[('scaler',\n",
+       "                 StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                        array([  65.,   23.,  250., 7000.])),\n",
+       "                                epsilon=0.5)),\n",
+       "                ('classifier',\n",
+       "                 LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                    data_norm=83.69469642643347))])"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "model = res['query_response']['model']\n",
+    "model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "id": "96562928-73e8-48b1-b8a5-97245c7da8d2",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array(['Adelie', 'Chinstrap'], dtype=object)"
+      ]
+     },
+     "execution_count": 35,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "020ceeb1-7a78-4036-8ece-0afa88e49342",
+   "metadata": {},
+   "source": [
+    "### Decision Tree Classifier"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "id": "22b5c5ae-b008-4980-8767-47e7a1ad3301",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('dtc', models.DecisionTreeClassifier(epsilon = 2.0, bounds=BOUNDS, classes=['Adelie', 'Chinstrap', 'Gentoo'])),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 37,
+   "id": "70b73ba0-67dc-4594-820c-0128916fafc8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    "    test_train_split_seed = 1,\n",
+    "    dummy = True,\n",
+    "    nb_rows = 100,\n",
+    "    seed = 42\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 38,
+   "id": "736e8283-604a-4e45-9e98-7da5cc03e1b0",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = ['species'],\n",
+    "    test_size = 0.2,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "id": "a3719359-58a6-4271-9a34-4b49e0d7188e",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array(['Adelie', 'Chinstrap'], dtype=object)"
+      ]
+     },
+     "execution_count": 39,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "model = res['query_response']['model']\n",
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4e72e9fc-bb5b-49d9-b4e6-543bdaf93f69",
+   "metadata": {},
+   "source": [
+    "### Regression: Linear Regression"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 40,
+   "id": "907e6f6c-a3da-4f7c-8d85-b0a7b8c018d1",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('lr', models.LinearRegression(epsilon = 2.0, bounds_X=(bl_l, bl_u), bounds_y=(bd_l, bd_u))),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "id": "58011a7d-7c03-4872-a032-5d93c55c1d5f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<style>#sk-container-id-3 {\n",
+       "  /* Definition of color scheme common for light and dark mode */\n",
+       "  --sklearn-color-text: black;\n",
+       "  --sklearn-color-line: gray;\n",
+       "  /* Definition of color scheme for unfitted estimators */\n",
+       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
+       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
+       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
+       "  --sklearn-color-unfitted-level-3: chocolate;\n",
+       "  /* Definition of color scheme for fitted estimators */\n",
+       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
+       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
+       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
+       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
+       "\n",
+       "  /* Specific color for light theme */\n",
+       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
+       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-icon: #696969;\n",
+       "\n",
+       "  @media (prefers-color-scheme: dark) {\n",
+       "    /* Redefinition of color scheme for dark theme */\n",
+       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
+       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-icon: #878787;\n",
+       "  }\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 pre {\n",
+       "  padding: 0;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 input.sk-hidden--visually {\n",
+       "  border: 0;\n",
+       "  clip: rect(1px 1px 1px 1px);\n",
+       "  clip: rect(1px, 1px, 1px, 1px);\n",
+       "  height: 1px;\n",
+       "  margin: -1px;\n",
+       "  overflow: hidden;\n",
+       "  padding: 0;\n",
+       "  position: absolute;\n",
+       "  width: 1px;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-dashed-wrapped {\n",
+       "  border: 1px dashed var(--sklearn-color-line);\n",
+       "  margin: 0 0.4em 0.5em 0.4em;\n",
+       "  box-sizing: border-box;\n",
+       "  padding-bottom: 0.4em;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-container {\n",
+       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
+       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
+       "     so we also need the `!important` here to be able to override the\n",
+       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
+       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
+       "  display: inline-block !important;\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-text-repr-fallback {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-parallel-item,\n",
+       "div.sk-serial,\n",
+       "div.sk-item {\n",
+       "  /* draw centered vertical line to link estimators */\n",
+       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
+       "  background-size: 2px 100%;\n",
+       "  background-repeat: no-repeat;\n",
+       "  background-position: center center;\n",
+       "}\n",
+       "\n",
+       "/* Parallel-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel-item::after {\n",
+       "  content: \"\";\n",
+       "  width: 100%;\n",
+       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
+       "  flex-grow: 1;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel {\n",
+       "  display: flex;\n",
+       "  align-items: stretch;\n",
+       "  justify-content: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel-item {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel-item:first-child::after {\n",
+       "  align-self: flex-end;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel-item:last-child::after {\n",
+       "  align-self: flex-start;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-parallel-item:only-child::after {\n",
+       "  width: 0;\n",
+       "}\n",
+       "\n",
+       "/* Serial-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-serial {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "  align-items: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  padding-right: 1em;\n",
+       "  padding-left: 1em;\n",
+       "}\n",
+       "\n",
+       "\n",
+       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
+       "clickable and can be expanded/collapsed.\n",
+       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
+       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
+       "*/\n",
+       "\n",
+       "/* Pipeline and ColumnTransformer style (default) */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-toggleable {\n",
+       "  /* Default theme specific background. It is overwritten whether we have a\n",
+       "  specific estimator or a Pipeline/ColumnTransformer */\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable label */\n",
+       "#sk-container-id-3 label.sk-toggleable__label {\n",
+       "  cursor: pointer;\n",
+       "  display: block;\n",
+       "  width: 100%;\n",
+       "  margin-bottom: 0;\n",
+       "  padding: 0.5em;\n",
+       "  box-sizing: border-box;\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 label.sk-toggleable__label-arrow:before {\n",
+       "  /* Arrow on the left of the label */\n",
+       "  content: \"▸\";\n",
+       "  float: left;\n",
+       "  margin-right: 0.25em;\n",
+       "  color: var(--sklearn-color-icon);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 label.sk-toggleable__label-arrow:hover:before {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable content - dropdown */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-toggleable__content {\n",
+       "  max-height: 0;\n",
+       "  max-width: 0;\n",
+       "  overflow: hidden;\n",
+       "  text-align: left;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-toggleable__content.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-toggleable__content pre {\n",
+       "  margin: 0.2em;\n",
+       "  border-radius: 0.25em;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-toggleable__content.fitted pre {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
+       "  /* Expand drop-down */\n",
+       "  max-height: 200px;\n",
+       "  max-width: 100%;\n",
+       "  overflow: auto;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
+       "  content: \"▾\";\n",
+       "}\n",
+       "\n",
+       "/* Pipeline/ColumnTransformer-specific style */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific style */\n",
+       "\n",
+       "/* Colorize estimator box */\n",
+       "#sk-container-id-3 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-label label.sk-toggleable__label,\n",
+       "#sk-container-id-3 div.sk-label label {\n",
+       "  /* The background is the default theme color */\n",
+       "  color: var(--sklearn-color-text-on-default-background);\n",
+       "}\n",
+       "\n",
+       "/* On hover, darken the color of the background */\n",
+       "#sk-container-id-3 div.sk-label:hover label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Label box, darken color on hover, fitted */\n",
+       "#sk-container-id-3 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator label */\n",
+       "\n",
+       "#sk-container-id-3 div.sk-label label {\n",
+       "  font-family: monospace;\n",
+       "  font-weight: bold;\n",
+       "  display: inline-block;\n",
+       "  line-height: 1.2em;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-label-container {\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific */\n",
+       "#sk-container-id-3 div.sk-estimator {\n",
+       "  font-family: monospace;\n",
+       "  border: 1px dotted var(--sklearn-color-border-box);\n",
+       "  border-radius: 0.25em;\n",
+       "  box-sizing: border-box;\n",
+       "  margin-bottom: 0.5em;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-estimator.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "/* on hover */\n",
+       "#sk-container-id-3 div.sk-estimator:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 div.sk-estimator.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
+       "\n",
+       "/* Common style for \"i\" and \"?\" */\n",
+       "\n",
+       ".sk-estimator-doc-link,\n",
+       "a:link.sk-estimator-doc-link,\n",
+       "a:visited.sk-estimator-doc-link {\n",
+       "  float: right;\n",
+       "  font-size: smaller;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1em;\n",
+       "  height: 1em;\n",
+       "  width: 1em;\n",
+       "  text-decoration: none !important;\n",
+       "  margin-left: 1ex;\n",
+       "  /* unfitted */\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted,\n",
+       "a:link.sk-estimator-doc-link.fitted,\n",
+       "a:visited.sk-estimator-doc-link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "/* Span, style for the box shown on hovering the info icon */\n",
+       ".sk-estimator-doc-link span {\n",
+       "  display: none;\n",
+       "  z-index: 9999;\n",
+       "  position: relative;\n",
+       "  font-weight: normal;\n",
+       "  right: .2ex;\n",
+       "  padding: .5ex;\n",
+       "  margin: .5ex;\n",
+       "  width: min-content;\n",
+       "  min-width: 20ex;\n",
+       "  max-width: 50ex;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  box-shadow: 2pt 2pt 4pt #999;\n",
+       "  /* unfitted */\n",
+       "  background: var(--sklearn-color-unfitted-level-0);\n",
+       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted span {\n",
+       "  /* fitted */\n",
+       "  background: var(--sklearn-color-fitted-level-0);\n",
+       "  border: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link:hover span {\n",
+       "  display: block;\n",
+       "}\n",
+       "\n",
+       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
+       "\n",
+       "#sk-container-id-3 a.estimator_doc_link {\n",
+       "  float: right;\n",
+       "  font-size: 1rem;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1rem;\n",
+       "  height: 1rem;\n",
+       "  width: 1rem;\n",
+       "  text-decoration: none;\n",
+       "  /* unfitted */\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 a.estimator_doc_link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "#sk-container-id-3 a.estimator_doc_link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-3 a.estimator_doc_link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "</style><div id=\"sk-container-id-3\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;lr&#x27;,\n",
+       "                 LinearRegression(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                                  bounds_X=(array([30.]), array([65.])),\n",
+       "                                  bounds_y=(array([13.]), array([23.])),\n",
+       "                                  epsilon=2.0))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-7\" type=\"checkbox\" ><label for=\"sk-estimator-id-7\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;lr&#x27;,\n",
+       "                 LinearRegression(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                                  bounds_X=(array([30.]), array([65.])),\n",
+       "                                  bounds_y=(array([13.]), array([23.])),\n",
+       "                                  epsilon=2.0))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-8\" type=\"checkbox\" ><label for=\"sk-estimator-id-8\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">LinearRegression</label><div class=\"sk-toggleable__content fitted\"><pre>LinearRegression(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                 bounds_X=(array([30.]), array([65.])),\n",
+       "                 bounds_y=(array([13.]), array([23.])), epsilon=2.0)</pre></div> </div></div></div></div></div></div>"
+      ],
+      "text/plain": [
+       "Pipeline(steps=[('lr',\n",
+       "                 LinearRegression(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                                  bounds_X=(array([30.]), array([65.])),\n",
+       "                                  bounds_y=(array([13.]), array([23.])),\n",
+       "                                  epsilon=2.0))])"
+      ]
+     },
+     "execution_count": 41,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm'],\n",
+    "    target_columns = ['bill_depth_mm'],\n",
+    "    dummy = True\n",
+    ")\n",
+    "model = dummy_response['query_response']['model']\n",
+    "model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "id": "e28b1bd0-0e73-49db-a2d3-e7f06dc39d49",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array([[18.84279457],\n",
+       "       [18.24687217]])"
+      ]
+     },
+     "execution_count": 42,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Dummy model predictions\n",
+    "x_to_predict = pd.DataFrame({'bill_length_mm': [bl_l, bl_u]})\n",
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ec9e921d-1640-4379-84e5-65b93a1ff203",
+   "metadata": {},
+   "source": [
+    "### Clustering: K-Means"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 54,
+   "id": "1e830a01-7800-4701-aef2-11dbfc517f61",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('kmeans', models.KMeans(n_clusters = 8, epsilon = 2.0, bounds=BOUNDS)),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 55,
+   "id": "cb5e3e1f-2d48-4d5e-992a-c0e0cb6add3b",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<style>#sk-container-id-5 {\n",
+       "  /* Definition of color scheme common for light and dark mode */\n",
+       "  --sklearn-color-text: black;\n",
+       "  --sklearn-color-line: gray;\n",
+       "  /* Definition of color scheme for unfitted estimators */\n",
+       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
+       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
+       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
+       "  --sklearn-color-unfitted-level-3: chocolate;\n",
+       "  /* Definition of color scheme for fitted estimators */\n",
+       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
+       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
+       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
+       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
+       "\n",
+       "  /* Specific color for light theme */\n",
+       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
+       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
+       "  --sklearn-color-icon: #696969;\n",
+       "\n",
+       "  @media (prefers-color-scheme: dark) {\n",
+       "    /* Redefinition of color scheme for dark theme */\n",
+       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
+       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
+       "    --sklearn-color-icon: #878787;\n",
+       "  }\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 pre {\n",
+       "  padding: 0;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 input.sk-hidden--visually {\n",
+       "  border: 0;\n",
+       "  clip: rect(1px 1px 1px 1px);\n",
+       "  clip: rect(1px, 1px, 1px, 1px);\n",
+       "  height: 1px;\n",
+       "  margin: -1px;\n",
+       "  overflow: hidden;\n",
+       "  padding: 0;\n",
+       "  position: absolute;\n",
+       "  width: 1px;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-dashed-wrapped {\n",
+       "  border: 1px dashed var(--sklearn-color-line);\n",
+       "  margin: 0 0.4em 0.5em 0.4em;\n",
+       "  box-sizing: border-box;\n",
+       "  padding-bottom: 0.4em;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-container {\n",
+       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
+       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
+       "     so we also need the `!important` here to be able to override the\n",
+       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
+       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
+       "  display: inline-block !important;\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-text-repr-fallback {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-parallel-item,\n",
+       "div.sk-serial,\n",
+       "div.sk-item {\n",
+       "  /* draw centered vertical line to link estimators */\n",
+       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
+       "  background-size: 2px 100%;\n",
+       "  background-repeat: no-repeat;\n",
+       "  background-position: center center;\n",
+       "}\n",
+       "\n",
+       "/* Parallel-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel-item::after {\n",
+       "  content: \"\";\n",
+       "  width: 100%;\n",
+       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
+       "  flex-grow: 1;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel {\n",
+       "  display: flex;\n",
+       "  align-items: stretch;\n",
+       "  justify-content: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  position: relative;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel-item {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel-item:first-child::after {\n",
+       "  align-self: flex-end;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel-item:last-child::after {\n",
+       "  align-self: flex-start;\n",
+       "  width: 50%;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-parallel-item:only-child::after {\n",
+       "  width: 0;\n",
+       "}\n",
+       "\n",
+       "/* Serial-specific style estimator block */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-serial {\n",
+       "  display: flex;\n",
+       "  flex-direction: column;\n",
+       "  align-items: center;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  padding-right: 1em;\n",
+       "  padding-left: 1em;\n",
+       "}\n",
+       "\n",
+       "\n",
+       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
+       "clickable and can be expanded/collapsed.\n",
+       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
+       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
+       "*/\n",
+       "\n",
+       "/* Pipeline and ColumnTransformer style (default) */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-toggleable {\n",
+       "  /* Default theme specific background. It is overwritten whether we have a\n",
+       "  specific estimator or a Pipeline/ColumnTransformer */\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable label */\n",
+       "#sk-container-id-5 label.sk-toggleable__label {\n",
+       "  cursor: pointer;\n",
+       "  display: block;\n",
+       "  width: 100%;\n",
+       "  margin-bottom: 0;\n",
+       "  padding: 0.5em;\n",
+       "  box-sizing: border-box;\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 label.sk-toggleable__label-arrow:before {\n",
+       "  /* Arrow on the left of the label */\n",
+       "  content: \"▸\";\n",
+       "  float: left;\n",
+       "  margin-right: 0.25em;\n",
+       "  color: var(--sklearn-color-icon);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 label.sk-toggleable__label-arrow:hover:before {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "}\n",
+       "\n",
+       "/* Toggleable content - dropdown */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-toggleable__content {\n",
+       "  max-height: 0;\n",
+       "  max-width: 0;\n",
+       "  overflow: hidden;\n",
+       "  text-align: left;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-toggleable__content.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-toggleable__content pre {\n",
+       "  margin: 0.2em;\n",
+       "  border-radius: 0.25em;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-toggleable__content.fitted pre {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
+       "  /* Expand drop-down */\n",
+       "  max-height: 200px;\n",
+       "  max-width: 100%;\n",
+       "  overflow: auto;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
+       "  content: \"▾\";\n",
+       "}\n",
+       "\n",
+       "/* Pipeline/ColumnTransformer-specific style */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific style */\n",
+       "\n",
+       "/* Colorize estimator box */\n",
+       "#sk-container-id-5 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-label label.sk-toggleable__label,\n",
+       "#sk-container-id-5 div.sk-label label {\n",
+       "  /* The background is the default theme color */\n",
+       "  color: var(--sklearn-color-text-on-default-background);\n",
+       "}\n",
+       "\n",
+       "/* On hover, darken the color of the background */\n",
+       "#sk-container-id-5 div.sk-label:hover label.sk-toggleable__label {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Label box, darken color on hover, fitted */\n",
+       "#sk-container-id-5 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Estimator label */\n",
+       "\n",
+       "#sk-container-id-5 div.sk-label label {\n",
+       "  font-family: monospace;\n",
+       "  font-weight: bold;\n",
+       "  display: inline-block;\n",
+       "  line-height: 1.2em;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-label-container {\n",
+       "  text-align: center;\n",
+       "}\n",
+       "\n",
+       "/* Estimator-specific */\n",
+       "#sk-container-id-5 div.sk-estimator {\n",
+       "  font-family: monospace;\n",
+       "  border: 1px dotted var(--sklearn-color-border-box);\n",
+       "  border-radius: 0.25em;\n",
+       "  box-sizing: border-box;\n",
+       "  margin-bottom: 0.5em;\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-estimator.fitted {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-0);\n",
+       "}\n",
+       "\n",
+       "/* on hover */\n",
+       "#sk-container-id-5 div.sk-estimator:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 div.sk-estimator.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-2);\n",
+       "}\n",
+       "\n",
+       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
+       "\n",
+       "/* Common style for \"i\" and \"?\" */\n",
+       "\n",
+       ".sk-estimator-doc-link,\n",
+       "a:link.sk-estimator-doc-link,\n",
+       "a:visited.sk-estimator-doc-link {\n",
+       "  float: right;\n",
+       "  font-size: smaller;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1em;\n",
+       "  height: 1em;\n",
+       "  width: 1em;\n",
+       "  text-decoration: none !important;\n",
+       "  margin-left: 1ex;\n",
+       "  /* unfitted */\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted,\n",
+       "a:link.sk-estimator-doc-link.fitted,\n",
+       "a:visited.sk-estimator-doc-link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
+       ".sk-estimator-doc-link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover,\n",
+       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
+       ".sk-estimator-doc-link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "/* Span, style for the box shown on hovering the info icon */\n",
+       ".sk-estimator-doc-link span {\n",
+       "  display: none;\n",
+       "  z-index: 9999;\n",
+       "  position: relative;\n",
+       "  font-weight: normal;\n",
+       "  right: .2ex;\n",
+       "  padding: .5ex;\n",
+       "  margin: .5ex;\n",
+       "  width: min-content;\n",
+       "  min-width: 20ex;\n",
+       "  max-width: 50ex;\n",
+       "  color: var(--sklearn-color-text);\n",
+       "  box-shadow: 2pt 2pt 4pt #999;\n",
+       "  /* unfitted */\n",
+       "  background: var(--sklearn-color-unfitted-level-0);\n",
+       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link.fitted span {\n",
+       "  /* fitted */\n",
+       "  background: var(--sklearn-color-fitted-level-0);\n",
+       "  border: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "\n",
+       ".sk-estimator-doc-link:hover span {\n",
+       "  display: block;\n",
+       "}\n",
+       "\n",
+       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
+       "\n",
+       "#sk-container-id-5 a.estimator_doc_link {\n",
+       "  float: right;\n",
+       "  font-size: 1rem;\n",
+       "  line-height: 1em;\n",
+       "  font-family: monospace;\n",
+       "  background-color: var(--sklearn-color-background);\n",
+       "  border-radius: 1rem;\n",
+       "  height: 1rem;\n",
+       "  width: 1rem;\n",
+       "  text-decoration: none;\n",
+       "  /* unfitted */\n",
+       "  color: var(--sklearn-color-unfitted-level-1);\n",
+       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 a.estimator_doc_link.fitted {\n",
+       "  /* fitted */\n",
+       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
+       "  color: var(--sklearn-color-fitted-level-1);\n",
+       "}\n",
+       "\n",
+       "/* On hover */\n",
+       "#sk-container-id-5 a.estimator_doc_link:hover {\n",
+       "  /* unfitted */\n",
+       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
+       "  color: var(--sklearn-color-background);\n",
+       "  text-decoration: none;\n",
+       "}\n",
+       "\n",
+       "#sk-container-id-5 a.estimator_doc_link.fitted:hover {\n",
+       "  /* fitted */\n",
+       "  background-color: var(--sklearn-color-fitted-level-3);\n",
+       "}\n",
+       "</style><div id=\"sk-container-id-5\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;kmeans&#x27;,\n",
+       "                 KMeans(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                        bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                array([  65.,   23.,  250., 7000.])),\n",
+       "                        epsilon=2.0))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-11\" type=\"checkbox\" ><label for=\"sk-estimator-id-11\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;kmeans&#x27;,\n",
+       "                 KMeans(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                        bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                array([  65.,   23.,  250., 7000.])),\n",
+       "                        epsilon=2.0))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-12\" type=\"checkbox\" ><label for=\"sk-estimator-id-12\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">KMeans</label><div class=\"sk-toggleable__content fitted\"><pre>KMeans(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "       bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "               array([  65.,   23.,  250., 7000.])),\n",
+       "       epsilon=2.0)</pre></div> </div></div></div></div></div></div>"
+      ],
+      "text/plain": [
+       "Pipeline(steps=[('kmeans',\n",
+       "                 KMeans(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                        bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                array([  65.,   23.,  250., 7000.])),\n",
+       "                        epsilon=2.0))])"
+      ]
+     },
+     "execution_count": 55,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    dummy = True\n",
+    ")\n",
+    "model = dummy_response['query_response']['model']\n",
+    "model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "id": "a0612b10-de7a-4355-8268-09a46c30056a",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array([1, 2], dtype=int32)"
+      ]
+     },
+     "execution_count": 56,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Dummy model predictions\n",
+    "x_to_predict = pd.DataFrame({\n",
+    "    'bill_length_mm': [bl_l, bl_u], \n",
+    "    'bill_depth_mm': [bd_l, bd_u] , \n",
+    "    'flipper_length_mm': [fl_l, fl_u], \n",
+    "    'body_mass_g': [bm_l, bm_u]\n",
+    "})\n",
+    "predictions = model.predict(x_to_predict)\n",
+    "predictions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6021b034-b15d-4826-8de3-80b99afc838d",
+   "metadata": {},
+   "source": [
+    "### Dimensionality Reduction: PCA"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 57,
+   "id": "f9a4cbaf-b994-41c0-a0e9-81674c6657fb",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dpl_pipeline = Pipeline([\n",
+    "    ('pca', models.PCA(n_components=None, epsilon = 2.0, bounds=BOUNDS, data_norm=100, centered=False)),\n",
+    "])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 58,
+   "id": "4681cc17-6285-4b9b-a26d-837d6a79e1c3",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dummy_response = client.diffprivlib_query(\n",
+    "    pipeline = dpl_pipeline,\n",
+    "    feature_columns =  ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g'],\n",
+    "    target_columns = None, # TODO enable empty list\n",
+    "    dummy = True\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 59,
+   "id": "b1ac9784-bd0e-41c6-bb54-8201d9ab13ad",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'query_response': {'score': -3532.5573958633877,\n",
+       "  'model': Pipeline(steps=[('pca',\n",
+       "                   PCA(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                       bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                               array([  65.,   23.,  250., 7000.])),\n",
+       "                       data_norm=100, epsilon=2.0))])}}"
+      ]
+     },
+     "execution_count": 59,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "dummy_response"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "94eaf59b-c108-424c-8978-b1c86e141ccb",
+   "metadata": {},
+   "source": [
+    "## Step 6: See archives of queries"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "64003c53-de56-4bdc-a3c2-0c3e40031919",
+   "metadata": {},
+   "source": [
+    "She now wants to verify all the queries that she did on the real data. It is possible because an archive of all queries is kept in a secure database. With a function call she can see her queries, budget and associated responses."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 93,
+   "id": "008fd230-cdfd-4e03-91ce-5a60b06c106d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "previous_queries = client.get_previous_queries()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f2a34bc3-d1a5-4124-983f-ddc09dd1af7b",
+   "metadata": {},
+   "source": [
+    "Let's check the first query, which was with smartnoise-sql:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 94,
+   "id": "1795a54b-d04e-4687-8649-93982c84ad30",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'user_name': 'Dr. Antartica',\n",
+       " 'dataset_name': 'PENGUIN',\n",
+       " 'dp_librairy': 'diffprivlib',\n",
+       " 'client_input': {'dataset_name': 'PENGUIN',\n",
+       "  'diffprivlib_json': '{\"module\": \"diffprivlib\", \"version\": \"0.6.4\", \"pipeline\": [{\"type\": \"_dpl_type:StandardScaler\", \"name\": \"scaler\", \"params\": {\"with_mean\": true, \"with_std\": true, \"copy\": true, \"epsilon\": 0.5, \"bounds\": {\"_tuple\": true, \"_items\": [[30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0]]}, \"random_state\": null, \"accountant\": \"_dpl_instance:BudgetAccountant\"}}, {\"type\": \"_dpl_type:LogisticRegression\", \"name\": \"classifier\", \"params\": {\"tol\": 0.0001, \"C\": 1.0, \"fit_intercept\": true, \"random_state\": null, \"max_iter\": 100, \"verbose\": 0, \"warm_start\": false, \"n_jobs\": null, \"epsilon\": 1.0, \"data_norm\": 83.69469642643347, \"accountant\": \"_dpl_instance:BudgetAccountant\"}}]}',\n",
+       "  'feature_columns': ['bill_length_mm',\n",
+       "   'bill_depth_mm',\n",
+       "   'flipper_length_mm',\n",
+       "   'body_mass_g'],\n",
+       "  'target_columns': ['species'],\n",
+       "  'test_size': 0.2,\n",
+       "  'test_train_split_seed': 1,\n",
+       "  'imputer_strategy': 'drop'},\n",
+       " 'response': {'requested_by': 'Dr. Antartica',\n",
+       "  'query_response': {'score': 0.3880597014925373,\n",
+       "   'model': Pipeline(steps=[('scaler',\n",
+       "                    StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                   bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                           array([  65.,   23.,  250., 7000.])),\n",
+       "                                   epsilon=0.5)),\n",
+       "                   ('classifier',\n",
+       "                    LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                       data_norm=83.69469642643347))])},\n",
+       "  'spent_epsilon': 1.5,\n",
+       "  'spent_delta': 0.0},\n",
+       " 'timestamp': 1719828430.3111608}"
+      ]
+     },
+     "execution_count": 94,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "query_1 = previous_queries[0]\n",
+    "query_1"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7ba3baec-bbf9-49fa-a211-57d88cbc0e3a",
+   "metadata": {},
+   "source": [
+    "The second with opendp:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 95,
+   "id": "ef251e47-67d8-426b-9655-c16d32778579",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'user_name': 'Dr. Antartica',\n",
+       " 'dataset_name': 'PENGUIN',\n",
+       " 'dp_librairy': 'diffprivlib',\n",
+       " 'client_input': {'dataset_name': 'PENGUIN',\n",
+       "  'diffprivlib_json': '{\"module\": \"diffprivlib\", \"version\": \"0.6.4\", \"pipeline\": [{\"type\": \"_dpl_type:StandardScaler\", \"name\": \"scaler\", \"params\": {\"with_mean\": true, \"with_std\": true, \"copy\": true, \"epsilon\": 0.5, \"bounds\": {\"_tuple\": true, \"_items\": [[30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0]]}, \"random_state\": null, \"accountant\": \"_dpl_instance:BudgetAccountant\"}}, {\"type\": \"_dpl_type:LogisticRegression\", \"name\": \"classifier\", \"params\": {\"tol\": 0.0001, \"C\": 1.0, \"fit_intercept\": true, \"random_state\": null, \"max_iter\": 100, \"verbose\": 0, \"warm_start\": false, \"n_jobs\": null, \"epsilon\": 1.0, \"data_norm\": 83.69469642643347, \"accountant\": \"_dpl_instance:BudgetAccountant\"}}]}',\n",
+       "  'feature_columns': ['bill_length_mm',\n",
+       "   'bill_depth_mm',\n",
+       "   'flipper_length_mm',\n",
+       "   'body_mass_g'],\n",
+       "  'target_columns': ['species'],\n",
+       "  'test_size': 0.2,\n",
+       "  'test_train_split_seed': 1,\n",
+       "  'imputer_strategy': 'drop'},\n",
+       " 'response': {'requested_by': 'Dr. Antartica',\n",
+       "  'query_response': {'score': 0.3880597014925373,\n",
+       "   'model': Pipeline(steps=[('scaler',\n",
+       "                    StandardScaler(accountant=BudgetAccountant(spent_budget=[(0.5, 0)]),\n",
+       "                                   bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                           array([  65.,   23.,  250., 7000.])),\n",
+       "                                   epsilon=0.5)),\n",
+       "                   ('classifier',\n",
+       "                    LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
+       "                                       data_norm=83.69469642643347))])},\n",
+       "  'spent_epsilon': 1.5,\n",
+       "  'spent_delta': 0.0},\n",
+       " 'timestamp': 1719828430.3111608}"
+      ]
+     },
+     "execution_count": 95,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "query_2 = previous_queries[1]\n",
+    "query_1"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fe6332c1-932c-4912-a06d-4e66f5dada65",
+   "metadata": {},
+   "source": [
+    "And the last one with DiffPrivLib:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 96,
+   "id": "b2fa8943-f0e0-4902-9001-bd10fb22a653",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'user_name': 'Dr. Antartica',\n",
+       " 'dataset_name': 'PENGUIN',\n",
+       " 'dp_librairy': 'diffprivlib',\n",
+       " 'client_input': {'dataset_name': 'PENGUIN',\n",
+       "  'diffprivlib_json': '{\"module\": \"diffprivlib\", \"version\": \"0.6.4\", \"pipeline\": [{\"type\": \"_dpl_type:RandomForestClassifier\", \"name\": \"rf\", \"params\": {\"n_estimators\": 10, \"n_jobs\": 1, \"random_state\": null, \"verbose\": 0, \"warm_start\": false, \"max_depth\": 5, \"epsilon\": 2.0, \"bounds\": {\"_tuple\": true, \"_items\": [[30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0]]}, \"classes\": [\"Adelie\", \"Chinstrap\", \"Gentoo\"], \"shuffle\": false, \"accountant\": \"_dpl_instance:BudgetAccountant\"}}]}',\n",
+       "  'feature_columns': ['bill_length_mm',\n",
+       "   'bill_depth_mm',\n",
+       "   'flipper_length_mm',\n",
+       "   'body_mass_g'],\n",
+       "  'target_columns': ['species'],\n",
+       "  'test_size': 0.2,\n",
+       "  'test_train_split_seed': 1,\n",
+       "  'imputer_strategy': 'drop'},\n",
+       " 'response': {'requested_by': 'Dr. Antartica',\n",
+       "  'query_response': {'score': 0.6268656716417911,\n",
+       "   'model': Pipeline(steps=[('rf',\n",
+       "                    RandomForestClassifier(accountant=BudgetAccountant(spent_budget=[(2.0, 0)]),\n",
+       "                                           bounds=(array([  30.,   13.,  150., 2000.]),\n",
+       "                                                   array([  65.,   23.,  250., 7000.])),\n",
+       "                                           classes=['Adelie', 'Chinstrap',\n",
+       "                                                    'Gentoo'],\n",
+       "                                           epsilon=2.0))])},\n",
+       "  'spent_epsilon': 2.0,\n",
+       "  'spent_delta': 0.0},\n",
+       " 'timestamp': 1719829029.0730438}"
+      ]
+     },
+     "execution_count": 96,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "query_3 = previous_queries[-1]\n",
+    "query_3"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e2c8d40d-94b3-4d69-af99-0ec2936f233e",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "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.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/client/requirements.txt b/client/requirements.txt
index c5c3d5f7..06b352e6 100644
--- a/client/requirements.txt
+++ b/client/requirements.txt
@@ -1,8 +1,9 @@
-#diffprivlib==0.6.0
+diffprivlib==0.6.4
+diffprivlib_logger==0.0.3
 jupyter==1.0.0
-numpy==1.26.4
+numpy==1.23.2
 opendp==0.8.0
 opendp_logger==0.3.0
 pandas==2.2.2
 requests==2.32.0
-#scikit-learn>=1.1.2
\ No newline at end of file
+scikit-learn==1.4.0
diff --git a/client/setup.py b/client/setup.py
index 08f78856..95e72366 100644
--- a/client/setup.py
+++ b/client/setup.py
@@ -26,12 +26,15 @@
         "Programming Language :: Python :: 3.9",
     ],
     keywords=["opendp", "smartnoise-sql", "logger", "ast"],
-    python_requires=">=3.8, <4",
+    python_requires=">=3.10, <4",
     install_requires=[
+        "diffprivlib>=0.6.4",
+        "diffprivlib_logger>=0.0.3",
+        "numpy>=1.23.2",
         "opendp==0.8.0",
         "opendp_logger==0.3.0",
-        "numpy==1.26.4",
-        "requests==2.32.0",
-        "pandas==2.2.2",
+        "pandas>=2.2.2",
+        "requests>=2.32.0",
+        "scikit-learn==1.4.0",
     ],
 )
diff --git a/server/lomas_server/admin_database/admin_database.py b/server/lomas_server/admin_database/admin_database.py
index ae0c3198..e3822ad1 100644
--- a/server/lomas_server/admin_database/admin_database.py
+++ b/server/lomas_server/admin_database/admin_database.py
@@ -4,9 +4,8 @@
 from abc import ABC, abstractmethod
 from typing import Callable, Dict, List
 
-from constants import DPLibraries
+from constants import MODEL_INPUT_TO_LIB
 from utils.error_handler import (
-    InternalServerException,
     InvalidQueryException,
     UnauthorizedAccessException,
 )
@@ -442,22 +441,16 @@ def prepare_save_query(
         Returns:
             dict: The query archive dictionary.
         """
+        model_input = query_json.__class__.__name__
         to_archive = {
             "user_name": user_name,
             "dataset_name": query_json.dataset_name,
+            "dp_librairy": MODEL_INPUT_TO_LIB[model_input],
             "client_input": query_json.model_dump(),
             "response": response,
             "timestamp": time.time(),
         }
-        match query_json.__class__.__name__:
-            case "SNSQLInp":
-                to_archive["dp_librairy"] = DPLibraries.SMARTNOISE_SQL
-            case "OpenDPInp":
-                to_archive["dp_librairy"] = DPLibraries.OPENDP
-            case _:
-                raise InternalServerException(
-                    f"Unknown query input: {query_json.__class__.__name__}"
-                )
+
         return to_archive
 
     @abstractmethod
diff --git a/server/lomas_server/constants.py b/server/lomas_server/constants.py
index 694e3d6a..9f1e67d3 100644
--- a/server/lomas_server/constants.py
+++ b/server/lomas_server/constants.py
@@ -78,6 +78,15 @@ class DPLibraries(StrEnum):
 
     SMARTNOISE_SQL = "smartnoise_sql"
     OPENDP = "opendp"
+    DIFFPRIVLIB = "diffprivlib"
+
+
+# Query model input to DP librairy
+MODEL_INPUT_TO_LIB = {
+    "SNSQLInp": DPLibraries.SMARTNOISE_SQL,
+    "OpenDPInp": DPLibraries.OPENDP,
+    "DiffPrivLibInp": DPLibraries.DIFFPRIVLIB,
+}
 
 
 # Private Databases
@@ -130,3 +139,7 @@ class OpenDPDatasetInputMetric(StrEnum):
 # Smartnoise sql
 STATS = ["count", "sum_int", "sum_large_int", "sum_float", "threshold"]
 MAX_NAN_ITERATION = 5
+
+
+# Data preprocessing
+NUMERICAL_DTYPES = ["int16", "int32", "int64", "float16", "float32", "float64"]
diff --git a/server/lomas_server/dp_queries/dp_libraries/diffprivlib.py b/server/lomas_server/dp_queries/dp_libraries/diffprivlib.py
new file mode 100644
index 00000000..e5dac0c7
--- /dev/null
+++ b/server/lomas_server/dp_queries/dp_libraries/diffprivlib.py
@@ -0,0 +1,125 @@
+import pickle
+import warnings
+from base64 import b64encode
+from typing import Dict
+
+import pandas as pd
+from diffprivlib.utils import PrivacyLeakWarning
+from diffprivlib_logger import deserialise_pipeline
+from sklearn.pipeline import Pipeline
+
+from constants import DPLibraries
+from dp_queries.dp_libraries.diffprivlib_utils import (
+    handle_missing_data,
+    split_train_test_data,
+)
+from dp_queries.dp_querier import DPQuerier
+from utils.error_handler import ExternalLibraryException
+from utils.input_models import DiffPrivLibInp
+
+
+class DiffPrivLibQuerier(DPQuerier):
+    """
+    Concrete implementation of the DPQuerier ABC for the DiffPrivLib library.
+    """
+
+    def fit_model_on_data(
+        self, query_json: DiffPrivLibInp
+    ) -> tuple[Pipeline, pd.DataFrame, pd.DataFrame]:
+        """Perform necessary steps to fit the model on the data
+
+        Args:
+            query_json (BaseModel): The JSON request object for the query.
+
+        Raises:
+            ExternalLibraryException: For exceptions from libraries
+                external to this package.
+
+        Returns:
+            fitted_dpl_pipeline (dpl model): the fitted model on the training data
+            x_test (pd.DataFrame): test data feature
+            y_test (pd.DataFrame): test data target
+        """
+        # Prepare data
+        raw_data = self.private_dataset.get_pandas_df()
+        data = handle_missing_data(raw_data, query_json.imputer_strategy)
+        x_train, x_test, y_train, y_test = split_train_test_data(
+            data, query_json
+        )
+
+        # Prepare DiffPrivLib pipeline
+        dpl_pipeline = deserialise_pipeline(query_json.diffprivlib_json)
+
+        # Fit the pipeline on the training set
+        warnings.simplefilter("error", PrivacyLeakWarning)
+        try:
+            fitted_dpl_pipeline = dpl_pipeline.fit(x_train, y_train)
+        except PrivacyLeakWarning as e:
+            raise ExternalLibraryException(
+                DPLibraries.DIFFPRIVLIB,
+                f"PrivacyLeakWarning: {e}. "
+                + "Lomas server cannot fit pipeline on data, "
+                + "PrivacyLeakWarning is a blocker.",
+            ) from e
+        except Exception as e:
+            raise ExternalLibraryException(
+                DPLibraries.DIFFPRIVLIB,
+                f"Cannot fit pipeline on data because {e}",
+            ) from e
+
+        return fitted_dpl_pipeline, x_test, y_test
+
+    def cost(self, query_json: DiffPrivLibInp) -> tuple[float, float]:
+        """Estimate cost of query
+
+        Args:
+            query_json (BaseModel): The JSON request object for the query.
+
+        Raises:
+            ExternalLibraryException: For exceptions from libraries
+                external to this package.
+
+        Returns:
+            tuple[float, float]: The tuple of costs, the first value
+                is the epsilon cost, the second value is the delta value.
+        """
+        fitted_dpl_pipeline, _, _ = self.fit_model_on_data(query_json)
+
+        # Compute budget
+        spent_epsilon = 0.0
+        spent_delta = 0.0
+        for step in fitted_dpl_pipeline.steps:
+            spent_epsilon += step[1].accountant.spent_budget[0][0]
+            spent_delta += step[1].accountant.spent_budget[0][1]
+        return spent_epsilon, spent_delta
+
+    def query(self, query_json: DiffPrivLibInp) -> Dict:
+        """Perform the query and return the response.
+
+        Args:
+            query_json (BaseModel): The JSON request object for the query.
+
+        Raises:
+            ExternalLibraryException: For exceptions from libraries
+                external to this package.
+            InvalidQueryException: If the budget values are too small to
+                perform the query.
+
+        Returns:
+            dict: The dictionary encoding of the resulting pd.DataFrame.
+        """
+        fitted_dpl_pipeline, x_test, y_test = self.fit_model_on_data(
+            query_json
+        )
+
+        # Model accuracy
+        score = fitted_dpl_pipeline.score(x_test, y_test)
+
+        # Serialise model
+        pickled_model = b64encode(pickle.dumps(fitted_dpl_pipeline))
+
+        query_response = {
+            "score": score,
+            "model": pickled_model.decode("utf-8"),
+        }
+        return query_response
diff --git a/server/lomas_server/dp_queries/dp_libraries/diffprivlib_utils.py b/server/lomas_server/dp_queries/dp_libraries/diffprivlib_utils.py
new file mode 100644
index 00000000..d817bd49
--- /dev/null
+++ b/server/lomas_server/dp_queries/dp_libraries/diffprivlib_utils.py
@@ -0,0 +1,111 @@
+import numpy as np
+import pandas as pd
+from sklearn.impute import SimpleImputer
+from sklearn.model_selection import train_test_split
+
+from constants import NUMERICAL_DTYPES
+from utils.error_handler import InvalidQueryException
+from utils.input_models import DiffPrivLibInp
+
+
+def handle_missing_data(
+    df: pd.DataFrame, imputer_strategy: str
+) -> pd.DataFrame:
+    """Impute missing data based on given imputation strategy for NaNs
+    Args:
+        df (pd.DataFrame): dataframe with the data
+        imputer_strategy (str): string to indicate imputatation for NaNs
+            "drop": will drop all rows with missing values
+            "mean": will replace values by the mean of the column values
+            "median": will replace values by the median of the column values
+            "most_frequent": : will replace values by the most frequent values
+
+    Raises:
+        InvalidQueryException: If the "imputer_strategy" does not exist
+
+    Returns:
+        df (pd.DataFrame): dataframe with the imputed data
+    """
+    dtypes = df.dtypes
+
+    if imputer_strategy == "drop":
+        df = df.dropna()
+    elif imputer_strategy in ["mean", "median"]:
+        numerical_cols = df.select_dtypes(
+            include=NUMERICAL_DTYPES
+        ).columns.tolist()
+        categorical_cols = [
+            col for col in df.columns if col not in numerical_cols
+        ]
+
+        # Impute numerical features using given strategy
+        imp_mean = SimpleImputer(strategy=imputer_strategy)
+        df_num_imputed = imp_mean.fit_transform(df[numerical_cols])
+
+        # Impute categorical features with most frequent value
+        imp_most_frequent = SimpleImputer(strategy="most_frequent")
+        df[categorical_cols] = df[categorical_cols].astype("object")
+        df[categorical_cols] = df[categorical_cols].replace({pd.NA: np.nan})
+        df_cat_imputed = imp_most_frequent.fit_transform(df[categorical_cols])
+
+        # Combine imputed dataframes
+        df = pd.concat(
+            [
+                pd.DataFrame(df_num_imputed, columns=numerical_cols),
+                pd.DataFrame(df_cat_imputed, columns=categorical_cols),
+            ],
+            axis=1,
+        )
+    elif imputer_strategy == "most_frequent":
+        # Impute all features with most frequent value
+        imp_most_frequent = SimpleImputer(strategy=imputer_strategy)
+        df[df.columns] = df[df.columns].astype("object")
+        df[df.columns] = df[df.columns].replace({pd.NA: np.nan})
+        df = pd.DataFrame(
+            imp_most_frequent.fit_transform(df), columns=df.columns
+        )
+    else:
+        raise InvalidQueryException(
+            f"Imputation strategy {imputer_strategy} not supported."
+        )
+
+    df = df.astype(dtype=dtypes)
+    return df
+
+
+def split_train_test_data(
+    df: pd.DataFrame, query_json: DiffPrivLibInp
+) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame]:
+    """Split the data between train and test set
+    Args:
+        df (pd.DataFrame): dataframe with the data
+        query_json (DiffPrivLibInp): user input query indication
+            feature_columns (list[str]): columns from data to use as features
+            target_columns (list[str]): columns from data to use as target (to predict)
+            test_size (float): proportion of data in the test set
+            test_train_split_seed (int): seed for the random train-test split
+
+    Returns:
+        x_train (pd.DataFrame): training data features
+        x_test (pd.DataFrame): testing data features
+        y_train (pd.DataFrame): training data target
+        y_test (pd.DataFrame): testing data target
+    """
+    feature_data = df[query_json.feature_columns]
+
+    if query_json.target_columns is None:
+        x_train, x_test = train_test_split(
+            feature_data,
+            test_size=query_json.test_size,
+            random_state=query_json.test_train_split_seed,
+        )
+        y_train, y_test = None, None
+    else:
+        label_data = df[query_json.target_columns]
+        x_train, x_test, y_train, y_test = train_test_split(
+            feature_data,
+            label_data,
+            test_size=query_json.test_size,
+            random_state=query_json.test_train_split_seed,
+        )
+    return x_train, x_test, y_train, y_test
diff --git a/server/lomas_server/dp_queries/dp_libraries/utils.py b/server/lomas_server/dp_queries/dp_libraries/utils.py
index ccfb3f11..674653c0 100644
--- a/server/lomas_server/dp_queries/dp_libraries/utils.py
+++ b/server/lomas_server/dp_queries/dp_libraries/utils.py
@@ -1,4 +1,5 @@
 from constants import DPLibraries
+from dp_queries.dp_libraries.diffprivlib import DiffPrivLibQuerier
 from dp_queries.dp_libraries.opendp import OpenDPQuerier
 from dp_queries.dp_libraries.smartnoise_sql import SmartnoiseSQLQuerier
 from dp_queries.dp_querier import DPQuerier
@@ -27,6 +28,9 @@ def querier_factory(lib: str, private_dataset: PrivateDataset) -> DPQuerier:
         case DPLibraries.OPENDP:
             querier = OpenDPQuerier(private_dataset)
 
+        case DPLibraries.DIFFPRIVLIB:
+            querier = DiffPrivLibQuerier(private_dataset)
+
         case _:
             raise InternalServerException(f"Unknown library: {lib}")
     return querier
diff --git a/server/lomas_server/routes_dp.py b/server/lomas_server/routes_dp.py
index 17a43b0e..8687845b 100644
--- a/server/lomas_server/routes_dp.py
+++ b/server/lomas_server/routes_dp.py
@@ -6,6 +6,8 @@
 from dp_queries.dummy_dataset import get_dummy_dataset_for_query
 from utils.error_handler import KNOWN_EXCEPTIONS, InternalServerException
 from utils.example_inputs import (
+    example_diffprivlib,
+    example_dummy_diffprivlib,
     example_dummy_opendp,
     example_dummy_smartnoise_sql,
     example_opendp,
@@ -13,6 +15,8 @@
     example_smartnoise_sql_cost,
 )
 from utils.input_models import (
+    DiffPrivLibInp,
+    DummyDiffPrivLibInp,
     DummyOpenDPInp,
     DummySNSQLInp,
     OpenDPInp,
@@ -382,3 +386,172 @@ def estimate_opendp_cost(
         raise InternalServerException(e) from e
 
     return JSONResponse(content=response)
+
+
+@router.post(
+    "/diffprivlib_query",
+    dependencies=[Depends(server_live)],
+    tags=["USER_QUERY"],
+)
+def diffprivlib_query_handler(
+    query_json: DiffPrivLibInp = Body(example_diffprivlib),
+    user_name: str = Header(None),
+):
+    """
+    Handles queries for the DiffPrivLib Library.
+
+    Args:
+        request (Request): Raw request object.
+        query_json (OpenDPInp, optional): A JSON object containing the following:
+            - pipeline: The DiffPrivLib pipeline for the query.
+            - feature_columns: the list of feature column to train
+            - target_columns: the list of target column to predict
+            - test_size: proportion of the test set
+            - test_train_split_seed: seed for the random train test split,
+            - imputer_strategy: imputation strategy
+
+            Defaults to Body(example_diffprivlib).
+
+        user_name (str, optional): The user name.
+            Defaults to Header(None).
+
+    Raises:
+        ExternalLibraryException: For exceptions from libraries
+            external to this package.
+        InternalServerException: For any other unforseen exceptions.
+        InvalidQueryException: The pipeline does not contain a "measurement",
+            there is not enough budget or the dataset does not exist.
+        UnauthorizedAccessException: A query is already ongoing for this user,
+            the user does not exist or does not have access to the dataset.
+
+    Returns:
+        JSONResponse: A JSON object containing the following:
+            - requested_by (str): The user name.
+            - query_response (pd.DataFrame): A DataFrame containing
+              the query response.
+            - spent_epsilon (float): The amount of epsilon budget spent
+              for the query.
+            - spent_delta (float): The amount of delta budget spent
+              for the query.
+    """
+    from app import app  # pylint: disable=C0415
+
+    try:
+        response = app.state.query_handler.handle_query(
+            DPLibraries.DIFFPRIVLIB, query_json, user_name
+        )
+    except KNOWN_EXCEPTIONS as e:
+        raise e
+    except Exception as e:
+        raise InternalServerException(e) from e
+
+    return response
+
+
+@router.post(
+    "/dummy_diffprivlib_query",
+    dependencies=[Depends(server_live)],
+    tags=["USER_DUMMY"],
+)
+def dummy_diffprivlib_query_handler(
+    query_json: DummyDiffPrivLibInp = Body(example_dummy_diffprivlib),
+):
+    """
+    Handles queries on dummy datasets for the DiffPrivLib library.
+
+    Args:
+        request (Request): Raw request object.
+        query_json (DiffPrivLibInp, optional): A JSON object containing the following:
+            - pipeline: The DiffPrivLib pipeline for the query.
+            - feature_columns: the list of feature column to train
+            - target_columns: the list of target column to predict
+            - test_size: proportion of the test set
+            - test_train_split_seed: seed for the random train test split,
+            - imputer_strategy: imputation strategy
+            - nb_rows (int, optional): The number of rows in the dummy dataset
+              (default: 100).
+            - seed (int, optional): The random seed for generating
+              the dummy dataset (default: 42).
+              Defaults to Body(example_dummy_diffprivlib)
+
+    Raises:
+        ExternalLibraryException: For exceptions from libraries
+            external to this package.
+        InternalServerException: For any other unforseen exceptions.
+        InvalidQueryException: If there is not enough budget or the dataset
+            does not exist.
+
+    Returns:
+        JSONResponse: A JSON object containing:
+            - query_response (pd.DataFrame): a DataFrame containing
+              the query response.
+    """
+    from app import app  # pylint: disable=C0415
+
+    ds_private_dataset = get_dummy_dataset_for_query(
+        app.state.admin_database, query_json
+    )
+    dummy_querier = querier_factory(
+        DPLibraries.DIFFPRIVLIB, private_dataset=ds_private_dataset
+    )
+
+    try:
+        _ = dummy_querier.cost(query_json)  # verify cost works
+        response = dummy_querier.query(query_json)
+    except KNOWN_EXCEPTIONS as e:
+        raise e
+    except Exception as e:
+        raise InternalServerException(e) from e
+
+    return JSONResponse(content={"query_response": response})
+
+
+@router.post(
+    "/estimate_diffprivlib_cost",
+    dependencies=[Depends(server_live)],
+    tags=["USER_QUERY"],
+)
+def estimate_diffprivlib_cost(
+    query_json: DiffPrivLibInp = Body(example_diffprivlib),
+):
+    """
+    Estimates the privacy loss budget cost of an DiffPrivLib query.
+
+    Args:
+        request (Request): Raw request object
+        query_json (DiffPrivLibInp, optional):
+        A JSON object containing the following:
+            - pipeline: The DiffPrivLib pipeline for the query.
+            - feature_columns: the list of feature column to train
+            - target_columns: the list of target column to predict
+            - test_size: proportion of the test set
+            - test_train_split_seed: seed for the random train test split,
+            - imputer_strategy: imputation strategy
+
+            Defaults to Body(example_dummy_diffprivlib).
+
+    Raises:
+        ExternalLibraryException: For exceptions from libraries
+            external to this package.
+        InternalServerException: For any other unforseen exceptions.
+        InvalidQueryException: The dataset does not exist or the
+            pipeline does not contain a measurement.
+
+    Returns:
+        JSONResponse: A JSON object containing:
+            - epsilon_cost (float): The estimated epsilon cost.
+            - delta_cost (float): The estimated delta cost.
+    """
+    from app import app  # pylint: disable=C0415
+
+    try:
+        response = app.state.query_handler.estimate_cost(
+            DPLibraries.DIFFPRIVLIB,
+            query_json,
+        )
+    except KNOWN_EXCEPTIONS as e:
+        raise e
+    except Exception as e:
+        raise InternalServerException(e) from e
+
+    return response
diff --git a/server/lomas_server/utils/example_inputs.py b/server/lomas_server/utils/example_inputs.py
index 0da8dadf..0e7aa6b8 100644
--- a/server/lomas_server/utils/example_inputs.py
+++ b/server/lomas_server/utils/example_inputs.py
@@ -119,3 +119,75 @@
     "dummy_nb_rows": DUMMY_NB_ROWS,
     "dummy_seed": DUMMY_SEED,
 }
+
+DIFFPRIVLIB_JSON = (
+    '{"module": "diffprivlib", '
+    '"version": "0.6.4", '
+    '"pipeline": ['
+    "{"
+    '"type": "_dpl_type:StandardScaler", '
+    '"name": "scaler", '
+    '"params": {'
+    '"with_mean": true, '
+    '"with_std": true, '
+    '"copy": true, '
+    '"epsilon": 0.5, '
+    '"bounds": {'
+    '"_tuple": true, '
+    '"_items": [[30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0]]'
+    "}, "
+    '"random_state": null, '
+    '"accountant": "_dpl_instance:BudgetAccountant"'
+    "}"
+    "}, "
+    "{"
+    '"type": "_dpl_type:LogisticRegression", '
+    '"name": "classifier", '
+    '"params": {'
+    '"tol": 0.0001, '
+    '"C": 1.0, '
+    '"fit_intercept": true, '
+    '"random_state": null, '
+    '"max_iter": 100, '
+    '"verbose": 0, '
+    '"warm_start": false, '
+    '"n_jobs": null, '
+    '"epsilon": 1.0, '
+    '"data_norm": 83.69469642643347, '
+    '"accountant": "_dpl_instance:BudgetAccountant"'
+    "}"
+    "}"
+    "]"
+    "}"
+)
+example_diffprivlib = {
+    "dataset_name": PENGUIN_DATASET,
+    "diffprivlib_json": DIFFPRIVLIB_JSON,
+    "feature_columns": [
+        "bill_length_mm",
+        "bill_depth_mm",
+        "flipper_length_mm",
+        "body_mass_g",
+    ],
+    "target_columns": ["species"],
+    "test_size": 0.2,
+    "test_train_split_seed": 1,
+    "imputer_strategy": "drop",
+}
+
+example_dummy_diffprivlib = {
+    "dataset_name": PENGUIN_DATASET,
+    "diffprivlib_json": DIFFPRIVLIB_JSON,
+    "feature_columns": [
+        "bill_length_mm",
+        "bill_depth_mm",
+        "flipper_length_mm",
+        "body_mass_g",
+    ],
+    "target_columns": ["species"],
+    "test_train_split_seed": 1,
+    "test_size": 0.2,
+    "imputer_strategy": "drop",
+    "dummy_nb_rows": DUMMY_NB_ROWS,
+    "dummy_seed": DUMMY_SEED,
+}
diff --git a/server/lomas_server/utils/input_models.py b/server/lomas_server/utils/input_models.py
index 4e5db0b5..34fabba8 100644
--- a/server/lomas_server/utils/input_models.py
+++ b/server/lomas_server/utils/input_models.py
@@ -77,3 +77,29 @@ class DummyOpenDPInp(BaseModel):
     dummy_nb_rows: int
     dummy_seed: int
     fixed_delta: Optional[float] = None
+
+
+class DiffPrivLibInp(BaseModel):
+    """Model input for a diffprivlib query"""
+
+    dataset_name: str
+    diffprivlib_json: str
+    feature_columns: list
+    target_columns: Optional[list]
+    test_size: float
+    test_train_split_seed: int
+    imputer_strategy: str
+
+
+class DummyDiffPrivLibInp(BaseModel):
+    """Model input for a dummy diffprivlib query"""
+
+    dataset_name: str
+    diffprivlib_json: str
+    feature_columns: list
+    target_columns: Optional[list]
+    test_size: float
+    test_train_split_seed: int
+    imputer_strategy: str
+    dummy_nb_rows: int
+    dummy_seed: int
diff --git a/server/requirements.txt b/server/requirements.txt
index 0b615a40..626d87f5 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -1,11 +1,14 @@
 boto3==1.34.115
+diffprivlib==0.6.4
+diffprivlib_logger==0.0.3
 fastapi==0.109.1
 httpx==0.27.0
-numpy==1.26.4
+numpy==1.23.2
 opendp==0.8.0
 opendp-logger==0.3.0
 pandas==2.2.2
 pyaml==23.9.5
 pymongo==4.6.3
+scikit-learn==1.4.0
 smartnoise-sql==1.0.3
 uvicorn==0.29.0


Test patch:
diff --git a/client/tests/test_opendp.py b/client/tests/test_opendp.py
deleted file mode 100644
index 45e5ba54..00000000
--- a/client/tests/test_opendp.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import opendp.transformations as trans
-from opendp.mod import enable_features
-from opendp_logger import enable_logging, make_load_json
-
-enable_logging()
-enable_features("contrib")
-
-
-def test_serialize() -> None:
-    # Random pipeline of transformation
-    pipeline = (
-        # Convert data into a dataframe where columns are of type Vec<str>
-        trans.make_split_dataframe(separator=",", col_names=["hello", "world"])
-        >>
-        # Selects a column of df, Vec<str>
-        trans.make_select_column(key="income", TOA=str)
-    )
-
-    # Ast to json to be sent
-    json_obj = pipeline.to_json()
-
-    expected_json = (
-        '{"version": "0.8.0", '
-        '"ast": {'
-        '"_type": "constructor", '
-        '"func": "make_chain_tt", '
-        '"module": "combinators", '
-        '"args": ['
-        "{"
-        '"_type": "constructor", '
-        '"func": "make_select_column", '
-        '"module": "transformations", '
-        '"kwargs": {"key": "income", "TOA": "String"}'
-        "}, "
-        "{"
-        '"_type": "constructor", '
-        '"func": "make_split_dataframe", '
-        '"module": "transformations", '
-        '"kwargs": {"separator": ",", "col_names": {'
-        '"_type": "list", '
-        '"_items": ["hello", "world"]'
-        "}}"
-        "}"
-        "]"
-        "}"
-        "}"
-    )
-
-    # Assert expected value
-    assert json_obj == expected_json
-
-
-def test_deserialize() -> None:
-    pipeline_json = (
-        '{"version": "0.8.0", '
-        '"ast": {'
-        '"_type": "constructor", '
-        '"func": "make_chain_tt", '
-        '"module": "combinators", '
-        '"args": ['
-        "{"
-        '"_type": "constructor", '
-        '"func": "make_select_column", '
-        '"module": "transformations", '
-        '"kwargs": {"key": "income", "TOA": "String"}'
-        "}, "
-        "{"
-        '"_type": "constructor", '
-        '"func": "make_split_dataframe", '
-        '"module": "transformations", '
-        '"kwargs": {"separator": ",", "col_names": {'
-        '"_type": "list", '
-        '"_items": ["hello", "world"]'
-        "}}"
-        "}"
-        "]"
-        "}"
-        "}"
-    )
-
-    pipeline = make_load_json(pipeline_json)
-
-    expected_pipeline = (
-        # Convert data into a dataframe where columns are of type Vec<str>
-        trans.make_split_dataframe(separator=",", col_names=["hello", "world"])
-        >>
-        # Selects a column of df, Vec<str>
-        trans.make_select_column(key="income", TOA=str)
-    )
-
-    # Assert expected value
-    assert str(pipeline) == str(expected_pipeline)
diff --git a/server/lomas_server/tests/test_api.py b/server/lomas_server/tests/test_api.py
index 7ec72f7f..f603c690 100644
--- a/server/lomas_server/tests/test_api.py
+++ b/server/lomas_server/tests/test_api.py
@@ -39,6 +39,7 @@
     example_smartnoise_sql,
     example_smartnoise_sql_cost,
 )
+from utils.loggr import LOG
 
 INITAL_EPSILON = 10
 INITIAL_DELTA = 0.005
@@ -716,6 +717,9 @@ def test_get_previous_queries(self) -> None:
             response_2 = client.post(
                 "/get_previous_queries", json=example_get_admin_db_data
             )
+            LOG.error(response_2)
+            response_dict = json.loads(response.content.decode("utf8"))
+            LOG.error(response_dict)
             assert response_2.status_code == status.HTTP_200_OK
 
             response_dict_2 = json.loads(response_2.content.decode("utf8"))
diff --git a/server/lomas_server/tests/test_api_diffprivlib.py b/server/lomas_server/tests/test_api_diffprivlib.py
new file mode 100644
index 00000000..079a4890
--- /dev/null
+++ b/server/lomas_server/tests/test_api_diffprivlib.py
@@ -0,0 +1,346 @@
+import json
+import warnings
+
+from diffprivlib import models
+from diffprivlib.utils import (
+    DiffprivlibCompatibilityWarning,
+    PrivacyLeakWarning,
+)
+from diffprivlib_logger import serialise_pipeline
+from fastapi import status
+from fastapi.testclient import TestClient
+from sklearn.pipeline import Pipeline
+
+from app import app
+from constants import DPLibraries
+from tests.test_api import TestRootAPIEndpoint
+from utils.example_inputs import example_diffprivlib, example_dummy_diffprivlib
+
+
+def validate_pipeline(response):
+    """Validate that the pipeline ran successfully
+    Returns a model and a score.
+    """
+    assert response.status_code == status.HTTP_200_OK
+    response_dict = json.loads(response.content.decode("utf8"))
+    assert response_dict["query_response"]["score"]
+    assert response_dict["query_response"]["model"]
+
+
+class TestDiffPrivLibEndpoint(TestRootAPIEndpoint):  # pylint: disable=R0904
+    """
+    Test DiffPrivLib Endpoint with different models
+    """
+
+    def test_diffprivlib_query(self) -> None:
+        """Test diffprivlib query"""
+        with TestClient(app, headers=self.headers) as client:
+            # Expect to work
+            response = client.post(
+                "/diffprivlib_query",
+                json=example_diffprivlib,
+                headers=self.headers,
+            )
+            assert response.status_code == status.HTTP_200_OK
+
+            response_dict = json.loads(response.content.decode("utf8"))
+            assert response_dict["requested_by"] == self.user_name
+            assert response_dict["query_response"]["score"] >= 0
+            assert response_dict["query_response"]["model"]
+            assert response_dict["spent_epsilon"] > 0
+            assert response_dict["spent_delta"] == 0
+
+            # # Should work for different imputation strategy (but does not yet #255)
+            def test_imputation(diffprivlib_body, imputer_strategy):
+                diffprivlib_body = dict(diffprivlib_body)
+                diffprivlib_body["imputer_strategy"] = imputer_strategy
+                response = client.post(
+                    "/diffprivlib_query",
+                    json=diffprivlib_body,
+                    headers=self.headers,
+                )
+                return response
+
+            response = test_imputation(example_diffprivlib, "mean")
+            response_dict = json.loads(response.content.decode("utf8"))
+            assert response.status_code == status.HTTP_200_OK
+
+            response = test_imputation(example_diffprivlib, "median")
+            assert response.status_code == status.HTTP_200_OK
+
+            response = test_imputation(example_diffprivlib, "most_frequent")
+            assert response.status_code == status.HTTP_200_OK
+
+            # Should not work unknow imputation strategy
+            response = test_imputation(example_diffprivlib, "i_do_not_exist")
+            assert response.status_code == status.HTTP_400_BAD_REQUEST
+            assert response.json() == {
+                "InvalidQueryException": ""
+                + "Imputation strategy i_do_not_exist not supported."
+            }
+
+            # Should not work: Privacy Leak Warning
+            warnings.simplefilter("error", PrivacyLeakWarning)
+            diffprivlib_body = dict(example_diffprivlib)
+            dpl_pipeline = Pipeline(
+                [
+                    ("scaler", models.StandardScaler(epsilon=0.5)),
+                    ("classifier", models.LogisticRegression(epsilon=1.0)),
+                ]
+            )
+            dpl_string = serialise_pipeline(dpl_pipeline)
+            diffprivlib_body["diffprivlib_json"] = dpl_string
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
+            assert response.json() == {
+                "ExternalLibraryException": "PrivacyLeakWarning: "
+                + "Bounds parameter hasn't been specified, so falling back to "
+                + "determining bounds from the data.\n "
+                + "This will result in additional privacy leakage.  "
+                + "To ensure differential privacy with no additional privacy "
+                + "loss, specify `bounds` for each valued returned by "
+                + "np.mean().. "
+                + "Lomas server cannot fit pipeline on data, "
+                + "PrivacyLeakWarning is a blocker.",
+                "library": DPLibraries.DIFFPRIVLIB,
+            }
+
+            # Should not work: Compatibility Warning
+            warnings.simplefilter("error", DiffprivlibCompatibilityWarning)
+            with self.assertRaises(DiffprivlibCompatibilityWarning):
+                Pipeline(
+                    [
+                        ("scaler", models.StandardScaler(epsilon=0.5)),
+                        (
+                            "classifier",
+                            models.LogisticRegression(
+                                epsilon=1.0, svd_solver="full"
+                            ),
+                        ),
+                    ]
+                )
+
+    def test_logistic_regression_models(self) -> None:
+        """Test diffprivlib query: Logistic Regression"""
+        with TestClient(app, headers=self.headers) as client:
+            bounds = ([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])
+
+            # Test Logistic Regression
+            pipeline = Pipeline(
+                [
+                    (
+                        "scaler",
+                        models.StandardScaler(epsilon=0.5, bounds=bounds),
+                    ),
+                    (
+                        "classifier",
+                        models.LogisticRegression(
+                            epsilon=1.0, data_norm=83.69
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_linear_regression_models(self) -> None:
+        """Test diffprivlib query: Linear Regression"""
+        with TestClient(app, headers=self.headers) as client:
+
+            # Test Linear Regression
+            pipeline = Pipeline(
+                [
+                    (
+                        "lr",
+                        models.LinearRegression(
+                            epsilon=2.0,
+                            bounds_X=(30.0, 65.0),
+                            bounds_y=(13.0, 23.0),
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            diffprivlib_body["feature_columns"] = ["bill_length_mm"]
+            diffprivlib_body["target_columns"] = ["bill_length_mm"]
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_naives_bayes_model(self) -> None:
+        """Test diffprivlib query: Gaussian Naives Bayes"""
+        with TestClient(app, headers=self.headers) as client:
+            bounds = ([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])
+            # Test Gaussian Naives Bayes
+            pipeline = Pipeline(
+                [
+                    (
+                        "scaler",
+                        models.StandardScaler(epsilon=0.5, bounds=bounds),
+                    ),
+                    (
+                        "gaussian",
+                        models.GaussianNB(
+                            epsilon=1.0, bounds=bounds, priors=(0.3, 0.3, 0.4)
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_trees_models(self) -> None:
+        """Test diffprivlib query: Random Forest, Decision Tree"""
+        with TestClient(app, headers=self.headers) as client:
+            bounds = ([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])
+
+            # Test Random Forest
+            pipeline = Pipeline(
+                [
+                    (
+                        "rf",
+                        models.RandomForestClassifier(
+                            n_estimators=10,
+                            epsilon=2.0,
+                            bounds=bounds,
+                            classes=["Adelie", "Chinstrap", "Gentoo"],
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+            # Test Decision Tree Classifier
+            pipeline = Pipeline(
+                [
+                    (
+                        "dtc",
+                        models.DecisionTreeClassifier(
+                            epsilon=2.0,
+                            bounds=bounds,
+                            classes=["Adelie", "Chinstrap", "Gentoo"],
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_clustering_models(self) -> None:
+        """Test diffprivlib query: K-Means"""
+        with TestClient(app, headers=self.headers) as client:
+            bounds = ([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])
+
+            # Test K-MEANS
+            pipeline = Pipeline(
+                [
+                    (
+                        "kmeans",
+                        models.KMeans(
+                            n_clusters=8, epsilon=2.0, bounds=bounds
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+            diffprivlib_body["target_columns"] = None
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_dimension_reduction_models(self) -> None:
+        """Test diffprivlib query: PCA"""
+        with TestClient(app, headers=self.headers) as client:
+            bounds = ([30.0, 13.0, 150.0, 2000.0], [65.0, 23.0, 250.0, 7000.0])
+            # Test PCA
+            pipeline = Pipeline(
+                [
+                    (
+                        "pca",
+                        models.PCA(
+                            n_components=8,
+                            epsilon=2.0,
+                            bounds=bounds,
+                            data_norm=100,
+                        ),
+                    ),
+                ]
+            )
+            diffprivlib_body = dict(example_diffprivlib)
+            diffprivlib_body["diffprivlib_json"] = serialise_pipeline(pipeline)
+            response = client.post(
+                "/diffprivlib_query",
+                json=diffprivlib_body,
+                headers=self.headers,
+            )
+            validate_pipeline(response)
+
+    def test_dummy_diffprivlib_query(self) -> None:
+        """test_dummy_diffprivlib_query"""
+        with TestClient(app) as client:
+            # Expect to work
+            response = client.post(
+                "/dummy_diffprivlib_query", json=example_dummy_diffprivlib
+            )
+            assert response.status_code == status.HTTP_200_OK
+
+            response_dict = json.loads(response.content.decode("utf8"))
+            assert response_dict["query_response"]["score"] > 0
+            assert response_dict["query_response"]["model"]
+
+    def test_diffprivlib_cost(self) -> None:
+        """test_diffprivlib_cost"""
+        with TestClient(app) as client:
+            # Expect to work
+            response = client.post(
+                "/estimate_diffprivlib_cost", json=example_diffprivlib
+            )
+            assert response.status_code == status.HTTP_200_OK
+
+            response_dict = json.loads(response.content.decode("utf8"))
+            assert response_dict["epsilon_cost"] == 1.5
+            assert response_dict["delta_cost"] == 0