Files
concrete/examples/QuantizedLinearRegression.ipynb
2021-08-18 08:08:19 +01:00

759 lines
88 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "markdown",
"id": "0fe629d6",
"metadata": {},
"source": [
"# Quantized Linear Regression\n",
"\n",
"Currently, **hdk** only supports unsigned integers up to 7-bits. Nevertheless, we want to evaluate a linear regression model with it. Luckily, we can make use of **quantization** to overcome this limitation!"
]
},
{
"cell_type": "markdown",
"id": "d0cfb561",
"metadata": {},
"source": [
"### Let's start by importing some libraries to develop our linear regression model"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "3c1d929c",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"id": "69d25f7c",
"metadata": {},
"source": [
"### And some helpers for visualization"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a89c1a6c",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"from IPython.display import display"
]
},
{
"cell_type": "markdown",
"id": "7729c1de",
"metadata": {},
"source": [
"### We need a dataset, a handcrafted one for simplicity"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b77a9e82",
"metadata": {},
"outputs": [],
"source": [
"x = np.array([[130], [110], [100], [145], [160], [185], [200], [80], [50]], dtype=np.float32)\n",
"y = np.array([325, 295, 268, 400, 420, 500, 520, 220, 120], dtype=np.float32)"
]
},
{
"cell_type": "markdown",
"id": "cc8673ff",
"metadata": {},
"source": [
"### Let's visualize our dataset to get a grasp of it"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "35a98d1a",
"metadata": {},
"outputs": [],
"source": [
"plt.ioff()\n",
"fig, ax = plt.subplots(1)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "56703410",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ax.scatter(x[:, 0], y, marker=\"x\", color=\"red\")\n",
"display(fig)"
]
},
{
"cell_type": "markdown",
"id": "e31b82e8",
"metadata": {},
"source": [
"### Now, we need a model so let's define it\n",
"\n",
"The main purpose of this tutorial is not to train a linear regression model but to use it homomorphically. So we will not discuss about how the model is trained."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "cc5e72a2",
"metadata": {},
"outputs": [],
"source": [
"class Model:\n",
" w = None\n",
" b = None\n",
"\n",
" def fit(self, x, y):\n",
" a = np.ones((x.shape[0], x.shape[1] + 1), dtype=np.float32)\n",
" a[:, 1:] = x\n",
"\n",
" regularization_contribution = np.identity(x.shape[1] + 1, dtype=np.float32)\n",
" regularization_contribution[0][0] = 0\n",
"\n",
" parameters = np.linalg.pinv(a.T @ a + regularization_contribution) @ a.T @ y\n",
"\n",
" self.b = parameters[0]\n",
" self.w = parameters[1:].reshape(-1, 1)\n",
"\n",
" return self\n",
"\n",
" def evaluate(self, x):\n",
" return x @ self.w + self.b"
]
},
{
"cell_type": "markdown",
"id": "cefd8346",
"metadata": {},
"source": [
"### And create one"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b9879f4d",
"metadata": {},
"outputs": [],
"source": [
"model = Model().fit(x, y)"
]
},
{
"cell_type": "markdown",
"id": "01cfc83f",
"metadata": {},
"source": [
"### Time to make some predictions"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "78356d37",
"metadata": {},
"outputs": [],
"source": [
"inputs = np.linspace(40, 210, 100).reshape(-1, 1)\n",
"predictions = model.evaluate(inputs)"
]
},
{
"cell_type": "markdown",
"id": "58160140",
"metadata": {},
"source": [
"### Let's visualize our predictions to see how our model performs"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2a623999",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ax.plot(inputs, predictions, color=\"blue\")\n",
"display(fig)"
]
},
{
"cell_type": "markdown",
"id": "d3f39faa",
"metadata": {},
"source": [
"### As a bonus let's inspect the model parameters"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "7fa65211",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[2.669915]]\n",
"-3.2335143\n"
]
}
],
"source": [
"print(model.w)\n",
"print(model.b)"
]
},
{
"cell_type": "markdown",
"id": "544d6e34",
"metadata": {},
"source": [
"They are floating point numbers and we can't directly work with them!"
]
},
{
"cell_type": "markdown",
"id": "abf310f2",
"metadata": {},
"source": [
"### So, let's abstract quantization\n",
"\n",
"Here is a quick summary of quantization. We have a range of values and we want to represent them using small number of bits (n). To do this, we split the range into 2^n sections and map each section to a value. Here is a visualization of the process!"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "a7b3b993",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"<svg content=\"&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2021-08-13T09:47:25.144Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;5QhM0DGu1eUjmjeXuyFL&quot; version=&quot;14.9.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;6rZNNX4_K12e_kCXuZoG&quot; name=&quot;Page-1&quot;&gt;7Zzdb5s6FMD/mkjdw66MHZL0sUl376SrStM6rc8euAGNYAZOk/avnw2YD5t8QAmhJA+tyLFzbM7v2D7HdjtCi9X2vxAHzgO1iTeCwN6O0P0IQhPd8t9C8JoI0MRMBMvQtRORkQse3TeSCkEqXbs2iUoVGaUec4Oy0KK+TyxWkuEwpJtytWfqlVsN8JJogkcLe7r0ybWZk0hnJsjlX4m7dGTLBkhLVlhWTgWRg226KYjQlxFahJSy5Gm1XRBP2E7aJfnevztKs46FxGfHfOFpYrx9//Hw//Lpq8mc+yd4H5mfUy0v2FunL5x2lr1KCxDfvhOG5J8sD0eRa43QPGI4ZLrYYSuPCwz+mOghtmbevL9GZgXuPYSuCAtfeZVNbmdpZqdgYikLiYeZ+1JWj1Pcy0xd1sI36vKGIUg905ikelLHhGNQVhHRdWiR9FtFuyqKTHhAETfVkjBNEX8ovHYuirHVQAhrIfSpTz4WKAgU+5oNQWmKULeg0NBBjdsCpSrqGNR46KBmbYFSFXUMyhw4KKQuLU1BaYo6BjUZOig1mGgMSlXUMajpwEGN2womNEUdg5oNHVRbwYSmqGNQt0MH1VYwoSnqGJTccaidC38gWFoKBKfNYGmRn6poByxuPfxaqBaICtGeDquBC5ju75e6fpbr84ekB+16DmzoOR9/F0Xzg+ltM4dCqmeqik49+utl50NieND0RzNUl9quGdZL3AfF8JDpj2Z4aECfmmG9nH5QDNuaS7XcpGuGVen+xOPWmtvuC39ciseV699sP8kC3lChTEPOyJaVaUYspL/Jgno0zOPmZ9fzFBH23KUvXIJDJlw+fyEhcy3s3aUFK9e2RTPzjeMy8hhgS7S5CXHAZSFd+zYRLwuybgkFZFvXh3YFKNJ5Cj42rvAxCHa7U4lfbVh6yr/C25jMUBloA+3sDBpl8zaOnNgsRpmKkH/DjFvajyUQoIyVPMOEH2A+hLdlTAiMm82HqiI4UxSdej6s2gMozIcFzJM/a3EePH+mPvscxafhd7yCAYJtTEyW5xNlokfUf5eiBz7U8qk4UVduQp2he/YCjI7i7DZR9ytspVdHmqKXE6XoeHqfwmhr8ZqqI3J23olTKr6OrFOPLOM6sk45stQc/Pwjq97NoAsJSdRz1MYhiXYg23FIImle8e47fW2OV83yusbb6ArL4PG2lVCoijrHW+8+xZA2ybTjXDj+ZzJphlE7cazQdWqSVRcuGsRtWpx173LWPo9hCmFbUjInbEOIrxcsqB8Rax3D0Qp/ig5GO6O3XsZpo/fHZaq7GdPcRc4VmqGqtbtnOcMwkh5Y8x16OQpOka2Me5atoKrT25551DCGBLoOieoh0betMaSnADcbR6z7QPzEEgD7espzis1LJdpD8n5ZgZABO0Wkn3WDy+Fh9O0UDh11bF17OoVV4Xky/EQqLfrM1cR/cdh4eThl1/5cZ/zq1GByVGpgGJ36cAv5ZKVXbOWasdcX3ul5ENwIMV4JgP6vKEgKlc/cduL3W1BR9KnotmBnby/FR9U1L5tziw7a7SRbddXh/TNZ5R7IdebaEavKCwmZV5hnXnkPXJDIpqAL52Ya/eImG7ty289t2rPxNq463NW4vZFQZPxBsvetbggfHZWKdVqmm5fuCerf3xjy4KjgCbN2PIF/zP+3SHJckf+DFvTlLw==&lt;/diagram&gt;&lt;/mxfile&gt;\" height=\"195px\" version=\"1.1\" viewBox=\"-0.5 -0.5 420 195\" width=\"420px\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs/><g><path d=\"M 14.37 84 L 361.63 84\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 9.12 84 L 16.12 80.5 L 14.37 84 L 16.12 87.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 366.88 84 L 359.88 87.5 L 361.63 84 L 359.88 80.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 48 94 L 48 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 88 94 L 88 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 128 94 L 128 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 168 94 L 168 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 208 94 L 208 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 248 94 L 248 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 288 94 L 288 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 328 94 L 328 74\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 48 71 L 60.93 58.07 Q 68 51 78 51 L 98 51 Q 108 51 115.07 58.07 L 123.5 66.5\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 127.21 70.21 L 119.78 67.73 L 123.5 66.5 L 124.73 62.78 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 134.37 123 L 141.63 123\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 129.12 123 L 136.12 119.5 L 134.37 123 L 136.12 126.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 146.88 123 L 139.88 126.5 L 141.63 123 L 139.88 119.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 154.37 123 L 181.63 123\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 149.12 123 L 156.12 119.5 L 154.37 123 L 156.12 126.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 186.88 123 L 179.88 126.5 L 181.63 123 L 179.88 119.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 194.37 123 L 221.63 123\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 189.12 123 L 196.12 119.5 L 194.37 123 L 196.12 126.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 226.88 123 L 219.88 126.5 L 221.63 123 L 219.88 119.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 234.37 123 L 241.63 123\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 229.12 123 L 236.12 119.5 L 234.37 123 L 236.12 126.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 246.88 123 L 239.88 126.5 L 241.63 123 L 239.88 119.5 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"108\" y=\"94\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 104px; margin-left: 109px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div>min(x)</div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"12px\" text-anchor=\"middle\" x=\"128\" y=\"108\">min(x)</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"228\" y=\"94\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 104px; margin-left: 229px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \">max(x)</div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"12px\" text-anchor=\"middle\" x=\"248\" y=\"108\">max(x)</text></switch></g><path d=\"M 138 148 L 138 128\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-dasharray=\"2 6\" stroke-miterlimit=\"10\" stroke-width=\"2\"/><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"118\" y=\"152\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 162px; margin-left: 119px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">Map</font></div><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">to 0<br style=\"font-size: 10px\"/></font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"138\" y=\"165\">Map...</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"148\" y=\"152\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 162px; margin-left: 149px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">Map</font></div><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">to 1<br style=\"font-size: 10px\"/></font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"168\" y=\"165\">Map...</text></switch></g><path d=\"M 168 148 L 168 128\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-dasharray=\"2 6\" stroke-miterlimit=\"10\" stroke-width=\"2\"/><path d=\"M 208 148 L 208 128\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-dasharray=\"2 6\" stroke-miterlimit=\"10\" stroke-width=\"2\"/><path d=\"M 238 148 L 238 128\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-dasharray=\"2 6\" stroke-miterlimit=\"10\" stroke-width=\"2\"/><path d=\"M 294.37 68.66 L 321.63 68.66\" fill=\"none\" pointer-events=\"stroke\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 289.12 68.66 L 296.12 65.16 L 294.37 68.66 L 296.12 72.16 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><path d=\"M 326.88 68.66 L 319.88 72.16 L 321.63 68.66 L 319.88 65.16 Z\" fill=\"#000000\" pointer-events=\"all\" stroke=\"#000000\" stroke-miterlimit=\"10\"/><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"288\" y=\"18.66\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 29px; margin-left: 289px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><font style=\"font-size: 10px\">Distance<br/>Between<br/>Consecutive<br/>Values</font></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"12px\" text-anchor=\"middle\" x=\"308\" y=\"32\">Distan...</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"188\" y=\"152\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 162px; margin-left: 189px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">Map</font></div><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">to 2</font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"208\" y=\"165\">Map...</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"218\" y=\"152\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 162px; margin-left: 219px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">Map</font></div><div style=\"font-size: 10px\"><font style=\"font-size: 10px\">to 3</font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"238\" y=\"165\">Map...</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"120\" x=\"128\" y=\"174\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 184px; margin-left: 129px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \">(when n = 2)</div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"188\" y=\"187\">(when n = 2)</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"28\" y=\"94\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 104px; margin-left: 29px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \">0</div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"48\" y=\"107\">0</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"110\" x=\"308\" y=\"18.66\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 29px; margin-left: 309px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div><font style=\"font-size: 12px\">= 1 / scale</font></div><div><font style=\"font-size: 12px\">= 1 / q</font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"363\" y=\"32\">= 1 / scale...</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"140\" x=\"128\" y=\"24\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 34px; margin-left: 129px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><font style=\"font-size: 12px\">x =</font><font style=\"font-size: 12px\"> (x   + zp  ) / q </font></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"198\" y=\"37\">x = (x   + zp  ) / q </text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"167\" y=\"29\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 39px; margin-left: 168px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div><font style=\"font-size: 10px\">q</font></div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"187\" y=\"42\">q</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"199\" y=\"29\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 39px; margin-left: 200px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div>x</div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"219\" y=\"42\">x</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"40\" x=\"227\" y=\"29\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 39px; margin-left: 228px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div>x</div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"247\" y=\"42\">x</text></switch></g><rect fill=\"none\" height=\"20\" pointer-events=\"all\" stroke=\"none\" width=\"80\" x=\"48\" y=\"28\"/><g transform=\"translate(-0.5 -0.5)\"><switch><foreignObject height=\"100%\" pointer-events=\"none\" requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\" style=\"overflow: visible; text-align: left;\" width=\"100%\"><div style=\"display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 38px; margin-left: 49px;\" xmlns=\"http://www.w3.org/1999/xhtml\"><div style=\"box-sizing: border-box; font-size: 0; text-align: center; \"><div style=\"display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; \"><div>zero point<br/></div><div>zp = 2</div></div></div></div></foreignObject><text fill=\"#000000\" font-family=\"Helvetica\" font-size=\"10px\" text-anchor=\"middle\" x=\"88\" y=\"41\">zero point...</text></switch></g></g><switch><g requiredFeatures=\"http://www.w3.org/TR/SVG11/feature#Extensibility\"/><a target=\"_blank\" transform=\"translate(0,-5)\" xlink:href=\"https://www.diagrams.net/doc/faq/svg-export-text-problems\"><text font-size=\"10px\" text-anchor=\"middle\" x=\"50%\" y=\"100%\">Viewer does not support full SVG 1.1</text></a></switch></svg>"
],
"text/plain": [
"<IPython.core.display.SVG object>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import SVG\n",
"SVG(filename=\"figures/QuantizationVisualized.svg\")"
]
},
{
"cell_type": "markdown",
"id": "9cbd7e1d",
"metadata": {},
"source": [
"If you want to learn more, head to https://intellabs.github.io/distiller/algo_quantization.html"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "a8bab855",
"metadata": {},
"outputs": [],
"source": [
"class QuantizationParameters:\n",
" def __init__(self, q, zp, n):\n",
" # q = scale factor = 1 / distance between consecutive values\n",
" # zp = zero point which is used to determine the beginning of the quantized range\n",
" # (quantized 0 = the beginning of the quantized range = zp * distance between consecutive values)\n",
" # n = number of bits\n",
" \n",
" # e.g.,\n",
" \n",
" # n = 2\n",
" # zp = 2\n",
" # q = 0.66\n",
" # distance between consecutive values = 1 / q = 1.5151\n",
" \n",
" # quantized 0 = zp / q = zp * distance between consecutive values = 3.0303\n",
" # quantized 1 = quantized 0 + distance between consecutive values = 4.5454\n",
" # quantized 2 = quantized 1 + distance between consecutive values = 6.0606\n",
" # quantized 3 = quantized 2 + distance between consecutive values = 7.5757\n",
" \n",
" self.q = q\n",
" self.zp = zp\n",
" self.n = n\n",
"\n",
"class QuantizedArray:\n",
" def __init__(self, values, parameters):\n",
" # values = quantized values\n",
" # parameters = parameters used during quantization\n",
" \n",
" # e.g.,\n",
" \n",
" # values = [1, 0, 2, 1]\n",
" # parameters = QuantizationParameters(q=0.66, zp=2, n=2)\n",
" \n",
" # original array = [4.5454, 3.0303, 6.0606, 4.5454]\n",
" \n",
" self.values = np.array(values)\n",
" self.parameters = parameters\n",
"\n",
" @staticmethod\n",
" def of(x, n):\n",
" if not isinstance(x, np.ndarray):\n",
" x = np.array(x)\n",
"\n",
" min_x = x.min()\n",
" max_x = x.max()\n",
"\n",
" if min_x == max_x: # encoding single valued arrays\n",
" \n",
" if min_x == 0.0: # encoding 0s\n",
" \n",
" # dequantization = (x_q + zp_x) / q_x = 0 --> q_x = 1 && zp_x = 0 && x_q = 0\n",
" q_x = 1\n",
" zp_x = 0\n",
" x_q = np.zeros(x.shape, dtype=np.uint)\n",
" \n",
" elif min_x < 0.0: # encoding negative scalars\n",
" \n",
" # dequantization = (x_q + zp_x) / q_x = -x --> q_x = 1 / x & zp_x = -1 & x_q = 0\n",
" q_x = abs(1 / min_x)\n",
" zp_x = -1\n",
" x_q = np.zeros(x.shape, dtype=np.uint)\n",
" \n",
" else: # encoding positive scalars\n",
" \n",
" # dequantization = (x_q + zp_x) / q_x = x --> q_x = 1 / x & zp_x = 0 & x_q = 1\n",
" q_x = 1 / min_x\n",
" zp_x = 0\n",
" x_q = np.ones(x.shape, dtype=np.uint)\n",
" \n",
" else: # encoding multi valued arrays\n",
" \n",
" # distance between consecutive values = range of x / number of different quantized values = (max_x - min_x) / (2^n - 1)\n",
" # q = 1 / distance between consecutive values\n",
" q_x = (2**n - 1) / (max_x - min_x)\n",
" \n",
" # zp = what should be added to 0 to get min_x -> min_x = (0 + zp) / q -> zp = min_x * q\n",
" zp_x = int(round(min_x * q_x))\n",
" \n",
" # x = (x_q + zp) / q -> x_q = (x * q) - zp\n",
" x_q = ((q_x * x) - zp_x).round().astype(np.uint)\n",
"\n",
" return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n))\n",
"\n",
" def dequantize(self):\n",
" # x = (x_q + zp) / q\n",
" # x = (x_q + zp) / q\n",
" return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q\n",
"\n",
" def affine(self, w, b, min_y, max_y, n_y):\n",
" # the formulas used in this method was derived from the following equations\n",
" #\n",
" # x = (x_q + zp_x) / q_x\n",
" # w = (w_q + zp_w) / q_w\n",
" # b = (b_q + zp_b) / q_b\n",
" #\n",
" # (x * w) + b = ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b)\n",
" # = y = (y_q + zp_y) / q_y\n",
" #\n",
" # So, ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b) = (y_q + zp_y) / q_y\n",
" # We can calculate zp_y and q_y from min_y, max_y, n_y. So, the only unknown is y_q and it can be solved.\n",
"\n",
" x_q = self.values\n",
" w_q = w.values\n",
" b_q = b.values\n",
"\n",
" q_x = self.parameters.q\n",
" q_w = w.parameters.q\n",
" q_b = b.parameters.q\n",
"\n",
" zp_x = self.parameters.zp\n",
" zp_w = w.parameters.zp\n",
" zp_b = b.parameters.zp\n",
"\n",
" q_y = (2**n_y - 1) / (max_y - min_y)\n",
" zp_y = int(round(min_y * q_y))\n",
"\n",
" y_q = (q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b))\n",
" y_q -= min_y * q_y\n",
" y_q = y_q.round().clip(0, 2**n_y - 1).astype(np.uint)\n",
"\n",
" return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y))\n",
"\n",
"class QuantizedFunction:\n",
" def __init__(self, table):\n",
" self.table = table\n",
"\n",
" @staticmethod\n",
" def of(f, input_bits, output_bits):\n",
" domain = np.array(range(2**input_bits), dtype=np.uint)\n",
" table = f(domain).round().clip(0, 2**output_bits - 1).astype(np.uint)\n",
" return QuantizedFunction(table)"
]
},
{
"cell_type": "markdown",
"id": "e5be0800",
"metadata": {},
"source": [
"### Let's quantize our model parameters\n",
"\n",
"Since the parameters only consist of scalars, we can use a single bit quantization."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "3ec0ad9b",
"metadata": {},
"outputs": [],
"source": [
"parameter_bits = 1\n",
"\n",
"w_q = QuantizedArray.of(model.w, parameter_bits)\n",
"b_q = QuantizedArray.of(model.b, parameter_bits)"
]
},
{
"cell_type": "markdown",
"id": "b43c0371",
"metadata": {},
"source": [
"### And quantize our inputs"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "20cea447",
"metadata": {},
"outputs": [],
"source": [
"input_bits = 6\n",
"\n",
"x_q = QuantizedArray.of(inputs, input_bits)"
]
},
{
"cell_type": "markdown",
"id": "ca76b68d",
"metadata": {},
"source": [
"### Time to make quantized inference"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "8728e939",
"metadata": {},
"outputs": [],
"source": [
"output_bits = 7\n",
"\n",
"min_y = predictions.min()\n",
"max_y = predictions.max()\n",
"y_q = x_q.affine(w_q, b_q, min_y, max_y, output_bits)\n",
"\n",
"quantized_predictions = y_q.dequantize()"
]
},
{
"cell_type": "markdown",
"id": "ab782b4a",
"metadata": {},
"source": [
"### And visualize the results"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "9d2bb5da",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ax.plot(inputs, quantized_predictions, color=\"black\")\n",
"display(fig)"
]
},
{
"cell_type": "markdown",
"id": "4834cdfc",
"metadata": {},
"source": [
"### Now it's time to make the inference homomorphic"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "fcf4ea26",
"metadata": {},
"outputs": [],
"source": [
"q_y = (2**output_bits - 1) / (max_y - min_y)\n",
"zp_y = int(round(min_y * q_y))\n",
"\n",
"q_x = x_q.parameters.q\n",
"q_w = w_q.parameters.q\n",
"q_b = b_q.parameters.q\n",
"\n",
"zp_x = x_q.parameters.zp\n",
"zp_w = w_q.parameters.zp\n",
"zp_b = b_q.parameters.zp\n",
"\n",
"x_q = x_q.values\n",
"w_q = w_q.values\n",
"b_q = b_q.values"
]
},
{
"cell_type": "markdown",
"id": "43e47369",
"metadata": {},
"source": [
"### Simplification to rescue!\n",
"\n",
"The `y_q` formula in `QuantizedArray.affine(...)` can be rewritten to make it easier to implement in homomorphically. Here is the breakdown.\n",
"```\n",
"(q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b)) - (min_y * q_y)\n",
"^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^\n",
"constant (c1) can be done constant (c2) constant (c3) constant (c4)\n",
" on the circuit \n",
" \n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" can be done on the circuit\n",
" \n",
"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
"cannot be done on the circuit because of floating point operation so will be a single table lookup\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "2de0cf20",
"metadata": {},
"outputs": [],
"source": [
"c1 = q_y / (q_x * q_w)\n",
"c2 = w_q + zp_w\n",
"c3 = (q_x * q_w / q_b) * (b_q + zp_b)\n",
"c4 = min_y * q_y\n",
"\n",
"f = lambda intermediate: (c1 * (intermediate + c3)) - c4\n",
"f_q = QuantizedFunction.of(f, input_bits + parameter_bits, output_bits)\n",
"\n",
"from hdk.common.extensions.table import LookupTable\n",
"table = LookupTable([int(entry) for entry in f_q.table])\n",
"\n",
"w_0 = int(c2.flatten()[0])\n",
"\n",
"def infer(x_0):\n",
" return table[(x_0 + zp_x) * w_0]"
]
},
{
"cell_type": "markdown",
"id": "93eb9499",
"metadata": {},
"source": [
"### Time to compile our quantized inference function"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "a80895fd",
"metadata": {},
"outputs": [],
"source": [
"from hdk.common.data_types.integers import Integer\n",
"from hdk.common.data_types.values import EncryptedValue\n",
"from hdk.hnumpy.compile import compile_numpy_function_into_op_graph\n",
"\n",
"dataset = []\n",
"for x_i in x_q:\n",
" dataset.append((int(x_i[0]),))\n",
"\n",
"homomorphic_model = compile_numpy_function_into_op_graph(\n",
" infer,\n",
" {\"x_0\": EncryptedValue(Integer(input_bits, is_signed=False))},\n",
" iter(dataset),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f0b08a0f",
"metadata": {},
"source": [
"### Here is the textual representation of the operation graph"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "2cc4e11d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"%0 = ConstantInput(1) # Integer<unsigned, 1 bits>\n",
"%1 = x_0 # Integer<unsigned, 6 bits>\n",
"%2 = ConstantInput(15) # Integer<unsigned, 4 bits>\n",
"%3 = Add(1, 2) # Integer<unsigned, 7 bits>\n",
"%4 = Mul(3, 0) # Integer<unsigned, 7 bits>\n",
"%5 = ArbitraryFunction(4) # Integer<unsigned, 7 bits>\n",
"return(%5)\n"
]
}
],
"source": [
"from hdk.common.debugging import get_printable_graph\n",
"print(get_printable_graph(homomorphic_model, show_data_types=True))"
]
},
{
"cell_type": "markdown",
"id": "ade14f17",
"metadata": {},
"source": [
"### Finally, it's time to make homomorphic inference\n",
"\n",
"Or, at least, simulate it until the compiler integration is complete."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "dd2d03d7",
"metadata": {},
"outputs": [],
"source": [
"homomorphic_predictions = []\n",
"for x_i in map(lambda x_i: int(x_i[0]), x_q):\n",
" evaluation = homomorphic_model.evaluate({0: x_i})\n",
" inference = QuantizedArray(evaluation[homomorphic_model.output_nodes[0]], y_q.parameters)\n",
" homomorphic_predictions.append(inference.dequantize())\n",
"homomorphic_predictions = np.array(homomorphic_predictions, dtype=np.float32)"
]
},
{
"cell_type": "markdown",
"id": "443fbc03",
"metadata": {},
"source": [
"### And visualize it"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "57050b5d",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD4CAYAAAAXUaZHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAmtklEQVR4nO3deXxU5b3H8c+PXcAkQBBZZcvC4gZUtHVBbSuoBbm4XmrRorigxrggiEBQoaJVjFcQqSsuUETBnaIoLq1wCV4rRURA1gQISQghGyHJc/+YgyYxQICEM5n5vl+vec2Z55yZ/DKv4cuT5zzzHHPOISIioaWO3wWIiEj1U7iLiIQghbuISAhSuIuIhCCFu4hICKrndwEA0dHRrmPHjn6XISJSq6xYsSLDOdeysn1BEe4dO3YkJSXF7zJERGoVM9t0oH0alhERCUEKdxGREKRwFxEJQQp3EZEQpHAXEQlBCncRkRCkcBcRCUEKdxERH2TnFNLmuj689/myGnl9hbuIyDG25PMiWo6IZ1unFSS9/lSN/Iyg+IaqiEg4yM2FUaOLeCatG5y6iV67fk/KjNdq5Gcp3EVEalBuQS73v3I/363L5qulkN9qCZy6hfNKLmTJk/+osZ+rcBcRqSH5hfl0HRPLjmbboAlwYaD9fM7nkwc/rtGfrXAXEakBhUWFtE+MJevEbfCPgVx/xjiGD4foqOOJax9X4z9f4S4iUs02bi6k+wOxFHRJJfKrQXw6bQGnn35sa1C4i4hUE+fg+ReKuGlhPKU9txC/+RJWvreAej4krcJdRKQabNwIN44o4uOmgZkwZxdexBfPv+dbPQp3EZEjVFpaysLl/2D+O0W88goU9bkHTv2RC+23fPyXhb7WpnAXETkC+YX5dBoVS3qLVGgADA+0n8/5fDz+I19rA4W7iMhh25NXSLuEOHLap1L3n2fzu5N/RXw36NjyJBIGJfhdHqBwFxE5LP9aWki/p2LZF7eVNt8O4utXFtCqld9V/ZLCXUSkCgoKYNyEIh5fHw+nbKF35iWkvLnA77IOSAuHiYgcwhdfwCmnFfH4ung4ZRMXuv6kPOXNhHHO3+IOQOEuInIAOTkwciSce14RG0/pDqdu4Hc/tOfjCR8EDnAOEhMhKcnXOiujcBcRqSC/MJ/Wt3Yh8hFjeqTBmIYU91zPBWvbsej1LYFA3x/sycmQnR10PXiNuYuIlLElNZ+4sbEUdEql/qoudG4dSZMm0Du6NzPHPwvRXqAnJweekJAAU6eCmb+FV2AuCP636dOnj0tJSfG7DBEJY87Ba7MLGfZ2LKXdtxC3YRD/fnYBDRtWcmCdMoMepaW+BbuZrXDO9alsn4ZlRCTspaXBZYOLuPateEq7b+Hcwkv4/qUDBHtiYvm2/UM0QUbhLiJhyzl4/nmI717EO3Xj4eRNXFS3P5/9pZI1YcqOsSckBHrsCQmBx0EY8BpzF5Gw9OOPMGIELP6kmEZXdYf4Dfyuzu9Y+MCHlT/BDKKiyo+xT50a2BcVpTH3ymjMXUSOinPlw7Xi4zL25OXTPeFctu7dCECDyAKKWuZzgV3A4vGLq/Vn1bSjHnM3s41mttLMvjGzFK+tuZl9ZGZrvftmXruZ2VNmts7MvjWzXtX3q4iIVJCUVH5Y5CBzz5d/nU+Lm2LZ2n4FdZsW0Lh5IfXrGwMbDqxasMMvgzzIeuz7Hc6wzPnOuYwyj0cDi51zj5jZaO/xfcAAIMa79QWe8e5FRKqXc4E55vunJU6dWn5c3OtVFxXBQ5MKefi7eOiZSq+dg0iZviBYc7laHM2Y+yCgn7f9MrCEQLgPAma5wHjPUjOLMrPWzrltR1OoiMgvlB33rjD3POfhiQyceD4bM7ezbTsUNd0OPXfzO3cJi55e4FvJx0pVZ8s4YJGZrTCzEV5bqzKBvR3Yvy5aW2BLmedu9drKMbMRZpZiZik7d+48gtJFRCgf8J7cyQ8R80Acn9lnbGq4lqIOa6nTIpdBDQexKMm/qyMdS1XtuZ/tnEs1sxOAj8zs+7I7nXPOzA7rzKxzbiYwEwInVA/nuSIiP6kw9zy3Lpx0azuyOuXAe1czotdsHn0UIiN9rNEHVeq5O+dSvft0YD5wBrDDzFoDePfp3uGpQPsyT2/ntYmIVK8Kc8+3bcul1dUtyOqUQ5NFA/lkyus8+2z4BTtUIdzNrImZHb9/G/g98B/gHWCYd9gw4G1v+x3gT96smTOB3RpvF5EaUWbu+bxzJ9EuMY78mExivjqb9H5ncP4FIXzG9BCqMizTCphvgdPK9YDXnXMLzWw5MNfMhgObgCu94z8ALgbWAfnA9dVetYiIZ+fIJEbeXsAbs+OgZyrnFVzGkg/fCtopisfKIcPdOfcjcGol7ZnAhZW0O2BktVQnIlKJnLwcxr02jm++y2HZMtjbYRH0TOPiepfy/iPz/S4vKGj5ARGpVXLycuhyfwwZzdOhGdA/0N6/Xn/eH/uur7UFE4W7iNQaOXm5tL87jpzW6dT5xxBu++3dXHEFtIiMoluHbn6XF1QU7iJSK3y7Kp9fTYmlqMt2Tlh+Bf96ZS5duvhdVfBSuItIUCsuhkf/ms/Y/4uF7tvotWMIKe/ODffzpYek9dxFJGitXAl9zypk7Ip46J7K790gVkyfp2CvAvXcRSSolJaWsvB/F/Pq6/v4+9+B826FHlu4pP4lvHf/Ar/LqzUU7iISNHLycuh0XyxZLXdAC+DWQHv/ev157/7wWBOmuijcRSQo7MjIpfPoOPLb76DBV+dxUe9T6HgSxJwYw+0Db/e7vFpH4S4ivnv/w3wGvRpLSex2uqy5gq/nziUiwu+qajeFu4j4Jjsb7rwrn5dzY6HHNs7LG8KS1+f6XVZI0GwZEfHFggUQ372Ql/fEQ49U/lD/MpY8Os/vskKGwl1EjqkdO+DKK2HwkEKy+sVBzy1c2uBS3rlfa8JUJw3LiMgxsTs3hy53n0Jms83QGbjPsa8hDKg3gHfHaE2Y6qZwF5Ea992aXE7/SxxFnbbTcHUXOrZuSqNG8OtWv2b6zdP9Li8kKdxFpMaUlsL/TMvnzn/FQvx2Tk+7iuWvzaFuXb8rC30KdxGpVlk5WfzqwV+xtTiNffvA1S+G+GL6uyF8+Owcv8sLGwp3Eak22bnZxI6PJTMqE9aegJXWJTISLm9xCc/d9je/ywsrCncRqRY5eTl0HhPLruhMePs6Bnd8kWnToHVrvysLTwp3ETlq6Zk5dBodQ367nTRcdC2vjX2RIUP8riq8KdxF5Kh8/Gku/V+Io6RrOp1WXUPKu7No3tzvqkThLiJHJDcX7rkvn2czY6Hbds7ZcwWfz33d77LEo3AXkSrLysli8BOD2bBzJ9vSoDgqDbrtZlCDISz4q9aECSYKdxGpkuzcbLqOi2VXs0xoXAe6BtYvGdLkCubeo2APNgp3ETmk7NxsOoyKYU+rwEyYMf1fZPx4aNTI78rkQBTuInJQP/yYwykPx7H3pAyaffEnPnnhRU47ze+q5FAU7iJSnnNghnPw7HO53LokFhebzqlbrmH5hy9Tv77fBUpVKNxF5GdJSZCdzcaEqQy/qYBPmsdCtx0MWHM6H7yumTC1icJdRADI3rOLCWvfYfmqvfzv58Mpif8Q4rYz5AOYd9a5P/XopXZQuIsI2bnZdLo/huzYTIgF+A4cXLYQ5p2VAFOnKthrGYW7SJjbuSubjqNjyG+dSf1F13DvZTfx+8n9iN4HPfKAfynYayNdZk8kjH32zxxaJ8SR3yaDk74dxta3XmNS6nzOy/aCHSAxMTAkI7WKwl0kDBUUwF335tJvWiwlXdI5L2coG998kRP+kgjJyZCQELjSRkJC4LECvtbRsIxImPniC7h+eD7rewVmwgxucBVvPf5qYGdUVCDQ94+xT536c7uGZmoVc0Hwv3GfPn1cSkqK32WIhKzikmI+XPo5M2eW8N77pdT9/XBK4lK5vPHlvHHvG+UPrjgrRrNkgpaZrXDO9alsn3ruIrXBUQRudm42J93XlZwTMqEzcDuUAIOPG/zLYIdfvq6CvVaqcribWV0gBUh1zl1qZp2AOUALYAVwrXOuyMwaArOA3kAmcJVzbmO1Vy4SLrwvFv00VOJcYAw8Kiqw7yDWbcym50Mx7G2fSeNl59O/bzytWkH3tt257Q+3HYPixS+H03NPAFYDEd7jKcBU59wcM5sBDAee8e53Oee6mtnV3nFXVWPNIuHDuUCwJycHHk+dGgj2/Sc9K/TgS0tLyc7NxjmY+2Y+Iz/pjYvJ4NSNw1g2/yUaNvTn15Bjr0rhbmbtgEuAScBdZmbABcB/e4e8DCQRCPdB3jbAPOBpMzMXDIP7IrVN2ZOayck/h3zCL79YlL4rne4Tu5PZLPPn58fAJaVDee/Fl45dzRIUqjoV8klgFFDqPW4BZDvnir3HW4G23nZbYAuAt3+3d3w5ZjbCzFLMLGXnzp1HVr1IOCgb8PtVCPaM3RnETYwjMzKTOl+dQZ3F/Ynb0p8HYx/mvYmvHuOCJRgcsuduZpcC6c65FWbWr7p+sHNuJjATArNlqut1RULO/jH2shITfwr4rJwsuo6LY3fzbJh/M2dHPcNzz0NMjC/VSpCoSs/9N8BAM9tI4ATqBUAyEGVm+/9zaAeketupQHsAb38kgROrInK49gf7Ab5YlJm9i/ajYtndPIv679/IjFuf4dNPFexShZ67c24MMAbA67nf45wbamZvAJcTCPxhwNveU97xHn/l7f9E4+0iR8jsgF8sWlbQjrPvjKO4Yybtvr6er+bPpF07f8uV4HE089zvA+aY2cPA/wHPe+3PA6+Y2TogC7j66EoUCXNJSeVmxRTtMyZGTGTyzliI3ck5u4bx2dsvaDq6lHNY4e6cWwIs8bZ/BM6o5JhC4IpqqE1EPOnZO+n7cF+2Fe+gaB+4BvsgtpjLGw7ljSdf8rs8CUL6hqpIkMvYnUHsxDh2R2bDuhOoY3WIjIBrWg1m+s3T/S5PgpTCXSSIZeVk0XlsHHuis+GtW7jxzOk89hhERvpdmQQ7hbtIkNqUmk1cUix722Zx/Cc38nbydM4/3++qpLZQuIsEoTnzsvnvd2JwnTPpuf56li2cSePGflcltYnCXSSI7NwJt9yew5t1YyE2g0uKh/HeKy/4XZbUQgp3EZ+l70rn8ievYMOOLLalQUn0Zjgph2uaDuX1u1/yuzyppRTuIj7K2J1BTFIcOc2yIcIgAupgDI28lll3zvK7PKnFFO4iPsnIzuKkMbHkn5BN3bdv4bE/TeeOO6BuXb8rk1CgcBfxwdffZtP3yRiKO+yizbKb+HzedLp08bsqCSUKd5FjqLgYJj+azYTVXaFLFr/JHM4XH8zQ0gFS7aq6nruIHKWVK+GMX+cwYVUsdM3kyuOu48v/eU7BLjVCPXeRGpSVk8XE2Q/zz2UFfP01cPKb0HUnQyOG8mrii36XJyFM4S5SQzJ2Z9D5gZjA0gEnEbg5uLrp1byaqKsjSc1SuIvUgC3bs4gZH8ve1tk0/ngYY4deS9++0CqqFT079fS7PAkDCneRapCTl8OsT2ZRUlrC9987Zq5+iNKOu+j+w0189f4MIiL8rlDCjcJd5CilZaYR/1A8e5rt+bmxI1xcPJz3X5/hW10S3hTuIkdhe9Z2uj3UjT2Re2j08TXsTetDv35w+3XxDD7nYr/LkzCmcBc5Qum70omdGM+eqByYl0hc/Sd4fi707u13ZSKa5y5yRHZmZ9BpbBx7onZjC25n0tAnWL5cwS7BQz13kcP071WZ/GpqLPvaZtPqX7fw6Zyn6NbN76pEylO4ixxCaWkp+XvzKS2Fp2fkMPbfJ0PnXfw6/SY+XzhdC31JUFK4ixxEWmYaPR/qya5mu35u7AxXNhzO36drJowEL4W7yAHsnwmTE5mD/bMvdYsjiIuDoedfwJgrR4NzlFsYpuJjER8p3EUqkb4rnZgJ8eQ2D8yEGRz/BNOmwYknegckJUF2NkydGgh05yAxEaKiAvtEfKbZMiIVbN2RQYcxceQ2381xH97BvAef4M03ywS7c4FgT04OBPr+YE9ODrQ752P1IgHquYuU8cFHGfxhdiyl7bOJ/e4WvlqYTPPmFQ4yC/TYIRDoycmB7YSEn3vyIj4zFwS9jD59+riUlBS/y5AwlpsLd43K4m+5MdA5iwF7b+KDvxzihKlzUKfMH7+lpQp2OabMbIVzrk9l+9Rzl7CVlpnGWZPPYse+DIqKwDUqgs7F/ClyOC8nViHYExPLtyUmqucuQUNj7hKWtmdtJ+7BeDY33czerCbUyW9Ks+Lm3NH2Dl5OfO7gTy47xp6QEOixJySUH4MX8Zl67hJ20nel02VcPPnRe7A3E7n/sid44AFo1KiKL2AWmBVTdox9/xh8VJR67hIUNOYuYWXVDxmc/lgM+9pk0/LzO1g0NZnTTjvCF9M8d/GZxtwl7DkHTz+bQcLSWNxJ2fRNu4UvFiVTv/5RvGjFIFewSxDRmLuEvI0b4YKLsrjjX3G4jru4quFNLP3b9KMLdpEgp567hKS0zDSufuoa1qdms20buA4boN0ehre4gedu15owEvoOGe5m1gj4HGjoHT/POTfBzDoBc4AWwArgWudckZk1BGYBvYFM4Crn3MYaql/kF7ZnbSd2YjfymudAtEE01Ck1ro8eznO3/c3v8kSOiaoMy+wFLnDOnQqcBvQ3szOBKcBU51xXYBcw3Dt+OLDLa5/qHSdyTKTuTKfj2HjymuXQ6N1EZvUopfQvpZQ8WsJztx1iiqNICDlkz90FptPkeg/rezcHXAD8t9f+MpAEPAMM8rYB5gFPm5m5YJiWI7VfhRkpm7Zv5O5X7iG/KJ+cHPgq65+Utsmh67d38OUHT9CqlY+1ivioSmPuZlaXwNBLV2AasB7Ids4Ve4dsBdp6222BLQDOuWIz201g6CajwmuOAEYAdOjQ4eh+CwkPFVZi3LR9I90fiiH/BO9j2BioDwMKb+WD+ck+FirivyrNlnHOlTjnTgPaAWcA8Uf7g51zM51zfZxzfVq2bHm0LyehrsJKjJt3bKLHgzHktyim2fw7YNJOhm7cyZaRe/hgyjS/qxXx3WHNlnHOZZvZp8BZQJSZ1fN67+2AVO+wVKA9sNXM6gGRBE6sihy5Mt8CTXsmmR65yeS1AeaOIapoEm98aFx4ob8ligSTQ/bczaylmUV528cBvwNWA58Cl3uHDQPe9rbf8R7j7f9E4+1SLcxIe+Beuv6xLrltgXn3cGf/SaxcqWAXqagqwzKtgU/N7FtgOfCRc+494D7gLjNbR2BM/Xnv+OeBFl77XcDo6i9bwtGqH7bRcXQcBe1KaD5vGF+t/oKpJNKksfoOIhVVZbbMt8DplbT/SGD8vWJ7IXBFtVQnYS07N5vZn82mtNSxYkUJL216ANchjzO+HMTnK16k4ejEny+UoaV2RcrRN1QlKG3asYkej/QgLyov0GBAB7j6P2cx++P5WolR5BAU7hJ0tu7cGgj24/Oov3AopZmncNFFcNM1PRmYNODnIN8f8Ap2kV9QuEtQSctMI/7hbuRF5sHcMfz6xMk89wF07XqAJyjYRSqlVSElaGxJT6PzuHjyonKp//a9PHvXZD755CDBLiIHpJ67HBuHuLDFZ19t58IXulHSZg+dVtzF5+8/Srt2PtQpEiIU7lLzKiwbgHOU3plAcWQERfeNJ2lyBo+ndYcOOVyUdwcfvvu4RltEjpLCXWpW2WUDAKZOZdPI4ZxS+CI5zYG/ToIGQAcY3uw2npuoNWFEqoPCXWpW2SmLyclsnpFMj2shrx3wZV8aWBPi4uDafv25d8i9vpYqEkp0gWw5Npxj63F1iP1jHQralMLcMYw4bzKPPgqRkX4XJ1I76QLZ4i/n+P7G2zhlaCP2tSkk8o0bWdCzDf1mOE1lFKkhmgopNcs5Zv3hcbqXvsK+doX03ngXaRdE0u/N2yExMTAmLyLVTj13qTE7d8JNt+1kfsuHof0erq6fwOxZjwcCvf4+LRsgUoMU7lKtNu3YxDlTziF9XxZ79wKt90JkMbe0Gsn0W58MHKRlA0RqnMJdqs3m9M10n9yD/Mg82NCCenWN4xs25oaO1/Ho9Y+WP1jBLlKjFO5SLTbv2Ersgz3Y2yKPem+NYcqfJ5OQAHXr+l2ZSHhSuMsRWbt1LQOfGsie4j0UF0O6y8BF76X90nv59O3JdOnid4Ui4U3hLodtfdp6Tn38VAqaFlB3b0NKSoCSOlyUM4oPF07RiItIEFC4y2HZsG0DJz92MgVNC2j7xUOkfvYAAwfC9OnQtq3f1YnIfgp3qbJNOzbR89GeFDQtwOYmUbTrAebMgSuv1PlRkWCjLzFJlWxO30z8pB7kH58Pc8cx9IwJfPcdXHWVgl0kGKnnLof0w6at9JzSg33ReRz/4f3MmfogF1/sd1UicjAKd/mFDds2MOrVURQWF5KRAcv2LMGdmMtp60bx2eJJRET4XaGIHIrCXcpZn7Y+cMI0qiDQEAEcB1fVu4c5r0/xtTYRqTqFu/yk7EyYpm9PIG/1CG65GcaPbUqraHXXRWoThbsAgZkwPab0pOD4Apgzkc6Nx/PCl9C7t9+ViciR0GyZUFVxKd2DLK27acdm4h7qQUFEPnXmjePh68aTkqJgF6nN1HMPRZVckJrExMASu0lJ5Q5d9s1WfjOjByUn5NHmn2P4+K0H6dbNh5pFpFop3ENNJRekJjEx8Dghgew9u5j3zzcpKSllyWeOOVn3QptcLtw1in8smqyFvkRChMI91FS4IPVPIZ+QwPp7b+Pk8W1/ngnTBDgOboy6h5kPaiaMSCjRBbJDlXNQ5+dTKhtS19PjscDSAXUWDqN+QQyXXAx/HnI6l/TVN5JEaiNdIDvc7B9j92xqBN0fjqewxT6YM5FBJ49n2jRo3drHGkWkRmm2TKjZH+zeGPvajRuJ+VMjCqP30WT+KOb9ZRxvvaVgFwl1CvdQYxaYFZOQwPzf3U3cpJ7sa1VIz8XD2HxJc4ZcrlW+RMKBhmVCUO49SdxxTyovvtUN2uZyBaOY++UjWr5RJIwo3EPE2q1r6fV4L3KjcgMNrYFSuKP1PSTfrJkwIuHmkOFuZu2BWUArwAEznXPJZtYc+DvQEdgIXOmc22VmBiQDFwP5wHXOua9rpnyBwJowpzx+KoVNC+DLvhxXvxFxcTDsvMu487I7/S5PRHxQlZ57MXC3c+5rMzseWGFmHwHXAYudc4+Y2WhgNHAfMACI8W59gWe8e6kBm3ZsIn5yT4qiCrC5Exk9ZDzjx0OjRn5XJiJ+OmS4O+e2Adu87T1mthpoCwwC+nmHvQwsIRDug4BZLjCBfqmZRZlZa+915Cit2riKs588mz319+CA0rql0MzRask4Fv59PKed5neFIhIMDmvM3cw6AqcDy4BWZQJ7O4FhGwgE/5YyT9vqtZULdzMbAYwA6NChw+HWHZZWb15N76d6s7fpXk5I70rGToNSY0C7G3jnk3uppzMoIuKpchyYWVPgTeBO51yOlZl54ZxzZnZYX3V1zs0EZkLgG6qH89xwtGbLGno92Yu9jfcSs/yvrF10N+ecA889B7GxflcnIsGmSvPczaw+gWB/zTn3lte8w8xae/tbA+leeyrQvszT23ltcoTWbl3LaU+cRmHjQurPm8K2f93NtGmwZImCXUQqd8hw92a/PA+sds49UWbXO8Awb3sY8HaZ9j9ZwJnAbo23H7kN2zZw8l9PpbBpIcx5mAs7jWLVKrj11nJLx4iIlFOVYZnfANcCK83sG6/tfuARYK6ZDQc2AVd6+z4gMA1yHYGpkNdXZ8HhZN3WTXR/pCf7mhXQaMFDzJwwlj/+Ud9FEpFDq8psmS+BA8XJhZUc74CRR1lXWFq1cRWXT7+cvOI8ivZBOjtwzYvo/u0EPvn4AVq1OvRriIiAvqEaNH6aCdNkL3X2NqDUASV1uKJ0HHMXJPldnojUMgr3IFB2Jkz0P/5KxvK7GT4cHnsMmjXzuzoRqY0U7j7bnL45MBOmSSHMnkLT0ruZ/RH89rd+VyYitZnmW/jsrKTfUhgRmAlz58BR/Oc/CnYROXrqufskIwN+e/Mk0k5eS6OUM/n01bGceabfVYlIqFDP/RhzDubOhdiTt/LvDhOom9WAjS99qGAXkWqlcD+G0tJg8GC46ioo+M1FEFHCjAHTaNUiyu/SRCTEaFjmGHAOLki4lSX2N+jksLugMKKE3nt7c0P/G/wuT0RCkMK9hv34I5w78lZS+z5DnR2NadegDQ3qQnRxNO/e967f5YlIiFK415CSEnjqKbh31u2UDHyGxulRbJi8lhOaRftdmoiEAYV7DVi1CoYPh2W5iTDkaSKyo1g7aY2CXUSOGZ1QrUZFRfDgg3D66fDvfXfDkCeJ2B3BmgmrOaHZCX6XJyJhRD33arJ8eaC3vnIlxA++j+97PsHxu49nzfg1nNj8RL/LE5Ewo3A/Svn50O+W+1ieP5u63aHFb0r5vmUqTXOa8v247xXsIuILhftRWLIEBo27hZwLZ2B5dahTUo89Bq33tGbp/Utp06KN3yWKSJhSuB+B3bth1CiY+dVIGDyDphnN2DDpB6IjdcJURIKDwv0wjH1lLHOXLmLTJthHAQxeReTuKH546HsFu4gEFYV7FV01ZRhzC2dBcyAq0NYypyXfjv9WM2FEJOgo3A/BOTg/8c98FjUL1kUzutNaJk6IokEDvysTETkwhftBbN0Kv7l1BJt7vUi9TS34PGEtZ/0qyu+yREQOSV9iqkRpKTz7LHS+7BY29/objbc3J+2JHxTsIlJrqOdewbp1cOONsCQzMBMmIqsZ66esITqyud+liYhUmcLd8/KiV3lp/kq++BKs2fcw+B2idkexZqJmwohI7aNwBwYmDeNdmwUnApcH2iJ3RbJmwhrNhBGRWimsw33vXjjz5j/zzUmzsPXRJPR4mjPPNOrWqcOlfS+lUYNGfpcoInJEwjbcly6FAaNvJLvfizTY2oLvHlpLl5OiAnMfzfwuT0TkqITdbJm8PEhMhLNuuoXsfs/RdEtjtj2+5udgT0yEpCS/yxQROSphFe6LF8PJJ8OTi0fCZTOISmvIhlfyaT7hoZ+DPTkZsrMDj0VEaqmwGJZZuXYz4x/MZsECiDjtGbhgBlG7o1j76A9EN54UCPTk5MDBCQkwdaqGZkSkVjMXBD3UPn36uJSUlBp57fPvHsaSprPK/Y0SsSuCtRPWBmbCOAd1yuwsLVWwi0itYGYrnHN9KtsXssMyO3ZAp/+6niURs6i7uTkX7buGa46/hhuibygf7ImJ5Z+YmKghGRGp9UJuWMY5ePVVuOHpGyka8BKN01qw8Yl1tGwW9csD94+x7x+K2f8YNDQjIrVaSIX75s1w002wMO0WGPwckZnN+fGxH2geEfXLg80gKqr8GPvUqYF9UVEKdhGp1UJizL20FGbMgPvug8KYkRT/YXrghOnEtYdeOqDivHbNcxeRWuKoxtzN7AUzSzez/5Rpa25mH5nZWu++mdduZvaUma0zs2/NrFf1/RqVW7MGzjsPRo6EZmcn/BTsayasqdqaMBWDXMEuIiGgKidUXwL6V2gbDSx2zsUAi73HAAOAGO82Animesqs3Fm3/ZH4Z+rzZe/61Emsz5YzniJid4TWhBGRsHfIcHfOfQ5kVWgeBLzsbb8MXFamfZYLWApEmVnraqr1F+LbdqVxZgc60IGOdTrQu7g3q8etVrCLSNg70hOqrZxz27zt7UArb7stsKXMcVu9tm1UYGYjCPTu6dChwxEV8eKYJF4k6YieKyISyo56nrsLnJE97LOyzrmZzrk+zrk+LVu2PNoyRESkjCMN9x37h1u8+3SvPRVoX+a4dl6biIgcQ0ca7u8Aw7ztYcDbZdr/5M2aORPYXWb4RkREjpFDjrmb2WygHxBtZluBCcAjwFwzGw5sAq70Dv8AuBhYB+QD19dAzSIicgiHDHfn3DUH2HVhJcc6YOTRFiUiIkcnZBcOExEJZwp3EZEQpHAXEQlBQbFwmJntJHBi1k/RQIbPNRwu1Vzzalu9oJqPlWCo+STnXKVfFAqKcA8GZpZyoNXVgpVqrnm1rV5QzcdKsNesYRkRkRCkcBcRCUEK95/N9LuAI6Caa15tqxdU87ES1DVrzF1EJASp5y4iEoIU7iIiIShsw93MNprZSjP7xsxSvLZKrw3rNzOL8+rcf8sxszvNLMnMUsu0X+xznUF9vd3DqPkxM/veq2u+mUV57R3NrKDM+z0jiGo+4GfBzMZ47/MaM7soiGr+e5l6N5rZN1677++zmbU3s0/N7DszW2VmCV57UH+ey3HOheUN2AhEV2h7FBjtbY8GpvhdZyV11yVw9auTgCTgHr9rKlPbuUAv4D+Hek8JrB76IWDAmcCyIKr590A9b3tKmZo7lj0uyN7nSj8LQHfg30BDoBOwHqgbDDVX2P84MD5Y3megNdDL2z4e+MF7L4P681z2FrY99wM40LVhg8mFwHrnnN/f6P0FF8TX2z2Qymp2zi1yzhV7D5cSuOhM0DjA+3wgg4A5zrm9zrkNBJbjPqPGijuAg9VsZkZg2fDZx7Sog3DObXPOfe1t7wFWE7hkaFB/nssK53B3wCIzW+FdzxUOfG3YYHI15f8R3Ob9GfhCsAwjVXC419sNNn8m0CPbr5OZ/Z+ZfWZm5/hV1AFU9lmoDe/zOcAO59zaMm1B8z6bWUfgdGAZtejzHM7hfrZzrhcwABhpZueW3ekCf2sF1TxRM2sADATe8JqeAboApxG4CPnj/lRWNcH4nh6MmY0FioHXvKZtQAfn3OnAXcDrZhbhV30V1KrPQgXXUL7DEjTvs5k1Bd4E7nTO5ZTdF+yf57ANd+dcqnefDswn8Kfqga4NGywGAF8753YAOOd2OOdKnHOlwN/w4c/tKqiV19s1s+uAS4Gh3j9ivKGNTG97BYHx61jfiizjIJ+FYH+f6wH/Bfx9f1uwvM9mVp9AsL/mnHvLa641n+ewDHcza2Jmx+/fJnAC7T8c+NqwwaJcD6fCmN5gAr9DsKl119s1s/7AKGCgcy6/THtLM6vrbXcGYoAf/amyvIN8Ft4BrjazhmbWiUDN/3us6zuI3wLfO+e27m8IhvfZOw/wPLDaOfdEmV215/Ps9xldP25AZwIzCP4NrALGeu0tgMXAWuBjoLnftZapuQmQCUSWaXsFWAl8S+DD1drnGmcT+JN6H4Exx+EHek8JzCqYRqBXthLoE0Q1ryMwfvqNd5vhHTvE+7x8A3wN/CGIaj7gZwEY673Pa4ABwVKz1/4ScHOFY31/n4GzCQy5fFvmc3BxsH+ey960/ICISAgKy2EZEZFQp3AXEQlBCncRkRCkcBcRCUEKdxGREKRwFxEJQQp3EZEQ9P9ZK9g9Ml/jMgAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ax.plot(inputs, homomorphic_predictions, color=\"green\")\n",
"display(fig)"
]
},
{
"cell_type": "markdown",
"id": "53ecca94",
"metadata": {},
"source": [
"### Enjoy!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.7.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}