mirror of
https://github.com/kashifulhaque/smoltorch.git
synced 2025-12-06 07:02:51 +00:00
642 lines
36 KiB
Plaintext
642 lines
36 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "1742e66e",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import math\n",
|
|
"import mlx.core as mx\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"%matplotlib inline"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "e010d996",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def f(x):\n",
|
|
" return 3*x**2 - 4*x + 5"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "257a1b89",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"20.0"
|
|
]
|
|
},
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"f(3.0)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "17691e6d",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[<matplotlib.lines.Line2D at 0x118cd58b0>]"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAP7JJREFUeJzt3Qd0VVX6/vEnvScQIAkhCYQaem9iF0HFggVlxIYMNnAE28jMiDM/HbGN+scC6lhAQRQVFUUcRIVBQhek9xIIKRBSSEjPf50dkgFFpdybc8v3s9ZdObeQbE9i7pN99n5fn6qqqioBAAC4EF+7BwAAAPBzBBQAAOByCCgAAMDlEFAAAIDLIaAAAACXQ0ABAAAuh4ACAABcDgEFAAC4HH+5ocrKSqWnpysiIkI+Pj52DwcAAJwEqzZsQUGB4uPj5evr63kBxQoniYmJdg8DAACchrS0NCUkJHheQLFmTmr+AyMjI+0eDgAAOAn5+flmgqHmfdzjAkrNZR0rnBBQAABwLyezPINFsgAAwOUQUAAAgMshoAAAAJdDQAEAAC6HgAIAAFwOAQUAALgcAgoAAHA5BBQAAOByCCgAAMD9A8rChQt1xRVXmEY/ViW4Tz/99BeNgMaPH6/GjRsrJCRE/fv319atW497TU5OjoYNG2aqwNarV08jRozQ4cOHz/y/BgAAeGdAKSwsVOfOnfXKK6+c8PlnnnlGEydO1OTJk7V06VKFhYVp4MCBKi4urn2NFU7Wr1+vefPm6YsvvjCh54477jiz/xIAAOAxfKqsKY/T/cc+Ppo1a5YGDx5s7lufyppZeeCBB/Tggw+ax/Ly8hQbG6t33nlHQ4cO1caNG9WuXTstX75cPXr0MK+ZO3euLrvsMu3du9f8+5NpNhQVFWU+N714AABwD6fy/u3QNSg7d+5URkaGuaxTwxpI7969lZqaau5bH63LOjXhxGK93tfX18y4nEhJSYn5jzr25gybMvL111lrNXtNulM+PwAAODkODShWOLFYMybHsu7XPGd9jImJOe55f39/RUdH177m5yZMmGCCTs3NatXsDPM3Zmna0j16Z/Eup3x+AADgQbt4xo0bZ6aDam5paWlO+TpDeiTI39dHK3cf0uaMAqd8DQAAUMcBJS4uznzMzMw87nHrfs1z1sesrKzjni8vLzc7e2pe83NBQUHmWtWxN2eIiQhW/7bVsz/vL9vjlK8BAADqOKAkJyebkDF//vzax6z1Itbakr59+5r71sfc3FytXLmy9jXffvutKisrzVoVu/2hd5L5+MmqvSouq7B7OAAAeCX/U/0HVr2Sbdu2HbcwdvXq1WYNSVJSksaMGaMnnnhCrVq1MoHl0UcfNTtzanb6tG3bVpdccolGjhxptiKXlZVp9OjRZofPyezgcbZzWjZUk3oh2pd7RHPW7tc13RLsHhIAAF7nlGdQVqxYoa5du5qb5f777zfHVnE2y8MPP6x7773X1DXp2bOnCTTWNuLg4ODazzFt2jSlpKTooosuMtuLzz77bL3++utyBb6+PvpDr+pFuFzmAQDADeug2MXZdVCy8ovV96lvVVFZpXljz1Wr2AiHfw0AALxNvl11UDxFTKS1WLZ6K/T7y5yzYwgAAPw6Asqv+EOv6sWyH7NYFgCAOkdA+RXntGpkFsvmHSnTV+v21+13BQAAL0dA+RV+vj4a2vPoYtmlXOYBAKAuEVB+w5AeiSaoLNuVo21ZVJYFAKCuEFB+Q1xUsC5MYbEsAAB1jYDyO25ksSwAAHWOgPI7zm3dSPFRwcotKtPX60/cbRkAADgWAeV3WGtQbuhZveV4+lIaCAIAUBcIKCfh+p4J8vWRlu7M0fbsw87/rgAA4OUIKCehcVRI7WLZGcuYRQEAwNkIKKdYWfajlXtVUl7hzO8JAABej4Byks5r3UiNo4J1yCyWzfT6HxwAAJyJgHKS/P18dX2PmsqyXOYBAMCZCCin4PqeiWaxbOqOg9rBYlkAAJyGgHIKrOaB57c5ulh2Of15AABwFgLKKWKxLAAAzkdAOUUXtGmk2Mgg5RSW6j8slgUAwCkIKKexWPaGmsWy1EQBAMApCCinuVjWx0davP2gdh4odPx3BQAAL0dAOQ0J9UN1futG5njGcrYcAwDgaASUM10su2KvSssrHfk9AQDA6xFQTpPVmycmIkgHC0s1d32G1/8gAQDgSASUM1gsO/ToLMp7qbsd+T0BAMDrEVDOwI29kuTn66Nlu3K0KSPf63+YAABwFALKGYiLCtbA9rHmeCqzKAAAOAwB5Qzd3KeZ+fjpj/uUX1zmiO8JAABej4Byhvo0j1br2HAVlVbo45V7vf4HCgAARyCgnCEfHx/d3KepOX53yW5VVVU54vsCAIBXI6A4wNXdEhQe5K8d2YX6YdtBR3xKAAC8GgHFAaxwck23JuZ4auouR3xKAAC8GgHFQWou83yzMVP7co846tMCAOCVCCgO0io2wiyYrayS3l9Kfx4AAM4EAcWBbunbrLaBYEl5hSM/NQAAXoWA4kAXt4tVbGSQDhwu1dx19OcBAOB0EVAcKMDPVzf2ql6LQmVZAABOHwHFwf7QK1H+vj5aufuQ1qfnOfrTAwDgFQgoDhYTGaxLOsSZ43fpzwMAwGkhoDhxseynq/cpr4j+PAAAnCoCihP0bFZfKXERKi6r1MyVac74EgAAeDQCirP68/StXiw7bekeVVrFUQAAwEkjoDjJ4C5NFBHkr50HCrVo2wFnfRkAADwSAcVJwoL8dW33BHPMlmMAAE4NAcWJbjran+fbTZnae6jImV8KAACPQkBxopYx4erXsoHpz2OtRQEAACeHgOJkN/ep3nL8wfI0FZfRnwcAgJNBQHGy/m1jFB8VrJzCUs1Zu9/ZXw4AAI9AQHEyf6s/T+8kc8xiWQAATg4BpQ7c0DNJAX4+Wp2Wq7V76c8DAMDvIaDUgUYRQbqsY2NzPDV1V118SQAA3BoBpY7ccrSy7Gdr0nXwcEldfVkAANwSAaWOdEuqr84JUSotr9R0thwDAPCbCCh12J/n9rOTzfHUJbtNUAEAACdGQKlDl3ZorNjIIGUXlOjLtel1+aUBAHArBJQ6FOjvq1v6Vhdue3PRTlVV0eUYAIATIaDUsRt7JSnI31fr9uVr+a5Ddf3lAQBwCwSUOlY/LFDXdKvucvzWop11/eUBAHALBBQb3N6v+jLPfzZkKC2HLscAAPwcAcUGrWIjdE6rhqbL8ZTFFG4DAMDpAaWiokKPPvqokpOTFRISohYtWujxxx8/bkGodTx+/Hg1btzYvKZ///7aunWrvEnNlmOry/HhknK7hwMAgGcHlKefflqTJk3Syy+/rI0bN5r7zzzzjF566aXa11j3J06cqMmTJ2vp0qUKCwvTwIEDVVxcLG9xXqtGatEoTAUl5Zq5Is3u4QAA4NkBZfHixbrqqqs0aNAgNWvWTNddd50GDBigZcuW1c6evPjii/rb3/5mXtepUydNnTpV6enp+vTTT+UtfH19NLxf9SzKO4t3qcK63gMAAJwTUM466yzNnz9fW7ZsMffXrFmjRYsW6dJLLzX3d+7cqYyMDHNZp0ZUVJR69+6t1NTUE37OkpIS5efnH3fzBNd0a6KokADtPlikbzdl2T0cAAA8N6A88sgjGjp0qFJSUhQQEKCuXbtqzJgxGjZsmHneCieW2NjY4/6ddb/muZ+bMGGCCTE1t8TERHmC0EB//aFXkjlmyzEAAE4MKB9++KGmTZum6dOna9WqVZoyZYqee+458/F0jRs3Tnl5ebW3tLQ0j+py7Ofro9QdB7Uh3TNmhgAAcLmA8tBDD9XOonTs2FE333yzxo4da2ZBLHFxceZjZmbmcf/Oul/z3M8FBQUpMjLyuJuniK8Xoss6NjbHb/1A4TYAAJwSUIqKiuTre/yn9fPzU2Vldfdea/uxFUSsdSo1rDUl1m6evn37enXhts9Xp5tGggAAeDuHB5QrrrhC//znP/Xll19q165dmjVrlp5//nldffXV5nkfHx+zJuWJJ57Q559/rrVr1+qWW25RfHy8Bg8eLG/UNam+uibVU2lFpaYt3W33cAAAsJ2/oz+hVe/EKtR2zz33KCsrywSPO++80xRmq/Hwww+rsLBQd9xxh3Jzc3X22Wdr7ty5Cg4Olre6vV+y7t3zo95bslt3n99CQf5+dg8JAADb+FQdW+LVTViXhKzdPNaCWU9Zj1JeUalzn/lO6XnFem5IZ13XvbqhIAAAnuJU3r/pxeMi/P18dctZ1WtR3ly087jWAAAAeBsCigsZ2jNRIQF+2rg/X0t25Ng9HAAAbENAcSH1QgN1bfcm5pgtxwAAb0ZAcTE1/Xm+2Zip3QcL7R4OAAC2IKC4mBaNwnVBm0aylqBYTQQBAPBGBBQXdPvZ1bMoM1fsVUFxmd3DAQCgzhFQXNDZLRuqdWy4DpeU6/1le+weDgAAdY6A4oKsart/PLu5OX5r0S6Vlle3CQAAwFsQUFzUVV3jFRMRpIz8Yn2+Jt3u4QAAUKcIKC7KKnVfsxbl9YXbVVlJ4TYAgPcgoLiwG3snKTzIX1syD+v7LVl2DwcAgDpDQHFhkcEBGtY7yRxPXrDD7uEAAFBnCChuULgtwM9Hy3bmaNWeQ3YPBwCAOkFAcXFxUcEa3KW6/P3rzKIAALwEAcUN3Hle9ZbjrzdkaEf2YbuHAwCA0xFQ3EDLmAj1bxtryt+/8d+ddg8HAACnI6C4ibuOzqJ8vGqvsgqK7R4OAABORUBxEz2aRat70/qmquwUmggCADwcAcWN3Hlu9SzKu6m7TZ8eAAA8FQHFjVjrUJo3ClN+cblm0EQQAODBCChuxNfXp3YW5c1FO2kiCADwWAQUNzO4axM1igjS/rxizaaJIADAQxFQ3LGJYL/qJoKvLdyuKmvvMQAAHoaA4u5NBDdn2z0cAAAcjoDihqJCAkxIsUxesN3u4QAA4HAEFDc1vF8z00Rw6c4c/UgTQQCAhyGguKnGUSG6qqaJ4MIddg8HAACHIqC4sTuObjmeuz5DOw8U2j0cAAAchoDixlrHRuiilJijTQSZRQEAeA4Cipu787wW5uNHK/cqu6DE7uEAAOAQBBQ317NZfXVNqkcTQQCARyGguDkfH6v8ffUsytTUXcovLrN7SAAAnDECigcY0C5WLWPCTRNBq9MxAADujoDiIU0ER13QoraJYFFpud1DAgDgjBBQPMQVneLVtEGocgpLNX3pHruHAwDAGSGgeAh/P1/dc371LMprC3eouKzC7iEBAHDaCCge5OquCWpSL8RsN/5wRZrdwwEA4LQRUDxIoL+v7jqvurrs5O+3m63HAAC4IwKKhxnSI1ExEUFKzyvWJ6v22j0cAABOCwHFwwQH+NX26Hn1++0qr2AWBQDgfggoHujG3klqEBaoPTlF+nxNut3DAQDglBFQPFBooL9GnJNsjl/+bpsqKqvsHhIAAKeEgOKhbu7TVFEhAdqRXaiv1u23ezgAAJwSAoqHiggO0PB+zczxy99uUyWzKAAAN0JA8WDDz0pWeJC/NmUU6JuNmXYPBwCAk0ZA8WBRoQG6pW9Tc/zSt9tUVcVaFACAeyCgeLgRZycrJMBPa/fl6fst2XYPBwCAk0JA8XANwoM0rHeSOX5p/lZmUQAAboGA4gWswm1WGfxVe3KVuv2g3cMBAOB3EVC8QExksIb2TKxdiwIAgKsjoHiJO89roQA/H6XuOKgVu3LsHg4AAL+JgOIlmtQL0bXdEswxsygAAFdHQPEi95zfUn6+PlqwJVtr0nLtHg4AAL+KgOJFkhqE6qrO8bU9egAAcFUEFC9zzwUt5eMjzduQqQ3p+XYPBwCAEyKgeJmWMeG6rGNjczxx/la7hwMAwAkRULzQmItamVmUuesztHZvnt3DAQDgFwgoXqhVbIQGd2lijp+ft9nu4QAA8AsEFC9130WtzI6e7zZna+XuQ3YPBwAA5weUffv26aabblKDBg0UEhKijh07asWKFbXPW111x48fr8aNG5vn+/fvr61bWQ9Rl5o1DNN1R+uiMIsCAPD4gHLo0CH169dPAQEB+uqrr7Rhwwb961//Uv369Wtf88wzz2jixImaPHmyli5dqrCwMA0cOFDFxcWOHg5+w70XtTTVZX/YdlCLtx/gXAEAXIZPlTWd4UCPPPKIfvjhB/33v/894fPWl4uPj9cDDzygBx980DyWl5en2NhYvfPOOxo6dOjvfo38/HxFRUWZfxcZGenI4Xud8Z+t09TU3erRtL5m3tVXPtbqWQAAnOBU3r8dPoPy+eefq0ePHhoyZIhiYmLUtWtXvfHGG7XP79y5UxkZGeayTg1rsL1791ZqauoJP2dJSYn5jzr2BscYdUFLBfn7asXuQ6bCLAAArsDhAWXHjh2aNGmSWrVqpa+//lp33323/vSnP2nKlCnmeSucWKwZk2NZ92ue+7kJEyaYEFNzS0ys7syLMxcbGayb+zQ1x8/P22JmuAAA8LiAUllZqW7duunJJ580syd33HGHRo4cadabnK5x48aZ6aCaW1pamkPH7O3uOr+FQgP99NPePFNhFgAAjwso1s6cdu3aHfdY27ZttWfPHnMcFxdnPmZmHv9GaN2vee7ngoKCzLWqY29wnIbhQRrer1ntLEplJbMoAAAPCyjWDp7Nm48v/rVlyxY1bVp9GSE5OdkEkfnz59c+b60psXbz9O3b19HDwUm645wWigj216aMAn25dj/nDQDgWQFl7NixWrJkibnEs23bNk2fPl2vv/66Ro0aZZ63domMGTNGTzzxhFlQu3btWt1yyy1mZ8/gwYMdPRycpKjQAI08p7k5fuGbLSqvqOTcAQA8J6D07NlTs2bN0vvvv68OHTro8ccf14svvqhhw4bVvubhhx/Wvffea9anWK8/fPiw5s6dq+DgYEcPB6fAusxTLzRAO7IL9enqdM4dAMBz6qDUBeqgOM/kBdv11FeblBgdom8fOF8BfnRDAAB4QB0UuLdb+jY1i2bTco5o5oq9dg8HAOClCCg4Tmigv0Zd0MIcv/TtVhWXVXCGAAB1joCCX/hDryQ1jgrW/rxivb+sens4AAB1iYCCXwgO8NPoC1ua41e+264jpcyiAADqFgEFJzSke6JZKHvgcImmpu7iLAEA6hQBBScU6O+r+y5qXbuzp6C4jDMFAKgzBBT8qsFd4tW8UZgOFZXp7R+YRQEA1B0CCn6Vv5+vxvavnkV5Y+EOHSos5WwBAOoEAQW/aVDHxmrbOFIFJeV6+bttnC0AQJ0goOC3f0B8fTTu0hRzbC2WTcsp4owBAJyOgILfdW7rRjqnVUOVVVTpuf8c36kaAABnIKDgpPz5khT5+EifrU7X2r15nDUAgFMRUHBSOjSJ0tVdmpjjJ+dslBv2mAQAuBECCk7a/QNam/ooqTsO6vst2Zw5APBAVVVV2pZVYPcwCCg4eQn1QzX8rGbm+Kk5m1RRySwKAHiaL37ar4tfWKjHPltn6ziYQcEpuef8looKCdDmzAJ9vGovZw8APEhxWYWenrtJ1lX86LAgW8dCQMEpiQoN0OgLqhsJPv+fLTQSBAAPMmXxLu09dESxkUEaeW6yrWMhoOCU3dy3qZrUC1FGfrHe+mEnZxAAPEBOYWltQc4HB7RRaKC/reMhoOCUBQf46aGBbczxpO+36+DhEs4iALi5//fNFhUUl6td40hd2y3B7uEQUHB6ruwcr/bxkTpcUq6XvqUEPgC4s+3ZhzVt6R5z/LdBbU0Vcbsxg4LT+8Hx9dFfLmtrjt9bslu7DhRyJgHATU2Ys0nllVW6KCVGZ7VsKFdAQMFp69eyoc5r3cj8UD9LCXwAcEup2w/qm42Z8rN6rx39w9MVEFBwRh65tLoE/pc/7dePew5xNgHAjVRWVumfczaY4xt7JallTLhcBQEFZ6TtMYupJnxl7Z2neBsAuItZP+7Tun35Cg/y15j+reRKCCg4Y/df3FpB/r5atjNH327K4owCgBs4UlpR26H+ngtaqEG4vYXZfo6AgjMWXy9Et59dXdDnqa82qbyikrMKAC7uzUU7tD+v2NS1ur2fvUXZToSAAoe4+/wWqh8aoK1Zh/XRSkrgA4AryyooNnWsLA9f0sbUt3I1BBQ4RGRwgO69sPr65fPztqiotJwzCwAu6oV5W1VYWqHOCVG6olO8XBEBBQ5zU5+mSooOVVZBid5YSAl8AHBFmzMK9MHyo0XZLm/nEkXZToSAAocJ9Pc1U4WWSQu2KT33CGcXAFzMk3M2qrJKuqR9nHo2i5arIqDAoQZ1bKxezaJVXFZpth0DAFzHwi3ZWrAlWwF+PqaOlSsjoMChfHx89NiV7WTNGM5ek262HgMA7FdRWWVmTyw392mmZg3D5MoIKHC49vFRGtoryRz//fP15n8KAIC9Zq5I06aMAkWFBOhPF7V0+W8HAQVO8eCANooM9teG/fmacXQxFgDAHoUl5frXvC3m+N4LW6peaKDLfysIKHCK6LBAjb24tTl+7uvNyisq40wDgE1eW7hD2QUlZqflzX2busX3gYACp247bhUTrkNFZXrhm+rkDgCoW/tyj+j1hdVF2ayFsUH+rleU7UQIKHCaAD9fPXZFe3P87pLd2pJZwNkGgDr2zy83mJ2V1g7LSzvEuc35J6DAqc5u1VAD28eahbL/N3sD3Y4BoA4t2npAc9ZmyM/XR/+4qr3ZaekuCChwur8NameKuC3adkD/2ZDJGQeAOlBaXqnHPl9njm/u01RtG0e61XknoMDpEqNDdcc5zc3xE2aqsYKzDgBO9vYPO7U9u1ANw/+3acGdEFBQJ+65oIXiIoOVlnNE//7vDs46ADhRRl6xJs7fao7/fEmKqX3ibggoqBOhgf4ad1l1WeVXvtuu/Xn06QEAZ3lyzkbTrbhrUj1d2y3BLU80AQV15srO8erZrL6OlFXoKfr0AIBTLNlxUJ+vSZe1Hvbxqzq4bLfi30NAQd326bnCWkUufbY6XSt20acHAByprKJSj3223hzf2CtJHZpEue0JJqCgTln/swztmWiO/z6bPj0A4Ejvpu7W5swC1Q8N0EMD27j1ySWgwJY+PRHB/lq3L18frkjjOwAADpBVUKwXjvbbeWhgilv02/ktBBTUuQbhQRrbv3rL27NWn54j9OkBgDP19FebVVBSrk4JUbrh6Ey1OyOgwBZWsyqrT09OYan+3zfVW+EAAKdn5e4cfbxqrzn+x5XtTeVYd0dAgW19esZf0c4cT03dpc0Z9OkBgNNRUVml8UcXxt7QI1Fdk+p7xIkkoMA257RqZPr0lFdW6S+z1qqysorvBgCcounL9mh9er4ig/318CXuvTD2WAQU2OrvV7ZXWKCfVu4+pA9YMAsApySnsFTPfb3ZHD84sI1Z4+cpCCiwVeOoED0woDrxT5izUdkFJXxHAOAkPfv1JrPRwGoEaNU98SQEFNju1rOaqWOTKOUXl5tmggCA37cmLVczlleXanj8qvby9/Ost3TP+q+BW7JWmz95dUf5Hq0wu3BLtt1DAgCXVmkWxq5TVZV0Tdcm6tEsWp6GgAKX0DEhysykWB79bJ2KyyrsHhIAuKwPVqRpzd48hQf565GjjVg9DQEFLsNaixIXGazdB4v08rfb7B4OALhsxdgJczaa4zH9WykmIlieiIACl2H9JWDt6rG8tnC7tmRSGwUAfu4fn28wa/astXu3HZ159kQEFLgUqy5K/7axKquo0l+pjQIAx5m3IVNfrt1v1u5NuKajxy2MPZbn/pfBLfn4+OgfV7VXaKCflu86pJkraSYIAJaC4jI9+uk6c/zHc5JNd3hP5vSA8tRTT5k3nTFjxtQ+VlxcrFGjRqlBgwYKDw/Xtddeq8zMTGcPBW6iSb0Q3X9xdTPBJ+ds0oHD1EYBgGe/3qyM/GI1bRCqMRdV/470ZE4NKMuXL9drr72mTp06Hff42LFjNXv2bM2cOVMLFixQenq6rrnmGmcOBW7Guq7arnGkKUD0zy+rF4MBgDc3A3x3yW5zPOHqjgoJ9JOnc1pAOXz4sIYNG6Y33nhD9ev/r3FRXl6e3nzzTT3//PO68MIL1b17d7399ttavHixlixZ4qzhwM1Y11Wt66s+PtKsH/dp0dYDdg8JAGxRUl6hP3+81tQ8GdI9QWe1bOgV3wmnBRTrEs6gQYPUv3//4x5fuXKlysrKjns8JSVFSUlJSk1NPeHnKikpUX5+/nE3eL7OifV0S5+m5vhvn66lNgoArzTp++3alnVYDcMD9ddBbeUtnBJQZsyYoVWrVmnChAm/eC4jI0OBgYGqV6/ecY/Hxsaa507E+jxRUVG1t8TERGcMGy7ogYFtFBsZpF0Hi/Tqd9RGAeBdtmYW6JWjv/seu6K96oUGyls4PKCkpaXpvvvu07Rp0xQc7JjiMePGjTOXhmpu1teAd4gMDtDfr6iujTJpgfVXBLVRAHhPOftHPllryi5clBKjyzs1ljdxeECxLuFkZWWpW7du8vf3NzdrIezEiRPNsTVTUlpaqtzc3OP+nbWLJy4u7oSfMygoSJGRkcfd4D0u6RBn/ue0/if9yyyr90SV3UMCAKebtmyPVu4+pLBAPz0+uIPZEetNHB5QLrroIq1du1arV6+uvfXo0cMsmK05DggI0Pz582v/zebNm7Vnzx717dvX0cOBB9VGCQnw07KdOZq5Yq/dQwIAp9qfd0RPf7XJHD98SYri64V43Rn3d/QnjIiIUIcOHY57LCwszNQ8qXl8xIgRuv/++xUdHW1mQ+69914TTvr06ePo4cBDJNQP1diLW5m6KE98uUHntm6kuCjP7D8BwLtZs8SPfrpeh0vK1TWpnm46ulnA29hSSfaFF17Q5Zdfbgq0nXvuuebSzieffGLHUOBGbu+XrM4JUaYHxbhPfuJSDwCP9NW6DH2zMVMBfj56+tpOpqy9N/KpcsML+tY2Y2s3j7VglvUo3reifdDERSqtqNQz13XS9T3Y0QXAc+QVlan/CwuUXVCiP13YUvcPaCNPcirv3/TigVtpFRuh+wdUl3h+fPYGpecesXtIAOAwE77aaMJJi0ZhGnVhS68+swQUuJ2R5zQ312ULSsrNFjw3nAQEgF9I3X5QM5ZXl9F46tpOCvL3/HL2v4WAArdjXY99bkhnBfn7auGWbH1w9H9oAHBXxWUV+susteZ4WO8k9WwWLW9HQIFbatEoXA8NrL42+8SXG7X3UJHdQwKA0/b03E3aeaDQVM7+86UpnEkCCtzZ8H7J6tG0vtmK9+eP2dUDwD0t3nZAb/+wyxxbu3asCtpgBgVufqnH2skTHOCrH7Yd1LSle+weEgCckrwjZXpw5praSzvnt4nhDB7FJR64teaNwvXwwOrp0CfnbFRaDpd6ALiPf8xer/S8YjVtEKq/XOY9nYpPBgEFbu+2s5qpV7NoFZVW6OGPfjINtgDA1c1dt1+frNonqw7b89d3VliQw4u7uzUCCtyer6+Pnh3SyfTqSd1xUO8t3W33kADgN2UVFJvmp5a7zmuh7k3ZtfNzBBR4hKYNwjTusupLPRPmbNLug4V2DwkATsiq3fSXT9Yqp7BUbRtHakz/6uKTOB4BBR7jpt5N1bd5Ax0pq9BDXOoB4KKsjuzfbMxSoJ+vXrihswL9eSs+Ec4KPOpSj7WrJzTQT8t25mhKavW2PQBwFdZCfmthrOWBAa2VEvfb/Wi8GQEFHiUx+n8r4WsKHwGAK6iorNIDH65RYWmFejarrz+e09zuIbk0Ago8jlVL4OyWDVVcVqmHZq4xvxQAwG5vLdqpZbtyzCzvv4Z0MbWc8OsIKPA4Pj4+eurajgoP8teK3Yc0ecF2u4cEwMttzijQs19vNsePXt5OSQ1C7R6SyyOgwCMl1A/VY1e0M8fPz9uiVXsO2T0kAF6qtLxSYz9YrdKKSl2YEqOhPRPtHpJbIKDAY13XPUFXdo43l3j+9P6Pyi8us3tIALzQxPlbtWF/vuqHBpjZXWuWF7+PgAKPZf0SeOLqDkqMDtHeQ0dM3QGr/gAA1JWVuw/p1e+3meN/Xt1RMRHBnPyTRECBR7O6gk4c2lX+vj764qf9mrlyr91DAuAlikrL9cCHq2Wt07+6axNd1rGx3UNyKwQUeLyuSfV1/4DqSo2PfbZe27MP2z0kAF7giS83atfBIsVFBuvvV7a3ezhuh4ACr3DXuS3Ur2V1ldl7p/+okvIKu4cEwIN98VO6pi/dY46fG9JZUSEBdg/J7RBQ4DVVZp+/vouiwwLNYrWnv6re7gcAjrbrQKEe+XitOb7n/BY6u1VDTvJpIKDAa8RGBuu5IZ3M8Vs/7NS3mzLtHhIAD2PNzo5+f5UOl5SbarH3X0wjwNNFQIFXuTAlVred1cwcPzjzJ2XlF9s9JAAe5MkvN2rdvuotxRP/0FX+frzNni7OHLzOI5emmBbnVqvzsdYKe0rhA3CAr9bu15TU3ebYuqTcOCqE83oGCCjwOsEBfnrpD10VEuCnH7Yd1GsLd9g9JABubs/BIj380U/m+M7zmuuClBi7h+T2CCjwSi1jwvX3K6tL4f/rP5v1I6XwAZzhupOCknJ1b1pfDw5ow7l0AAIKvNb1PRI1qFNjlVul8GdQCh/A6Xnqq036aW+e2UpsrTsJYN2JQxBQ4NWl8J+8uqOa1AtRWs4R/W3WOkrhAzglX6/P0Ns/7DLH/xrS2fw+gWMQUODVqv/i6SI/Xx99viZdH1EKH8BJSssp0kMz15jjkeckq3+7WM6dAxFQ4PW6N43W2P6tzHl49LN12rg/3+vPCYDfVlpeqXtNl/RydUmsp4cvSeGUORgBBZB09/ktdU6rhiouq9Sd765UblEp5wXAr3pm7iatTstVZLC/Xr6RdSfOQEABJHOJx+p6nFA/RHtyinTfjNWqoD4KgBP4ZkOm/r1oZ22fnYT6oZwnJyCgAEfVDwvUazd3V5C/rxZsydaL32zh3AA4zr7cI3rg6LqT2/sla0D7OM6QkxBQgGO0j4/SU9d2NMcvfbvNrNAHAEtZRaXunb5KeUfK1DkhylSlhvMQUICfubprQm2/ngc+XKPt2Yc5RwD0+BcbtGpPriLMupNuCvTnLdSZOLvACfx1UFv1ahZtOpJai2atjwC81/vL9mhq6m75+EgvXN9FidGsO3E2AgpwAlYlyJeHdVVsZJC2ZR3Wgx+uoYgb4KWW78rR+M/WmeMHLm5NvZM6QkABfkVMRLAm3dRdAX4+mrs+Q5MWbOdcAV7GWhR717srVVZRZVpjjLqgpd1D8hoEFOA3dEuqr79f2d4cP/f1Zi3cks35ArzEkdIK3TF1hQ4Wlqpd40g9e10n0yIDdYOAAvyOG3sl6YYeibLKolhNBa3y1gA8W1VVlR76aI3Wp+erQVig3ri1h0ID/e0ellchoAC/w/qL6R9XtTfbCnOLysyiWesvKwCe69Xvt+uLn/bL39fHXOqlCWDdI6AAJyE4wM/8krL+ktqwP19/nbWWRbOAB1eKfe4/m82x9cdJr+Rou4fklQgowEmKrxeil27sasrif/LjPk1ZXN1iHYDn2JpZoDEfrFZVlXRTnyQN693U7iF5LQIKcArOatFQ445Wj3ziy41K3X6Q8wd4iLyiMo2cusLUPeqdHK3HrqheIA97EFCAUzTi7GRd2Tle5ZVVuvPdFdqWVcA5BNxceUWlRr+/SrsOFpn1Jq8O62bqIcE+nH3gNBbNPnNdJ3VLqqf84nLd9vZyZReUcB4BN/bUV5v0360HFBLgpzdu6aEG4UF2D8nrEVCA01w0a/0Sa9ogVHsPHdEfpyxXUSnl8AF39PHKvfr3op3m+F/Xd1a7+Ei7hwRmUIDTZ/2F9c7wXqofGqA1e/P0p/dXq8IqlgLAbfy455DGzVprjv90YUtd1rGx3UPCUcygAGcguWGYmUmxupp+szHTdDsF4B72HCzSyKkrVVpeqQHtYjWmf2u7h4RjEFCAM9SjWbSev76zOX5n8S69eXSqGIDrOnC4RLe8tdR8bNs4Us/f0EW+vpSxdyUEFMABLu8Ur0dqtx9v0Nx1GZxXwEUVlpRrxDvLzY6dhPohmjK8p8KDKGPvaggogIPceW5zDeudZAo83TfjR3NtG4BrKauo1N3TVpl1Y9FhgZp6ey/FRAbbPSycAAEFcGTPnivb64I2jVRSXqk/TllhrnEDcJ0GgH/+6CfTldzaTvzmrT3UvFG43cPCryCgAA7k7+erl2/spvbxkaZF+23vLFNuUSnnGHABT8/dbNpUWO0qrEJsXZPq2z0k/AYCCuBgYUH+euu2noqPCtaO7ELdMXWlSsrpfgzY6a1FOzV5wXZz/NQ1HXVBSgzfEBdHQAGcIDYyWG8P76WIIH8t25Wjh2b+pEpqpAC2mL0mXY9/WV0C4KGBbTSkRyLfCTdAQAGcpE1chCbd1F3+vj76fE26nj3avh1A3Vm87YAe+HCNWbx+a9+muuf8Fpx+bw0oEyZMUM+ePRUREaGYmBgNHjxYmzcf/4u5uLhYo0aNUoMGDRQeHq5rr71WmZmZjh4KYLuzWzXUhGs6muNJ3283NwB1Y316nu54d6VKKyp1Wcc4jb+ivVnMDi8NKAsWLDDhY8mSJZo3b57Kyso0YMAAFRYW1r5m7Nixmj17tmbOnGlen56ermuuucbRQwFcgjWd/PAlbczx03M3mWvhAJwrLafINPI8XFKu3slWMcUuZnEs3IdPlbXvyomys7PNTIoVRM4991zl5eWpUaNGmj59uq677jrzmk2bNqlt27ZKTU1Vnz59fvdz5ufnKyoqynyuyEiaOsE9PP+fzZr47TZz/OTVHXVj7yS7hwR4pIOHSzRkcqp2HChUSlyEPryrryKDA+weFnRq799OX4NiDcISHR1tPq5cudLMqvTv37/2NSkpKUpKSjIBBfBUYy9urTvObW6O//rpWn2yaq/dQwI8jtVV/PYpK0w4aVIvRFNu70U4cVNOre1bWVmpMWPGqF+/furQoYN5LCMjQ4GBgapXr95xr42NjTXPnUhJSYm5HZvAAHdjXfsed2mKSsoqNCV1tx6cucY0GbTK5ANwTDgZ/vZyrUnLVb3QABNOrB11cE9OnUGx1qKsW7dOM2bMOOOFt9aUUM0tMZEtYnDfkPLYFe01tGeirF3HY2as1n/W07cHcFQ4Wbozx2zvf/u2nmoZQ5VYd+a0gDJ69Gh98cUX+u6775SQkFD7eFxcnEpLS5Wbm3vc661dPNZzJzJu3DhzqajmlpaW5qxhA05ndUz959UdNbhLvMorqzR6+o9asCWbMw84KJxMGdGLKrEewOEBxVpza4WTWbNm6dtvv1VycvJxz3fv3l0BAQGaP39+7WPWNuQ9e/aob9++J/ycQUFBZjHNsTfAnVm7CZ4b0lmXdogzWyDvmLpCqdsP2j0swCPCSTdK2HsEX2dc1nnvvffMLh2rFoq1rsS6HTlyxDxvXaIZMWKE7r//fjO7Yi2aHT58uAknJ7ODB/Ckvj3/b2hXXZQSY5oLjpiyXCt359g9LMBtEE48m8O3Gf9aEZy3335bt912W22htgceeEDvv/++Wfw6cOBAvfrqq796iefn2GYMT1JcVqGRU1fov1sPmL8Ap43srU4Jxy8iB3A8wol7OpX3b6fXQXEGAgo8zZHSCt369jIt25mjqJAAzbijj9o25lImcCKEE/flUnVQAPy+kEA/0wG5S2I95R0p003/XqqtmQWcOuBnCCfeg4ACuIhwa4Hf7b3UPj5SBwtLdf1rqVqddvxuN8CbEU68CwEFcCHW5Z33RvRW58R6OlRUphvfWKIfth2we1iA7Qgn3oeAAriY+mGBmvbH3urXsoGKSivMFsq56/bbPSzANoQT70RAAVz0co+1JqWmTso901ZpxrI9dg8LqHPWmqzb3qLOiTcioAAuKsjfTy/f2K22LP4jn6zV5AXb7R4WUGfSc49oyOTFWraLImzeiIACuHjF2QnXdNRd57Uw95/6apMmzNloKjYDnmxTRr6ueXWxtmQeVmxkkD64sy8VYr0MAQVwcVbxw0cuTTGdkC2vLdyhRz5eq/KKSruHBjjF4m0HNGRSqjLyi9UqJlyf3NNP7eKpC+RtCCiAm7jzvBZ65tpO8vWRPliRZpoMWlVoAU/y2ep9pmhhQUm5eiVH66O7zlKTeiF2Dws2IKAAbuT6nol6dVh3Bfr5au76DN3+znIdLim3e1jAGbMuW762YLvum7FaZRVVGtSpsabe3ktRoQGcXS9FQAHczCUd4vTO7T0VFuinxdsPmlopOYWldg8LOG0VlVX6x+wNmvDVJnN/xNnJemloVwUH+HFWvRgBBXBDZ7VoqPfv6KPosED9tDdP101erJ0HCu0eFnDKrMuUo6at0juLd5n7fxvUVo9e3k6+1rVMeDUCCuCmrI7HH97ZV/FRwdqRXairXl6kBVuy7R4WcNIOFZaavlPW5UrrsuXLN3bVH89pzhmEQUAB3FjLmHB9Orqfujetr/zicg1/e5m5js82ZLi6tJwiXTt5sVbsPqTIYH9NHdFLl3eKt3tYcCEEFMDNxUQEa/rI3rqhR3VBN+s6/pgPVrPDBy5rTVqurpm02Mz8WTOAH919lvo0b2D3sOBiCCiAh1Sdferajvq/q9rL39dHn61ON+tS9uUesXtoQC1rZm/a0t0aMjlV2QUlSomLMDVOWsdGcJbwCwQUwIMKut3St5neHdHbLJ5dty/frEtZvivH7qEBOlJaoQdmrtFfZ60z/aUGtIvVh3f1VVxUMGcHJ0RAATxM3xYN9PnofmrbOFIHDpeabcjWX62AXawdZle/+oM+WbXPtG+wqiK/dnN3RQZT4wS/joACeKCE+qH6+O6+ptiVVfTK+qv1r7PWqrSc8vioW1+vz9CVLy3SpowCNQwP0rQ/9jZVka0ZP+C3EFAADxUa6K+X/9BVDw1sI+u9YNrSPRr27yXm2j/gbFavqAlfbdSd7640Zet7NquvOX86m8WwOGkEFMCDWX+ljrqgpd68tYcigvy1fNchXfnyIq1Oy7V7aPBgWQXFGvbvpXptwQ5zf+Q5yZo+so9iIllvgpNHQAG8wIUpsZo1qp+aNwzT/rxiXTtpsf7fN1vpiAyHsxZlXz5xkZbuzFF4kL9eHdZNfx3UTgF+vN3g1PATA3hRUTcrpFjrUqzeJy98s0VDXkvVLkrkw0FbiP/93x0a+voSZRWUqHVsuD4b3U+XdWzM+cVpIaAAXiQqJMCsS3nxhi6KCPbXj3tyddnE/+r9ZXuoPovTZjWrvPu9VXriy40m/F7VJV6fjuqnFo3COas4bT5VblgTOz8/X1FRUcrLy1NkZKTdwwHcklXE7YEPV2vJjuo6Kf3bxmjCNZ3UKCLI7qHBjXz5036N/2ydDhaWKsDPR+Mvb6eb+jRllw7O+P2bgAJ4scrKKr25aKee/XqzKZ7VICxQT13bSRe3i7V7aHBx1m4wK5h8tS7D3G8TG6HnhnRWx4Qou4cGF0ZAAXBKNu7P19gPVptaFZahPRNNy/uwIH/OJI5jTbpbrRT+Pnu9covKTGuFey5oqdEXtFSgP6sG8NsIKABOWXFZhZ6ft0Vv/HeHrAu/TRuE6vnru5hOyYAlK79Yf5m1Tt9szDT32zWO1LNDOql9PLMmODkEFACnLXX7QbM2JT2vWL4+0j3nt9ToC1sqOMCPs+rFsyYfr9qn/5u9XvnF5WatyZ8ubKW7zm/B9mGcEgIKgDOSd6RMf/98vWb9uM/cT4wO0d8GtTMN3ihR7l325x3RuE/W6vvN2eZ+p4QoPXtdZ7WJowMxTh0BBYBDzFm7X/83e4My8ovN/bNbNtRjV7RTq1jenLxh1mTG8jQ9+eVGU6reWl8ytn9rUxXWn6JrOE0EFAAOU1hSrknfb9frC3eYnT5WN9pb+zbTff1bmboq8Dwrd+foyTmbtHL3IXO/a1I9PXtdJ7WMIZjizBBQADjc7oOFphDXvA3VCyStLclWI8IhPRJNaIH725F9WM/M3ay566u3DgcH+OrBAW00vF8y32M4BAEFgNMs3JKtf8xer+3ZheZ+xyZR+vuV7dS9aTRn3Y1rmkycv1XTl+0xlWCtvHl9j0SNvbi1YmnwBwcioABwqrKKSk1N3a0X520x6xMsV3dtokcuTeENzY0UlZbr3//dqdcWbFdhaYV57KKUGP350hS1Zp0RnICAAqBOHDhcomfnbtaHK9NM7ZTQQD+NPKe5bjurmeqHBfJdcFHlFZWauXKvXpi3xTT2s3ROiNK4y9qqT/MGdg8PHiyfUvcA6tJPe3PNtuRVe3LNfSuo/KFXkv54TrIaR4XwzXChnTnzN2bpqbmbtC3rsHksKTpUD1/SRoM6NmYLOZyOgALAlje/OWsz9Mp327Rhf755zCrodU3XBN15XnM1p7Otbax1Jd9uytIbC3do2a7q5pD1QwN074WtNKxPkoL8KcKHukFAAWBrUFmwJdtsTV66s/rN0MdHurRDnO4+ryXN5OpQQXGZPlyxV1MW79KenCLzWJC/r24/O1l3ndeCbeKocwQUAC7BqqMx6ftt+mZjVu1j57RqqLvPb6G+zRtwScFJdh4oNKFk5oq02sWvkcH+5rLbbf2acdkNtiGgAHApmzLy9dqCHfp8Tbq53GDpkljP/BXfv20MlUkdNHO1aNsBvf3DLn23OcssWra0jAk3i5av6dZEoYF0p4a9CCgAXFJaTpGpSPvhijSVlFfWFny7onO8BndtYnaS0Ovn1BwprdAnP+7VOz/s0tajC18tF7RpZAqsWTNWnFO4CgIKAJcvDPb2Dzv1wfI0HSwsrX08uWGYruoSr8FdmqhZwzBbx+jq24Stxa5z12Xos9XpprmjJSzQT9d1T9CtZzVjUTJcEgEFgNsUfLMuS3z64z59vT5DxWXVsyo1/V+soHJ5p8ZqEB4kb1dSXqEfth0wocRqN3CoqDqU1HSbtvojXd8zUZHB9EeC6yKgAHA7h0vK9Z/1Gfp0dboWbc3W0aUqpgfMua0amktA/dvGKizI36saNX6/Odv0xvluU5Y5RzWsbcIXt4vVpR0a69zWjeiVA7dAQAHg1rIKijV7zX59tnqfftqbV/u4v6+POjSJUu/m0eqT3EDdm9X3uBmD3KJSU0zNCiVW36OatTqW2MggXdI+TgM7xKlXs2gWF8PtEFAAeAyr4qkVVKwdQLsPVtfyqGE1tWsXH6neyQ3UOzlavZKjVS800K0u22zcX2Aq8a5JyzMft2Ufrt2BY2naIFSXdIgzwaRzQj350jkaboyAAsBjdwFZxd+W7TxoPv48sFhS4iJMWOmZHK0WjcKVGB2qcBe4LGRtr7bC1pq0XK3Zm2tmhqzt12UVx6SRo9rERphQcmnHOHPMLhx4CgIKAK+QkVespUfDytIdB7U9u/CEr4sOCzRBxeo7kxQdYj4m1g81jzWOCnbIpRJru6+1Oyn7cHH1x5rb4RJtzyrUuvQ8FR0tmvbzsXVKiFKnhHpmm7X1sVEEi4LhmQgoALySFQiWHZ1hWZ2Wa8q7H7vb5USsdS1N6oeYoBDg6yt/Px8TWKzHrVuA39HHfI8+5ucjXx8f5RSVmq934GgQKThmAeuvsbYBW2toOifWM6HEumSTUD+EGRJ4jXy6GQPA0V+IxWXm0lBazhHzcc/Rm3W899ARlVb8bxHqmbL63MREBqlReJCZBTG38GATQqxAYjVMtHYlAd4q/xQCiv0XZgHAiaxdPu3jo8zt5yorq5RZUGzWsuQfKVN5ZZWpzVJeUaXyykqzPqSi5rHKKlMgzXqssqpK9UMD/xdCjt4igvyZDQEchIACwGtZO2IaR4XQPA9wQWe+MgwAAMDBCCgAAMDlEFAAAIDLIaAAAACXQ0ABAAAuh4ACAABcjq0B5ZVXXlGzZs0UHBys3r17a9myZXYOBwAAeHtA+eCDD3T//ffrscce06pVq9S5c2cNHDhQWVlZdg0JAAB4e0B5/vnnNXLkSA0fPlzt2rXT5MmTFRoaqrfeesuuIQEAAG8OKKWlpVq5cqX69+//v4H4+pr7qampv3h9SUmJqd9/7A0AAHguWwLKgQMHVFFRodjY2OMet+5nZGT84vUTJkwwzYVqbomJiXU4WgAAUNfcYhfPuHHjTOfDmltaWprdQwIAAJ7WLLBhw4by8/NTZmbmcY9b9+Pi4n7x+qCgIHMDAADewZaAEhgYqO7du2v+/PkaPHiweayystLcHz169O/++6qqKvORtSgAALiPmvftmvdxlwsoFmuL8a233qoePXqoV69eevHFF1VYWGh29fyegoIC85G1KAAAuB/rfdxaU+qSAeWGG25Qdna2xo8fbxbGdunSRXPnzv3FwtkTiY+PN+tQIiIi5OPjUyfjdYdUagU267xERkbaPRyPx/nmnHs6fsY5585gzZxY4cR6H/89PlUnM88Ct/hlYqVRaxExAYXz7Yn4Ged8ezp+xt1wFw8AAPAuBBQAAOByCCgewtqGbfU1Yjs259tT8TPO+fZ0/IwfjzUoAADA5TCDAgAAXA4BBQAAuBwCCgAAcDkEFAAA4HIIKB6spKTEVOi1qu2uXr3a7uF4rF27dmnEiBFKTk5WSEiIWrRoYXZUlZaW2j00j/HKK6+oWbNmCg4OVu/evbVs2TK7h+SxJkyYoJ49e5pK3TExMaZf2ubNm+0eltd46qmnzO/sMWPGyNsRUDzYww8/fFLlhHFmNm3aZJpdvvbaa1q/fr1eeOEFTZ48WX/5y184tQ7wwQcfmN5dVuhbtWqVOnfurIEDByorK4vz6wQLFizQqFGjtGTJEs2bN09lZWUaMGCA6ZUG51q+fLn5PdKpUydOtcUqdQ/PM2fOnKqUlJSq9evXW60Mqn788Ue7h+RVnnnmmark5GS7h+ERevXqVTVq1Kja+xUVFVXx8fFVEyZMsHVc3iIrK8v8DlmwYIHdQ/FoBQUFVa1ataqaN29e1XnnnVd13333VXk7ZlA8UGZmpkaOHKl3331XoaGhdg/HK1k9kaKjo+0ehtuzLpOtXLlS/fv3r33M19fX3E9NTbV1bN70s2zh59m5rFmrQYMGHfez7u1s62YM57B6P952222666671KNHD7M+AnVr27Zteumll/Tcc89x6s/QgQMHVFFR8Ysu59Z969IanMu6dGmthejXr586dOjA6XaSGTNmmMuX1iUe/A8zKG7ikUceMQunfutm/cK23hitVtbjxo2ze8hec86PtW/fPl1yySUaMmSImcUC3P2v+nXr1pk3UDhHWlqa7rvvPk2bNs0sAsf/UOreTWRnZ+vgwYO/+ZrmzZvr+uuv1+zZs82bZw3rL1A/Pz8NGzZMU6ZMqYPRetc5DwwMNMfp6ek6//zz1adPH73zzjvmUgTO/BKPdZnyo48+MrtJatx6663Kzc3VZ599xil2ktGjR5vzu3DhQrNDDc7x6aef6uqrrza/o4/9ne3j42N+h1i7MY99zpsQUDzMnj17lJ+fX3vfetO0djxYv+Ct7ZkJCQm2js9TWTMnF1xwgbp376733nvPa3+hOIP1c9urVy8zO1hz2SEpKcm8gVqzXHD8ZeJ7771Xs2bN0vfff69WrVpxip3ImvHevXv3cY8NHz5cKSkp+vOf/+zVl9ZYg+JhrF/cxwoPDzcfrdochBPnhRNr5qRp06Zm3Yk181IjLi7OSV/Ve1hbjK0ZE2tNlRVUXnzxRbPl1folDudc1pk+fbqZPbFqoWRkZJjHo6KiTJ0fOJZ1jn8eQsLCwtSgQQOvDicWAgpwhqxaEdbCWOv28xBo/TWKM3PDDTeY0Dd+/HjzZmkVH5w7d+4vFs7CMSZNmmQ+WqH7WG+//bZZgA/UFS7xAAAAl8MqPgAA4HIIKAAAwOUQUAAAgMshoAAAAJdDQAEAAC6HgAIAAFwOAQUAALgcAgoAAHA5BBQAAOByCCgAAMDlEFAAAIDLIaAAAAC5mv8PpiKQomVHzY0AAAAASUVORK5CYII=",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"xs = mx.arange(-5, 5, 0.25)\n",
|
|
"ys = f(xs)\n",
|
|
"\n",
|
|
"plt.plot(xs, ys)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3a6e6a0c",
|
|
"metadata": {},
|
|
"source": [
|
|
"**Simple refresher on differentiation**\n",
|
|
"$$\n",
|
|
"L = \\lim_{h \\rightarrow 0}\\frac{f(x + h) - f(x)}{h}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "152355b0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"h = 0.0001\n",
|
|
"x = 3.0"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "cbb72ce5",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"(20.0, 20.001400030000006)"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"f(x), f(x + h)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "1d17c49f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"14.000300000063248"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"(f(x + h) - f(x)) / h"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d0ec3cf4",
|
|
"metadata": {},
|
|
"source": [
|
|
"### **micrograd implementation**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "02c2f4eb",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Value:\n",
|
|
" def __init__(self, data, _parents=(), _op=''):\n",
|
|
" self.data = data\n",
|
|
" self._parents = _parents\n",
|
|
" self._op = _op\n",
|
|
"\n",
|
|
" # gradient\n",
|
|
" self.grad = 0.0 # at init, the value does not affect the output\n",
|
|
" self._backward = lambda: None\n",
|
|
" \n",
|
|
" def __repr__(self):\n",
|
|
" return f\"Value(data={self.data})\"\n",
|
|
" \n",
|
|
" def __add__(self, other: 'Value') -> 'Value':\n",
|
|
" other = other if isinstance(other, Value) else Value(other)\n",
|
|
" out = Value(self.data + other.data, (self, other), '+')\n",
|
|
"\n",
|
|
" def _backward():\n",
|
|
" self.grad += 1.0 * out.grad\n",
|
|
" other.grad += 1.0 * out.grad\n",
|
|
" out._backward = _backward\n",
|
|
"\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def __radd__(self, other: 'Value') -> 'Value':\n",
|
|
" return self + other\n",
|
|
" \n",
|
|
" def __mul__(self, other: 'Value') -> 'Value':\n",
|
|
" other = other if isinstance(other, Value) else Value(other)\n",
|
|
" out = Value(self.data * other.data, (self, other), '*')\n",
|
|
"\n",
|
|
" def _backward():\n",
|
|
" self.grad += other.data * out.grad\n",
|
|
" other.grad += self.data * out.grad\n",
|
|
" out._backward = _backward\n",
|
|
"\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def __neg__(self) -> 'Value':\n",
|
|
" return -1 * self\n",
|
|
" \n",
|
|
" def __sub__(self, other: 'Value') -> 'Value':\n",
|
|
" return self + (-other)\n",
|
|
" \n",
|
|
" def __rsub__(self, other: 'Value') -> 'Value':\n",
|
|
" return Value(other) - self\n",
|
|
" \n",
|
|
" def __rmul__(self, other: 'Value') -> 'Value':\n",
|
|
" return self * other\n",
|
|
" \n",
|
|
" def __pow__(self, other: 'Value') -> 'Value':\n",
|
|
" assert isinstance(other, (int, float)), \"only support int/float powers for now\"\n",
|
|
" out = Value(self.data**other, (self, ), f'**{other}')\n",
|
|
"\n",
|
|
" def _backward():\n",
|
|
" self.grad += (other * self.data**(other - 1)) * out.grad\n",
|
|
" out._backward = _backward\n",
|
|
"\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def __truediv__(self, other: 'Value') -> 'Value':\n",
|
|
" return self * other**-1\n",
|
|
" \n",
|
|
" def tanh(self) -> 'Value':\n",
|
|
" x = self.data\n",
|
|
" _tanh = (math.exp(2*x) - 1) / (math.exp(2*x) + 1)\n",
|
|
" out = Value(_tanh, (self, ), 'tanh')\n",
|
|
"\n",
|
|
" def _backward():\n",
|
|
" self.grad += (1 - _tanh ** 2) * out.grad\n",
|
|
" out._backward = _backward\n",
|
|
"\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def exp(self) -> 'Value':\n",
|
|
" x = self.data\n",
|
|
" out = Value(math.exp(x), (self, ), 'exp')\n",
|
|
"\n",
|
|
" def _backward():\n",
|
|
" self.grad += out.data * out.grad\n",
|
|
" out._backward = _backward\n",
|
|
"\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def backward(self):\n",
|
|
" topo = []\n",
|
|
" visited = set()\n",
|
|
"\n",
|
|
" def build_topo(v: 'Value'):\n",
|
|
" if v not in visited:\n",
|
|
" visited.add(v)\n",
|
|
" \n",
|
|
" for child in v._parents:\n",
|
|
" build_topo(child)\n",
|
|
" \n",
|
|
" topo.append(v)\n",
|
|
" \n",
|
|
" build_topo(self)\n",
|
|
"\n",
|
|
" self.grad = 1.0\n",
|
|
" for node in reversed(topo):\n",
|
|
" node._backward()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "015176d1",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Gradient: -3.000000000010772\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# manual backprop\n",
|
|
"a = Value(2.0)\n",
|
|
"b = Value(-3.0)\n",
|
|
"c = Value(10.0)\n",
|
|
"d = a*b + c\n",
|
|
"\n",
|
|
"# If we change 'a' by a small amount 'h'\n",
|
|
"# How would the gradient change?\n",
|
|
"a = Value(a.data + h)\n",
|
|
"d_ = a*b + c\n",
|
|
"print(f\"Gradient: {(d_.data - d.data)/h}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "42a72d0d",
|
|
"metadata": {},
|
|
"source": [
|
|
"**autograd example**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "f7f36924",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"x1 = Value(2.0)\n",
|
|
"x2 = Value(0.0)\n",
|
|
"\n",
|
|
"w1 = Value(-3.0)\n",
|
|
"w2 = Value(1.0)\n",
|
|
"\n",
|
|
"b = Value(6.8813735870195432)\n",
|
|
"\n",
|
|
"x1w1 = x1*w1\n",
|
|
"x2w2 = x2*w2\n",
|
|
"x1w1x2w2 = x1w1 + x2w2\n",
|
|
"n = x1w1x2w2 + b\n",
|
|
"o = n.tanh()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "32fe0b65",
|
|
"metadata": {},
|
|
"source": [
|
|
"### **Neural Network, using micrograd**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "5f6e988b",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import random\n",
|
|
"\n",
|
|
"class Neuron:\n",
|
|
" def __init__(self, n_inputs: int):\n",
|
|
" self.w = [Value(random.uniform(-1, 1)) for _ in range(n_inputs)]\n",
|
|
" self.b = Value(random.uniform(-1, 1))\n",
|
|
" \n",
|
|
" def __call__(self, x: list) -> Value:\n",
|
|
" activations = sum((w_i * x_i for w_i, x_i in zip(self.w, x)), self.b)\n",
|
|
" out = activations.tanh()\n",
|
|
" return out\n",
|
|
" \n",
|
|
" def parameters(self):\n",
|
|
" return self.w + [self.b]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "a36f7621",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Layer:\n",
|
|
" def __init__(self, n_inputs: int, n_outputs: int):\n",
|
|
" self.neurons = [Neuron(n_inputs) for _ in range(n_outputs)]\n",
|
|
" \n",
|
|
" def __call__(self, x: list) -> list[Value]:\n",
|
|
" outs = [n(x) for n in self.neurons]\n",
|
|
" return outs\n",
|
|
" \n",
|
|
" def parameters(self):\n",
|
|
" return [p for n in self.neurons for p in n.parameters()]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "66b1d988",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class MLP:\n",
|
|
" def __init__(self, n_inputs: int, n_outputs: int):\n",
|
|
" sz = [n_inputs] + n_outputs\n",
|
|
" self.layers = [Layer(sz[i], sz[i + 1]) for i in range(len(n_outputs))]\n",
|
|
" \n",
|
|
" def __call__(self, x):\n",
|
|
" for layer in self.layers:\n",
|
|
" x = layer(x)\n",
|
|
" return x\n",
|
|
" \n",
|
|
" def parameters(self):\n",
|
|
" return [p for layer in self.layers for p in layer.parameters()]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "0971f41c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"Value(data=-0.9996003578010221)"
|
|
]
|
|
},
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# single neuron example\n",
|
|
"x = [2.5, 3.5]\n",
|
|
"n = Neuron(len(x))\n",
|
|
"n(x)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "7fc58556",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[Value(data=-0.9925180315426618),\n",
|
|
" Value(data=-0.9334684394208494),\n",
|
|
" Value(data=0.9998730786341865)]"
|
|
]
|
|
},
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# layer of neurons example\n",
|
|
"x = [1.5, 4.5]\n",
|
|
"nn = Layer(2, 3)\n",
|
|
"nn(x)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "210eb775",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[Value(data=0.24818297035972064)]"
|
|
]
|
|
},
|
|
"execution_count": 16,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"# MLP example: input with 3 neurons, first layers with 4 neurons, second layer with 4 neurons, last output layer with 1 neuron\n",
|
|
"x = [2.0, 3.0, -1.0]\n",
|
|
"nn = MLP(3, [4, 4, 1])\n",
|
|
"nn(x)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "72283b5e",
|
|
"metadata": {},
|
|
"source": [
|
|
"### **Tune weights of our neural net**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 72,
|
|
"id": "777ec5b8",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"nn = MLP(3, [4, 4, 1])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 73,
|
|
"id": "0e7c0d95",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"xs = [\n",
|
|
" [2.0, 3.0, -1.0],\n",
|
|
" [3.0, -1.0, 0.5],\n",
|
|
" [0.5, 1.0, 1.0],\n",
|
|
" [1.0, 1.0, -1.0]\n",
|
|
"]\n",
|
|
"ys = [1.0, -1.0, -1.0, 1.0]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 74,
|
|
"id": "82948286",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"0 6.396213210433728\n",
|
|
"1 4.6713236053103415\n",
|
|
"2 1.7838028388745268\n",
|
|
"3 0.9951214940339372\n",
|
|
"4 0.6244643866397004\n",
|
|
"5 0.4316410423669388\n",
|
|
"6 0.31926395466439167\n",
|
|
"7 0.24840306807651757\n",
|
|
"8 0.2007423843155306\n",
|
|
"9 0.1669919570672581\n",
|
|
"10 0.14208896965193704\n",
|
|
"11 0.12309433355505289\n",
|
|
"12 0.10820765800755643\n",
|
|
"13 0.09627486514636871\n",
|
|
"14 0.08652715381843712\n",
|
|
"15 0.07843528216744697\n",
|
|
"16 0.07162448249002293\n",
|
|
"17 0.06582279271389418\n",
|
|
"18 0.06082858473998165\n",
|
|
"19 0.05648953163226797\n",
|
|
"20 0.05268861520812526\n",
|
|
"21 0.04933459347816223\n",
|
|
"22 0.046355366905371904\n",
|
|
"23 0.04369327289796304\n",
|
|
"24 0.04130168994597353\n",
|
|
"25 0.03914254822288492\n",
|
|
"26 0.037184478469955776\n",
|
|
"27 0.03540141743451709\n",
|
|
"28 0.033771544606773694\n",
|
|
"29 0.03227646256553751\n",
|
|
"30 0.030900558653375945\n",
|
|
"31 0.029630503156717405\n",
|
|
"32 0.02845485132950758\n",
|
|
"33 0.027363725187645082\n",
|
|
"34 0.026348557141398016\n",
|
|
"35 0.025401881973255196\n",
|
|
"36 0.024517166914483014\n",
|
|
"37 0.023688671970382196\n",
|
|
"38 0.022911334430816083\n",
|
|
"39 0.022180672846249565\n",
|
|
"40 0.0214927067685973\n",
|
|
"41 0.02084388933519709\n",
|
|
"42 0.020231050374206097\n",
|
|
"43 0.019651348175094643\n",
|
|
"44 0.019102228431309494\n",
|
|
"45 0.01858138914775967\n",
|
|
"46 0.018086750531565127\n",
|
|
"47 0.017616429064053273\n",
|
|
"48 0.017168715095545756\n",
|
|
"49 0.016742053419866516\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Training loop\n",
|
|
"lr = 0.05\n",
|
|
"epochs = 50\n",
|
|
"for epoch in range(epochs):\n",
|
|
" # forward pass\n",
|
|
" y_preds = [nn(x) for x in xs]\n",
|
|
" loss = sum((y_pred[0] - y_true)**2 for y_true, y_pred in zip(ys, y_preds))\n",
|
|
"\n",
|
|
" # backward pass\n",
|
|
" for p in nn.parameters(): # zero grad\n",
|
|
" p.grad = 0.0\n",
|
|
" loss.backward()\n",
|
|
"\n",
|
|
" # update\n",
|
|
" for p in nn.parameters():\n",
|
|
" p.data += -lr * p.grad\n",
|
|
" \n",
|
|
" print(epoch, loss.data)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 75,
|
|
"id": "cc4aea5b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[[Value(data=0.9511549278309712)],\n",
|
|
" [Value(data=-0.9459667617553708)],\n",
|
|
" [Value(data=-0.9166788866728437)],\n",
|
|
" [Value(data=0.9329611039515775)]]"
|
|
]
|
|
},
|
|
"execution_count": 75,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"y_preds"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "7043ca91",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "karpathy-micrograd",
|
|
"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.12.11"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|