Skip to content

Instantly share code, notes, and snippets.

@ogrisel
Last active October 28, 2022 13:54
Show Gist options
  • Save ogrisel/eb3073db349cdf6445f9631f4fc416d5 to your computer and use it in GitHub Desktop.
Save ogrisel/eb3073db349cdf6445f9631f4fc416d5 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"numpy's RNG cannot directly generate uniform values in the uint64 domain but it's not complicated to reinterpret the random bytes via `np.frombuffer`:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"int64_values = np.random.default_rng().integers(\n",
" np.iinfo(np.int64).min, np.iinfo(np.int64).max, size=int(1e7)\n",
")\n",
"uint64_values = np.frombuffer(int64_values.data, dtype=np.uint64)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Visually check that we actually have uniform data in the uint64 domain:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjoAAAGwCAYAAACgi8/jAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA0rElEQVR4nO3df1SU553//9eEHyNSmPJDfswJJW6rRIpJU+wCmjQaFbSiscmpdtnOStdiWhM9rHJSTbdnzZ6tmmi03dq6Jk1jY03Idg1psxgKPYmmrKKRla1EY5NWIzYgRodB+ZiBkvv7R77exxEUhh+RufJ8nHOfw9z3+77u65qZe+bFxdyDw7IsSwAAAAa66UZ3AAAAYLgQdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAY4Xf6A7cSB9++KHee+89xcTEyOFw3OjuAACAfrAsSxcuXJDb7dZNN/UxZ2MF4ac//ak1ceJEKyYmxoqJibFyc3Ot3bt329sXLVpkSQpYcnJyAtr44IMPrIceeshKSEiwRo8ebc2dO9dqamoKqDl//rz1jW98w4qNjbViY2Otb3zjG5bX6w2oeffdd63CwkJr9OjRVkJCgrVs2TLL7/cHMxyrqampR39ZWFhYWFhYQmO5Oj/0JqgZnZtvvlnr16/X5z73OUnSL37xC9177706fPiwPv/5z0uSZs2apWeeecbeJzIyMqCN0tJSvfzyyyovL1dCQoJWrlypwsJC1dfXKywsTJJUVFSk06dPq6qqSpK0ZMkSeTwevfzyy5Kk7u5uzZkzR2PGjFFtba3OnTunRYsWybIs/fjHP+73eGJiYiRJTU1Nio2NDeauAAAAN0h7e7vS0tLs9/HrCmoKpBdxcXHWz372M8uyPprRuffee69Z29bWZkVERFjl5eX2ur/85S/WTTfdZFVVVVmWZVlHjx61JFl1dXV2zf79+y1J1ltvvWVZlmXt3r3buummm6y//OUvds3zzz9vOZ1Oy+fz9bvvPp/PkhTUPgAA4MYK5v17wB9G7u7uVnl5uTo6OpSXl2ev37Nnj5KSkjR+/HiVlJSotbXV3lZfX6+uri7l5+fb69xut7KysrRv3z5J0v79++VyuZSTk2PX5ObmyuVyBdRkZWXJ7XbbNQUFBfL7/aqvr79mn/1+v9rb2wMWAABgrqCDzpEjR/SpT31KTqdT3/72t1VRUaHMzExJ0uzZs7Vz5069+uqreuKJJ/TGG2/onnvukd/vlyS1tLQoMjJScXFxAW0mJyerpaXFrklKSupx3KSkpICa5OTkgO1xcXGKjIy0a3qzbt06uVwue0lLSwt2+AAAIIQEfdVVRkaGGhoa1NbWpl27dmnRokXau3evMjMztXDhQrsuKytLkyZNUnp6uiorK3Xfffdds03LsgKueurtCqiB1Fxt9erVWrFihX378t/4AACAmYKe0YmMjNTnPvc5TZo0SevWrdPtt9+uH/3oR73WpqamKj09XW+//bYkKSUlRZ2dnfJ6vQF1ra2t9gxNSkqKzpw506Ots2fPBtRcPXPj9XrV1dXVY6bnSk6nU7GxsQELAAAw16C/MNCyLPtPU1c7d+6cmpqalJqaKknKzs5WRESEampq7Jrm5mY1NjZq8uTJkqS8vDz5fD4dPHjQrjlw4IB8Pl9ATWNjo5qbm+2a6upqOZ1OZWdnD3ZIAADAEA7Lsqz+Fj/yyCOaPXu20tLSdOHCBZWXl2v9+vWqqqpSXl6e1qxZo/vvv1+pqak6efKkHnnkEZ06dUrHjh2zLwH7zne+o//+7//W9u3bFR8fr7KyMp07dy7g8vLZs2frvffe07Zt2yR9dHl5enp6wOXlX/jCF5ScnKwNGzbo/PnzKi4u1vz584O6vLy9vV0ul0s+n4/ZHQAAQkQw799BfUbnzJkz8ng8am5ulsvl0m233aaqqirNnDlTly5d0pEjR/Tss8+qra1NqampmjZtml544YWA69w3b96s8PBwLViwQJcuXdL06dO1fft2O+RI0s6dO7V8+XL76qx58+Zpy5Yt9vawsDBVVlZq6dKlmjJliqKiolRUVKSNGzcGMxwAAGC4oGZ0TMOMDgAAoSeY92/+qScAADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOiHollWVumVV5Y3uBgBcF69VGAkIOp8wH9cLz416cbvWcXmxxVC58hy61s/DffxQROj5yHDcD9y310fQGUbXe+Ld6CdmqJ4UN/p+u1E+iWMeiQbyOFx+zg5m36EyEp5HH1cY7Os4fYXUYPs5Eu5b9I6g8zHq6+Tr7UQL5mTta/1QvtBeq/3BHHcwL+rB3FdX1g3VDNBwvMgN5g3y6naCOV5f64bymEPVzkDvq/78MjJSngvDsW8w4+tP3cf9Zv9xzqLd6Bm74XjdGg43YpazL0H9rysMXDABp7ftJ9fPGeou9Xmsq0+soerD5Xb7215fJ/Vg+3Vl+721dfXxr6wJ5sX/6rb7Wt/ffl7ZTl/3ydWPb3/Ge72+DOQxvFbf+9NGf+t6a7O3++la9UMVLofyvB3q8+B67fX1HBnMcfvbt2CeJ9d7Lvf2ejZS9PXac3VNMNuv13Z/X9P685r1cb4/DRRB5wboT+gZyIvWcD/ZhvuFbajqr/WGNpjjBaM/j8W1Zr2G6zEc6Ivlldv7CkXXu7/7Otb12hnqUDISfhu+1rGCDY79fcMJ9vEN5r641nM5mONfr53BBrjBPD8GM4vV2/qB/pI00L5dWTvQwDfUQfpGIOiMUEP5RjicM0bB7tvfF8VgTsobdUIN9gW1P232VRfsb7jBHKOvY99ogw1vQ3n8/r7ZDcdsWV/tDMf6kRAKh+P864/BBsobZSj7NhJ+WQgGQWeYDfUTfyifSH391jyUfboR4xwJb+iDuV+DbT+YbcHUDLYvN/I5HmqG+/kyWEPVp4G2M9znULC/aCA0EHQMMtJOvpH82821DFU4GIrfjoL1cc5gYGS4UbMaGHojMeT2t08jpb/XQtAJYTfqzzoj/UmNkcGk54lJY4HZeK72xOXlAEIOL+bA0DH9fCLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxgoq6GzdulW33XabYmNjFRsbq7y8PL3yyiv2dsuytGbNGrndbkVFRWnq1Kl68803A9rw+/1atmyZEhMTFR0drXnz5un06dMBNV6vVx6PRy6XSy6XSx6PR21tbQE1p06d0ty5cxUdHa3ExEQtX75cnZ2dQQ4fAACYLKigc/PNN2v9+vU6dOiQDh06pHvuuUf33nuvHWYef/xxbdq0SVu2bNEbb7yhlJQUzZw5UxcuXLDbKC0tVUVFhcrLy1VbW6uLFy+qsLBQ3d3ddk1RUZEaGhpUVVWlqqoqNTQ0yOPx2Nu7u7s1Z84cdXR0qLa2VuXl5dq1a5dWrlw52PsDAAAYxGFZljWYBuLj47Vhwwb94z/+o9xut0pLS/Xd735X0kezN8nJyXrsscf0wAMPyOfzacyYMdqxY4cWLlwoSXrvvfeUlpam3bt3q6CgQMeOHVNmZqbq6uqUk5MjSaqrq1NeXp7eeustZWRk6JVXXlFhYaGamprkdrslSeXl5SouLlZra6tiY2P71ff29na5XC75fL5+7xOMW1ZVDnmbAACEkpPr5wx5m8G8fw/4Mzrd3d0qLy9XR0eH8vLydOLECbW0tCg/P9+ucTqduvvuu7Vv3z5JUn19vbq6ugJq3G63srKy7Jr9+/fL5XLZIUeScnNz5XK5AmqysrLskCNJBQUF8vv9qq+vH+iQAACAYcKD3eHIkSPKy8vTBx98oE996lOqqKhQZmamHUKSk5MD6pOTk/Xuu+9KklpaWhQZGam4uLgeNS0tLXZNUlJSj+MmJSUF1Fx9nLi4OEVGRto1vfH7/fL7/fbt9vb2/g4bAACEoKBndDIyMtTQ0KC6ujp95zvf0aJFi3T06FF7u8PhCKi3LKvHuqtdXdNb/UBqrrZu3Tr7A84ul0tpaWnX7RcAAAhtQQedyMhIfe5zn9OkSZO0bt063X777frRj36klJQUSeoxo9La2mrPvqSkpKizs1Ner/e6NWfOnOlx3LNnzwbUXH0cr9errq6uHjM9V1q9erV8Pp+9NDU1BTl6AAAQSgb9PTqWZcnv92vs2LFKSUlRTU2Nva2zs1N79+7V5MmTJUnZ2dmKiIgIqGlublZjY6Ndk5eXJ5/Pp4MHD9o1Bw4ckM/nC6hpbGxUc3OzXVNdXS2n06ns7Oxr9tXpdNqXxl9eAACAuYL6jM4jjzyi2bNnKy0tTRcuXFB5ebn27NmjqqoqORwOlZaWau3atRo3bpzGjRuntWvXavTo0SoqKpIkuVwuLV68WCtXrlRCQoLi4+NVVlamiRMnasaMGZKkCRMmaNasWSopKdG2bdskSUuWLFFhYaEyMjIkSfn5+crMzJTH49GGDRt0/vx5lZWVqaSkhPACAABsQQWdM2fOyOPxqLm5WS6XS7fddpuqqqo0c+ZMSdLDDz+sS5cuaenSpfJ6vcrJyVF1dbViYmLsNjZv3qzw8HAtWLBAly5d0vTp07V9+3aFhYXZNTt37tTy5cvtq7PmzZunLVu22NvDwsJUWVmppUuXasqUKYqKilJRUZE2btw4qDsDAACYZdDfoxPK+B4dAACGV8h+jw4AAMBIR9ABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjBVU0Fm3bp2+9KUvKSYmRklJSZo/f76OHz8eUFNcXCyHwxGw5ObmBtT4/X4tW7ZMiYmJio6O1rx583T69OmAGq/XK4/HI5fLJZfLJY/Ho7a2toCaU6dOae7cuYqOjlZiYqKWL1+uzs7OYIYEAAAMFlTQ2bt3rx588EHV1dWppqZGf/3rX5Wfn6+Ojo6AulmzZqm5udledu/eHbC9tLRUFRUVKi8vV21trS5evKjCwkJ1d3fbNUVFRWpoaFBVVZWqqqrU0NAgj8djb+/u7tacOXPU0dGh2tpalZeXa9euXVq5cuVA7gcAAGCg8GCKq6qqAm4/88wzSkpKUn19vb785S/b651Op1JSUnptw+fz6emnn9aOHTs0Y8YMSdIvf/lLpaWl6Xe/+50KCgp07NgxVVVVqa6uTjk5OZKkp556Snl5eTp+/LgyMjJUXV2to0ePqqmpSW63W5L0xBNPqLi4WD/4wQ8UGxsbzNAAAICBBvUZHZ/PJ0mKj48PWL9nzx4lJSVp/PjxKikpUWtrq72tvr5eXV1dys/Pt9e53W5lZWVp3759kqT9+/fL5XLZIUeScnNz5XK5AmqysrLskCNJBQUF8vv9qq+vH8ywAACAIYKa0bmSZVlasWKF7rzzTmVlZdnrZ8+era997WtKT0/XiRMn9P3vf1/33HOP6uvr5XQ61dLSosjISMXFxQW0l5ycrJaWFklSS0uLkpKSehwzKSkpoCY5OTlge1xcnCIjI+2aq/n9fvn9fvt2e3v7wAYPAABCwoCDzkMPPaQ//OEPqq2tDVi/cOFC++esrCxNmjRJ6enpqqys1H333XfN9izLksPhsG9f+fNgaq60bt06Pfroo9ceFAAAMMqA/nS1bNky/eY3v9Frr72mm2+++bq1qampSk9P19tvvy1JSklJUWdnp7xeb0Bda2urPUOTkpKiM2fO9Gjr7NmzATVXz9x4vV51dXX1mOm5bPXq1fL5fPbS1NTUvwEDAICQFFTQsSxLDz30kF588UW9+uqrGjt2bJ/7nDt3Tk1NTUpNTZUkZWdnKyIiQjU1NXZNc3OzGhsbNXnyZElSXl6efD6fDh48aNccOHBAPp8voKaxsVHNzc12TXV1tZxOp7Kzs3vti9PpVGxsbMACAADMFdSfrh588EE999xz+vWvf62YmBh7RsXlcikqKkoXL17UmjVrdP/99ys1NVUnT57UI488osTERH31q1+1axcvXqyVK1cqISFB8fHxKisr08SJE+2rsCZMmKBZs2appKRE27ZtkyQtWbJEhYWFysjIkCTl5+crMzNTHo9HGzZs0Pnz51VWVqaSkhICDAAAkBTkjM7WrVvl8/k0depUpaam2ssLL7wgSQoLC9ORI0d07733avz48Vq0aJHGjx+v/fv3KyYmxm5n8+bNmj9/vhYsWKApU6Zo9OjRevnllxUWFmbX7Ny5UxMnTlR+fr7y8/N12223aceOHfb2sLAwVVZWatSoUZoyZYoWLFig+fPna+PGjYO9TwAAgCEclmVZN7oTN0p7e7tcLpd8Pt+wzALdsqpyyNsEACCUnFw/Z8jbDOb9m/91BQAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIwVVNBZt26dvvSlLykmJkZJSUmaP3++jh8/HlBjWZbWrFkjt9utqKgoTZ06VW+++WZAjd/v17Jly5SYmKjo6GjNmzdPp0+fDqjxer3yeDxyuVxyuVzyeDxqa2sLqDl16pTmzp2r6OhoJSYmavny5ers7AxmSAAAwGBBBZ29e/fqwQcfVF1dnWpqavTXv/5V+fn56ujosGsef/xxbdq0SVu2bNEbb7yhlJQUzZw5UxcuXLBrSktLVVFRofLyctXW1urixYsqLCxUd3e3XVNUVKSGhgZVVVWpqqpKDQ0N8ng89vbu7m7NmTNHHR0dqq2tVXl5uXbt2qWVK1cO5v4AAAAGcViWZQ1057NnzyopKUl79+7Vl7/8ZVmWJbfbrdLSUn33u9+V9NHsTXJysh577DE98MAD8vl8GjNmjHbs2KGFCxdKkt577z2lpaVp9+7dKigo0LFjx5SZmam6ujrl5ORIkurq6pSXl6e33npLGRkZeuWVV1RYWKimpia53W5JUnl5uYqLi9Xa2qrY2Ng++9/e3i6XyyWfz9ev+mDdsqpyyNsEACCUnFw/Z8jbDOb9e1Cf0fH5fJKk+Ph4SdKJEyfU0tKi/Px8u8bpdOruu+/Wvn37JEn19fXq6uoKqHG73crKyrJr9u/fL5fLZYccScrNzZXL5QqoycrKskOOJBUUFMjv96u+vn4wwwIAAIYIH+iOlmVpxYoVuvPOO5WVlSVJamlpkSQlJycH1CYnJ+vdd9+1ayIjIxUXF9ej5vL+LS0tSkpK6nHMpKSkgJqrjxMXF6fIyEi75mp+v19+v9++3d7e3u/xAgCA0DPgGZ2HHnpIf/jDH/T888/32OZwOAJuW5bVY93Vrq7prX4gNVdat26d/eFml8ultLS06/YJAACEtgEFnWXLluk3v/mNXnvtNd188832+pSUFEnqMaPS2tpqz76kpKSos7NTXq/3ujVnzpzpcdyzZ88G1Fx9HK/Xq66urh4zPZetXr1aPp/PXpqamoIZNgAACDFBBR3LsvTQQw/pxRdf1KuvvqqxY8cGbB87dqxSUlJUU1Njr+vs7NTevXs1efJkSVJ2drYiIiICapqbm9XY2GjX5OXlyefz6eDBg3bNgQMH5PP5AmoaGxvV3Nxs11RXV8vpdCo7O7vX/judTsXGxgYsAADAXEF9RufBBx/Uc889p1//+teKiYmxZ1RcLpeioqLkcDhUWlqqtWvXaty4cRo3bpzWrl2r0aNHq6ioyK5dvHixVq5cqYSEBMXHx6usrEwTJ07UjBkzJEkTJkzQrFmzVFJSom3btkmSlixZosLCQmVkZEiS8vPzlZmZKY/How0bNuj8+fMqKytTSUkJAQYAAEgKMuhs3bpVkjR16tSA9c8884yKi4slSQ8//LAuXbqkpUuXyuv1KicnR9XV1YqJibHrN2/erPDwcC1YsECXLl3S9OnTtX37doWFhdk1O3fu1PLly+2rs+bNm6ctW7bY28PCwlRZWamlS5dqypQpioqKUlFRkTZu3BjUHQAAAMw1qO/RCXV8jw4AAMMrpL9HBwAAYCQj6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGCjrovP7665o7d67cbrccDodeeumlgO3FxcVyOBwBS25ubkCN3+/XsmXLlJiYqOjoaM2bN0+nT58OqPF6vfJ4PHK5XHK5XPJ4PGprawuoOXXqlObOnavo6GglJiZq+fLl6uzsDHZIAADAUEEHnY6ODt1+++3asmXLNWtmzZql5uZme9m9e3fA9tLSUlVUVKi8vFy1tbW6ePGiCgsL1d3dbdcUFRWpoaFBVVVVqqqqUkNDgzwej729u7tbc+bMUUdHh2pra1VeXq5du3Zp5cqVwQ4JAAAYKjzYHWbPnq3Zs2dft8bpdColJaXXbT6fT08//bR27NihGTNmSJJ++ctfKi0tTb/73e9UUFCgY8eOqaqqSnV1dcrJyZEkPfXUU8rLy9Px48eVkZGh6upqHT16VE1NTXK73ZKkJ554QsXFxfrBD36g2NjYYIcGAAAMMyyf0dmzZ4+SkpI0fvx4lZSUqLW11d5WX1+vrq4u5efn2+vcbreysrK0b98+SdL+/fvlcrnskCNJubm5crlcATVZWVl2yJGkgoIC+f1+1dfXD8ewAABAiAl6Rqcvs2fP1te+9jWlp6frxIkT+v73v6977rlH9fX1cjqdamlpUWRkpOLi4gL2S05OVktLiySppaVFSUlJPdpOSkoKqElOTg7YHhcXp8jISLvman6/X36/377d3t4+qLECAICRbciDzsKFC+2fs7KyNGnSJKWnp6uyslL33XffNfezLEsOh8O+feXPg6m50rp16/Too4/2axwAACD0Dfvl5ampqUpPT9fbb78tSUpJSVFnZ6e8Xm9AXWtrqz1Dk5KSojNnzvRo6+zZswE1V8/ceL1edXV19ZjpuWz16tXy+Xz20tTUNOjxAQCAkWvYg865c+fU1NSk1NRUSVJ2drYiIiJUU1Nj1zQ3N6uxsVGTJ0+WJOXl5cnn8+ngwYN2zYEDB+Tz+QJqGhsb1dzcbNdUV1fL6XQqOzu71744nU7FxsYGLAAAwFxB/+nq4sWLeuedd+zbJ06cUENDg+Lj4xUfH681a9bo/vvvV2pqqk6ePKlHHnlEiYmJ+upXvypJcrlcWrx4sVauXKmEhATFx8errKxMEydOtK/CmjBhgmbNmqWSkhJt27ZNkrRkyRIVFhYqIyNDkpSfn6/MzEx5PB5t2LBB58+fV1lZmUpKSggwAABA0gCCzqFDhzRt2jT79ooVKyRJixYt0tatW3XkyBE9++yzamtrU2pqqqZNm6YXXnhBMTEx9j6bN29WeHi4FixYoEuXLmn69Onavn27wsLC7JqdO3dq+fLl9tVZ8+bNC/junrCwMFVWVmrp0qWaMmWKoqKiVFRUpI0bNwZ/LwAAACM5LMuybnQnbpT29na5XC75fL5hmQW6ZVXlkLcJAEAoObl+zpC3Gcz7N//rCgAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABgr6KDz+uuva+7cuXK73XI4HHrppZcCtluWpTVr1sjtdisqKkpTp07Vm2++GVDj9/u1bNkyJSYmKjo6WvPmzdPp06cDarxerzwej1wul1wulzwej9ra2gJqTp06pblz5yo6OlqJiYlavny5Ojs7gx0SAAAwVNBBp6OjQ7fffru2bNnS6/bHH39cmzZt0pYtW/TGG28oJSVFM2fO1IULF+ya0tJSVVRUqLy8XLW1tbp48aIKCwvV3d1t1xQVFamhoUFVVVWqqqpSQ0ODPB6Pvb27u1tz5sxRR0eHamtrVV5erl27dmnlypXBDgkAABjKYVmWNeCdHQ5VVFRo/vz5kj6azXG73SotLdV3v/tdSR/N3iQnJ+uxxx7TAw88IJ/PpzFjxmjHjh1auHChJOm9995TWlqadu/erYKCAh07dkyZmZmqq6tTTk6OJKmurk55eXl66623lJGRoVdeeUWFhYVqamqS2+2WJJWXl6u4uFitra2KjY3ts//t7e1yuVzy+Xz9qg/WLasqh7xNAABCycn1c4a8zWDev4f0MzonTpxQS0uL8vPz7XVOp1N333239u3bJ0mqr69XV1dXQI3b7VZWVpZds3//frlcLjvkSFJubq5cLldATVZWlh1yJKmgoEB+v1/19fVDOSwAABCiwoeysZaWFklScnJywPrk5GS9++67dk1kZKTi4uJ61Fzev6WlRUlJST3aT0pKCqi5+jhxcXGKjIy0a67m9/vl9/vt2+3t7cEMDwAAhJhhuerK4XAE3LYsq8e6q11d01v9QGqutG7dOvvDzS6XS2lpadftEwAACG1DGnRSUlIkqceMSmtrqz37kpKSos7OTnm93uvWnDlzpkf7Z8+eDai5+jher1ddXV09ZnouW716tXw+n700NTUNYJQAACBUDGnQGTt2rFJSUlRTU2Ov6+zs1N69ezV58mRJUnZ2tiIiIgJqmpub1djYaNfk5eXJ5/Pp4MGDds2BAwfk8/kCahobG9Xc3GzXVFdXy+l0Kjs7u9f+OZ1OxcbGBiwAAMBcQX9G5+LFi3rnnXfs2ydOnFBDQ4Pi4+P1mc98RqWlpVq7dq3GjRuncePGae3atRo9erSKiookSS6XS4sXL9bKlSuVkJCg+Ph4lZWVaeLEiZoxY4YkacKECZo1a5ZKSkq0bds2SdKSJUtUWFiojIwMSVJ+fr4yMzPl8Xi0YcMGnT9/XmVlZSopKSHAAAAASQMIOocOHdK0adPs2ytWrJAkLVq0SNu3b9fDDz+sS5cuaenSpfJ6vcrJyVF1dbViYmLsfTZv3qzw8HAtWLBAly5d0vTp07V9+3aFhYXZNTt37tTy5cvtq7PmzZsX8N09YWFhqqys1NKlSzVlyhRFRUWpqKhIGzduDP5eAAAARhrU9+iEOr5HBwCA4WXU9+gAAACMJAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYKwhDzpr1qyRw+EIWFJSUuztlmVpzZo1crvdioqK0tSpU/Xmm28GtOH3+7Vs2TIlJiYqOjpa8+bN0+nTpwNqvF6vPB6PXC6XXC6XPB6P2trahno4AAAghA3LjM7nP/95NTc328uRI0fsbY8//rg2bdqkLVu26I033lBKSopmzpypCxcu2DWlpaWqqKhQeXm5amtrdfHiRRUWFqq7u9uuKSoqUkNDg6qqqlRVVaWGhgZ5PJ7hGA4AAAhR4cPSaHh4wCzOZZZl6Yc//KG+973v6b777pMk/eIXv1BycrKee+45PfDAA/L5fHr66ae1Y8cOzZgxQ5L0y1/+Umlpafrd736ngoICHTt2TFVVVaqrq1NOTo4k6amnnlJeXp6OHz+ujIyM4RgWAAAIMcMyo/P222/L7XZr7Nix+vrXv64///nPkqQTJ06opaVF+fn5dq3T6dTdd9+tffv2SZLq6+vV1dUVUON2u5WVlWXX7N+/Xy6Xyw45kpSbmyuXy2XX9Mbv96u9vT1gAQAA5hryoJOTk6Nnn31Wv/3tb/XUU0+ppaVFkydP1rlz59TS0iJJSk5ODtgnOTnZ3tbS0qLIyEjFxcVdtyYpKanHsZOSkuya3qxbt87+TI/L5VJaWtqgxgoAAEa2IQ86s2fP1v3336+JEydqxowZqqyslPTRn6guczgcAftYltVj3dWurumtvq92Vq9eLZ/PZy9NTU39GhMAAAhNw355eXR0tCZOnKi3337b/tzO1bMura2t9ixPSkqKOjs75fV6r1tz5syZHsc6e/Zsj9miKzmdTsXGxgYsAADAXMMedPx+v44dO6bU1FSNHTtWKSkpqqmpsbd3dnZq7969mjx5siQpOztbERERATXNzc1qbGy0a/Ly8uTz+XTw4EG75sCBA/L5fHYNAADAkF91VVZWprlz5+ozn/mMWltb9W//9m9qb2/XokWL5HA4VFpaqrVr12rcuHEaN26c1q5dq9GjR6uoqEiS5HK5tHjxYq1cuVIJCQmKj49XWVmZ/acwSZowYYJmzZqlkpISbdu2TZK0ZMkSFRYWcsUVAACwDXnQOX36tP7u7/5O77//vsaMGaPc3FzV1dUpPT1dkvTwww/r0qVLWrp0qbxer3JyclRdXa2YmBi7jc2bNys8PFwLFizQpUuXNH36dG3fvl1hYWF2zc6dO7V8+XL76qx58+Zpy5YtQz0cAAAQwhyWZVk3uhM3Snt7u1wul3w+37B8XueWVZVD3iYAAKHk5Po5Q95mMO/f/K8rAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgLIIOAAAwFkEHAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYKyQDzo//elPNXbsWI0aNUrZ2dn6/e9/f6O7BAAARoiQDjovvPCCSktL9b3vfU+HDx/WXXfdpdmzZ+vUqVM3umsAAGAECOmgs2nTJi1evFjf+ta3NGHCBP3whz9UWlqatm7deqO7BgAARoDwG92Bgers7FR9fb1WrVoVsD4/P1/79u3rdR+/3y+/32/f9vl8kqT29vZh6eOH/v83LO0CABAqhuM99nKblmX1WRuyQef9999Xd3e3kpOTA9YnJyerpaWl133WrVunRx99tMf6tLS0YekjAACfdK4fDl/bFy5ckMvlum5NyAadyxwOR8Bty7J6rLts9erVWrFihX37ww8/1Pnz55WQkHDNfQaqvb1daWlpampqUmxs7JC2PVIxZsZsKsbMmE0VqmO2LEsXLlyQ2+3uszZkg05iYqLCwsJ6zN60trb2mOW5zOl0yul0Bqz79Kc/PVxdlCTFxsaG1JNnKDDmTwbG/MnAmD8ZQnHMfc3kXBayH0aOjIxUdna2ampqAtbX1NRo8uTJN6hXAABgJAnZGR1JWrFihTwejyZNmqS8vDw9+eSTOnXqlL797W/f6K4BAIARIKSDzsKFC3Xu3Dn967/+q5qbm5WVlaXdu3crPT39RndNTqdT//Iv/9LjT2UmY8yfDIz5k4ExfzJ8EsbssPpzbRYAAEAICtnP6AAAAPSFoAMAAIxF0AEAAMYi6AAAAGMRdPrppz/9qcaOHatRo0YpOztbv//9769bv3fvXmVnZ2vUqFH6m7/5G/3Hf/xHj5pdu3YpMzNTTqdTmZmZqqioGK7uD0gwY37xxRc1c+ZMjRkzRrGxscrLy9Nvf/vbgJrt27fL4XD0WD744IPhHkq/BTPmPXv29Dqet956K6DOpMe5uLi41zF//vOft2tG+uP8+uuva+7cuXK73XI4HHrppZf63CfUz+dgx2zC+RzsmE04n4Mdswnnc38QdPrhhRdeUGlpqb73ve/p8OHDuuuuuzR79mydOnWq1/oTJ07oK1/5iu666y4dPnxYjzzyiJYvX65du3bZNfv379fChQvl8Xj0f//3f/J4PFqwYIEOHDjwcQ3ruoId8+uvv66ZM2dq9+7dqq+v17Rp0zR37lwdPnw4oC42NlbNzc0By6hRoz6OIfUp2DFfdvz48YDxjBs3zt5m2uP8ox/9KGCsTU1Nio+P19e+9rWAupH8OHd0dOj222/Xli1b+lVvwvkc7JhNOJ+DHfNloXw+BztmE87nfrHQp7/927+1vv3tbwesu/XWW61Vq1b1Wv/www9bt956a8C6Bx54wMrNzbVvL1iwwJo1a1ZATUFBgfX1r399iHo9OMGOuTeZmZnWo48+at9+5plnLJfLNVRdHHLBjvm1116zJFler/eabZr+OFdUVFgOh8M6efKkvW6kP85XkmRVVFRct8aE8/lK/Rlzb0LtfL5Sf8Zswvl8pYE8zqF+Pl8LMzp96OzsVH19vfLz8wPW5+fna9++fb3us3///h71BQUFOnTokLq6uq5bc602P04DGfPVPvzwQ124cEHx8fEB6y9evKj09HTdfPPNKiws7PEb4o0ymDHfcccdSk1N1fTp0/Xaa68FbDP9cX766ac1Y8aMHl/SOVIf54EI9fN5KITa+TwYoXo+DwVTz2eCTh/ef/99dXd39/hHocnJyT3+oehlLS0tvdb/9a9/1fvvv3/dmmu1+XEayJiv9sQTT6ijo0MLFiyw1916663avn27fvOb3+j555/XqFGjNGXKFL399ttD2v+BGMiYU1NT9eSTT2rXrl168cUXlZGRoenTp+v111+3a0x+nJubm/XKK6/oW9/6VsD6kfw4D0Son89DIdTO54EI9fN5sEw+n0P6X0B8nBwOR8Bty7J6rOur/ur1wbb5cRto/55//nmtWbNGv/71r5WUlGSvz83NVW5urn17ypQp+uIXv6gf//jH+vd///eh6/ggBDPmjIwMZWRk2Lfz8vLU1NSkjRs36stf/vKA2rwRBtq/7du369Of/rTmz58fsD4UHudgmXA+D1Qon8/BMOV8HiiTz2dmdPqQmJiosLCwHom9tbW1R7K/LCUlpdf68PBwJSQkXLfmWm1+nAYy5steeOEFLV68WP/5n/+pGTNmXLf2pptu0pe+9KUR8ZvBYMZ8pdzc3IDxmPo4W5aln//85/J4PIqMjLxu7Uh6nAci1M/nwQjV83mohNL5PBimn88EnT5ERkYqOztbNTU1Aetramo0efLkXvfJy8vrUV9dXa1JkyYpIiLiujXXavPjNJAxSx/95ldcXKznnntOc+bM6fM4lmWpoaFBqampg+7zYA10zFc7fPhwwHhMfJyljy63fuedd7R48eI+jzOSHueBCPXzeaBC+XweKqF0Pg+G8efzx//559BTXl5uRUREWE8//bR19OhRq7S01IqOjrY/mb5q1SrL4/HY9X/+85+t0aNHW//0T/9kHT161Hr66aetiIgI67/+67/smv/5n/+xwsLCrPXr11vHjh2z1q9fb4WHh1t1dXUf+/h6E+yYn3vuOSs8PNz6yU9+YjU3N9tLW1ubXbNmzRqrqqrK+tOf/mQdPnzY+uY3v2mFh4dbBw4c+NjH15tgx7x582aroqLC+uMf/2g1NjZaq1atsiRZu3btsmtMe5wv+8Y3vmHl5OT02uZIf5wvXLhgHT582Dp8+LAlydq0aZN1+PBh691337Usy8zzOdgxm3A+BztmE87nYMd8WSifz/1B0Omnn/zkJ1Z6eroVGRlpffGLX7T27t1rb1u0aJF19913B9Tv2bPHuuOOO6zIyEjrlltusbZu3dqjzV/96ldWRkaGFRERYd16660BJ9RIEMyY7777bktSj2XRokV2TWlpqfWZz3zGioyMtMaMGWPl5+db+/bt+xhH1LdgxvzYY49Zn/3sZ61Ro0ZZcXFx1p133mlVVlb2aNOkx9myLKutrc2KioqynnzyyV7bG+mP8+XLiK/1XDXxfA52zCacz8GO2YTzeSDP7VA/n/vDYVn//6fqAAAADMNndAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAACAoLz++uuaO3eu3G63HA6HXnrppaD2/+CDD1RcXKyJEycqPDy8xz8TvewnP/mJJkyYoKioKGVkZOjZZ58Nuq8EHQAAEJSOjg7dfvvt2rJly4D27+7uVlRUlJYvX37Nfxi7detWrV69WmvWrNGbb76pRx99VA8++KBefvnloI7FNyMDAIABczgcqqioCJiV6ezs1D//8z9r586damtrU1ZWlh577DFNnTq1x/7FxcVqa2vrMSs0efJkTZkyRRs2bLDXlZaW6tChQ6qtre13/8KDHRAAAMD1fPOb39TJkydVXl4ut9utiooKzZo1S0eOHNG4ceP61Ybf79eoUaMC1kVFRengwYPq6upSREREv9rhT1cAAGDI/OlPf9Lzzz+vX/3qV7rrrrv02c9+VmVlZbrzzjv1zDPP9LudgoIC/exnP1N9fb0sy9KhQ4f085//XF1dXXr//ff73Q4zOgAAYMj87//+ryzL0vjx4wPW+/1+JSQk9Lud73//+2ppaVFubq4sy1JycrKKi4v1+OOPKywsrN/tEHQAAMCQ+fDDDxUWFqb6+voegeRTn/pUv9uJiorSz3/+c23btk1nzpxRamqqnnzyScXExCgxMbHf7RB0AADAkLnjjjvU3d2t1tZW3XXXXYNuLyIiQjfffLMkqby8XIWFhbrppv5/8oagAwAAgnLx4kW988479u0TJ06ooaFB8fHxGj9+vP7+7/9e//AP/6AnnnhCd9xxh95//329+uqrmjhxor7yla9Iko4eParOzk6dP39eFy5cUENDgyTpC1/4giTpj3/8ow4ePKicnBx5vV5t2rRJjY2N+sUvfhFUX7m8HAAABGXPnj2aNm1aj/WLFi3S9u3b1dXVpX/7t3/Ts88+q7/85S9KSEhQXl6eHn30UU2cOFGSdMstt+jdd9/t0cblWHLs2DEVFRXp+PHjioiI0LRp0/TYY48pIyMjqL4SdAAAgLG4vBwAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQAQAAxiLoAAAAY/1/1dB3S6Z9H9UAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"_ = plt.hist(uint64_values, bins=300)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Naively convert the values to the [0, 1] range using a float32 representation 2 casts and a floating point division:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"naive_float32_values = uint64_values.astype(np.float32) / np.float32(np.iinfo(np.uint64).max)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"assert np.isfinite(naive_float32_values).all()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Visually check if we have introduced a bias with this naive method:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjoAAAGdCAYAAAAbudkLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAxSklEQVR4nO3df1iVdZ7/8ReBnJCRe0CEwxkZs0kZCXN2cEJ0Ji0VdEVyai/dZa9z6a6LNZbEKltae012XTNSajpNTq7rtllG4bVjNrUaA12WxSj+YOUaSWucSVccQUzhgI57ILq/f3R5fzuCykF+yKfn47ru6+Lc9/u+78/94Zxzv/ic+z6E2LZtCwAAwEA39XUDAAAAegpBBwAAGIugAwAAjEXQAQAAxiLoAAAAYxF0AACAsQg6AADAWAQdAABgrLC+bkBf+uKLL3Tq1CkNGjRIISEhfd0cAADQCbZtq7m5WR6PRzfddPUxm6910Dl16pQSExP7uhkAAKALampqNHTo0KvWBBV01q9fr/Xr1+v48eOSpNtvv10//elPNX36dEnSvHnz9PLLLwesk5aWpoqKCuex3+9XQUGBXn/9dV28eFGTJ0/WCy+8ENDQhoYG5eXl6a233pIkZWdn6/nnn9c3v/lNp+bEiRN66KGHtHPnTkVERCgnJ0erV69WeHh4p49n0KBBkr7sqKioqGC6AgAA9JGmpiYlJiY65/GrCSroDB06VE8//bRuu+02SdLLL7+se++9VwcPHtTtt98uSZo2bZpeeuklZ53Lg0d+fr7efvttFRcXa/DgwVqyZImysrJUWVmp0NBQSVJOTo5OnjypkpISSdKCBQvk9Xr19ttvS5La2to0Y8YMDRkyROXl5Tp79qzmzp0r27b1/PPPd/p4Ln1cFRUVRdABAKCf6dRlJ/Z1io6Otv/jP/7Dtm3bnjt3rn3vvfdesbaxsdEeMGCAXVxc7Mz785//bN900012SUmJbdu2ffjwYVuSXVFR4dTs2bPHlmR//PHHtm3b9o4dO+ybbrrJ/vOf/+zUvP7667bL5bJ9Pl+n2+7z+WxJQa0DAAD6VjDn7y7fddXW1qbi4mJduHBB6enpzvz3339fcXFxGjlypHJzc1VfX+8sq6ysVGtrqzIyMpx5Ho9HKSkp2r17tyRpz549sixLaWlpTs24ceNkWVZATUpKijwej1OTmZkpv9+vysrKK7bZ7/erqakpYAIAAOYKOugcOnRI3/jGN+RyufTggw9q27ZtSk5OliRNnz5dRUVF2rlzp5599lnt379f99xzj/x+vySprq5O4eHhio6ODthmfHy86urqnJq4uLh2+42LiwuoiY+PD1geHR2t8PBwp6YjhYWFsizLmbgQGQAAswV911VSUpKqqqrU2NiorVu3au7cudq1a5eSk5M1Z84cpy4lJUVjx47VsGHDtH37dt13331X3KZt2wGfs3X0mVtXai63bNkyLV682Hl86WImAABgpqBHdMLDw3Xbbbdp7NixKiws1JgxY/Tcc891WJuQkKBhw4bp6NGjkiS3262WlhY1NDQE1NXX1zsjNG63W6dPn263rTNnzgTUXD5y09DQoNbW1nYjPV/lcrmcC4+5ABkAAPNd9zcj27btfDR1ubNnz6qmpkYJCQmSpNTUVA0YMEBlZWVOTW1traqrqzV+/HhJUnp6unw+n/bt2+fU7N27Vz6fL6CmurpatbW1Tk1paalcLpdSU1Ov95AAAIAhQmzbtjtb/Pjjj2v69OlKTExUc3OziouL9fTTT6ukpETp6elavny57r//fiUkJOj48eN6/PHHdeLECR05csS51/0nP/mJ/vu//1ubNm1STEyMCgoKdPbs2YDby6dPn65Tp05pw4YNkr68vXzYsGEBt5d/73vfU3x8vFatWqVz585p3rx5mjVrVlC3lzc1NcmyLPl8PkZ3AADoJ4I5fwd1jc7p06fl9XpVW1sry7J0xx13qKSkRFOnTtXFixd16NAhvfLKK2psbFRCQoLuvvtubdmyJeALfdauXauwsDDNnj3b+cLATZs2OSFHkoqKipSXl+fcnZWdna1169Y5y0NDQ7V9+3YtXLhQEyZMCPjCQAAAgEuCGtExDSM6AAD0P8Gcv/nv5QAAwFgEHQAAYCyCDgAAMBZBBwAAGIugAwAAjEXQ6YduWbpdtyzd3tfNAICr4r0KNwKCztdMb73x9NWb25X2y5stusuVXkOmv7auF6HnSz3RD/Tt1RF0etCN/MS7kdt2NV/XF/TX8ZhvRJf/HjrzfLye52x3P99vhOdRb4XBYH4vHdUH284boW/RMYJOLwr2hdXdb6JdeSEG89frpXldfcPo6RNCR+270d+cejPYXe132leCfe5058mpu7d5Pf3YU+t29/tHbz9XenMU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0M+9g37uPT3R19caybmk316MHBsbq9DQ0HajN/X19e1GeQAAwNdTvw064eHhSk1NVVlZWcD8srIyjR8/vo9aBQAAbiT9+qOrxYsXy+v1auzYsUpPT9e///u/68SJE3rwwQf7umlyuVx68skn231Uhu5FP/cO+rl30M+9g37uPTdCX/fr28ulL78wcOXKlaqtrVVKSorWrl2ru+66q6+bBQAAbgD9PugAAABcSb+9RgcAAOBaCDoAAMBYBB0AAGAsgg4AADAWQaeLXnjhBQ0fPlw333yzUlNT9eGHH161fteuXUpNTdXNN9+sW2+9Vf/2b//WSy3t/4Lp6zfeeENTp07VkCFDFBUVpfT0dP32t/xz1c4I9jl9ye9+9zuFhYXpe9/7Xs820BDB9rPf79cTTzyhYcOGyeVy6Tvf+Y7+8z//s5da238F289FRUUaM2aMBg4cqISEBP3DP/yDzp4920ut7Z8++OADzZw5Ux6PRyEhIXrzzTevuU6fnAttBK24uNgeMGCAvXHjRvvw4cP2I488YkdGRtr/+7//22H9p59+ag8cONB+5JFH7MOHD9sbN260BwwYYP/617/u5Zb3P8H29SOPPGI/88wz9r59++w//OEP9rJly+wBAwbY//M//9PLLe9fgu3nSxobG+1bb73VzsjIsMeMGdM7je3HutLP2dnZdlpaml1WVmYfO3bM3rt3r/273/2uF1vd/wTbzx9++KF900032c8995z96aef2h9++KF9++2327NmzerllvcvO3bssJ944gl769attiR727ZtV63vq3MhQacL7rzzTvvBBx8MmPfd737XXrp0aYf1jz76qP3d7343YN4DDzxgjxs3rsfaaIpg+7ojycnJ9lNPPdXdTTNKV/t5zpw59r/+67/aTz75JEGnE4Lt53feece2LMs+e/ZsbzTPGMH286pVq+xbb701YN4vf/lLe+jQoT3WRtN0Juj01bmQj66C1NLSosrKSmVkZATMz8jI0O7duztcZ8+ePe3qMzMzdeDAAbW2tvZYW/u7rvT15b744gs1NzcrJiamJ5pohK7280svvaQ//elPevLJJ3u6iUboSj+/9dZbGjt2rFauXKlvfetbGjlypAoKCnTx4sXeaHK/1JV+Hj9+vE6ePKkdO3bItm2dPn1av/71rzVjxozeaPLXRl+dC/v1v4DoC5999pna2tra/ePQ+Pj4dv9g9JK6uroO6z///HN99tlnSkhI6LH29mdd6evLPfvss7pw4YJmz57dE000Qlf6+ejRo1q6dKk+/PBDhYXxNtIZXennTz/9VOXl5br55pu1bds2ffbZZ1q4cKHOnTvHdTpX0JV+Hj9+vIqKijRnzhz93//9nz7//HNlZ2fr+eef740mf2301bmQEZ0uCgkJCXhs23a7edeq72g+2gu2ry95/fXXtXz5cm3ZskVxcXE91TxjdLaf29ralJOTo6eeekojR47sreYZI5jn8xdffKGQkBAVFRXpzjvv1F//9V9rzZo12rRpE6M61xBMPx8+fFh5eXn66U9/qsrKSpWUlOjYsWM3xP9NNE1fnAv5UyxIsbGxCg0NbfeXQX19fbukeonb7e6wPiwsTIMHD+6xtvZ3XenrS7Zs2aL58+frv/7rvzRlypSebGa/F2w/Nzc368CBAzp48KAefvhhSV+ekG3bVlhYmEpLS3XPPff0Stv7k648nxMSEvStb31LlmU580aNGiXbtnXy5EmNGDGiR9vcH3WlnwsLCzVhwgT9y7/8iyTpjjvuUGRkpH70ox/pZz/7GaPu3aSvzoWM6AQpPDxcqampKisrC5hfVlam8ePHd7hOenp6u/rS0lKNHTtWAwYM6LG29ndd6Wvpy5GcefPm6bXXXuMz9k4Itp+joqJ06NAhVVVVOdODDz6opKQkVVVVKS0trbea3q905fk8YcIEnTp1SufPn3fm/eEPf9BNN92koUOH9mh7+6uu9PNf/vIX3XRT4OkwNDRU0v8fccD167NzYY9e6myoS7cuvvjii/bhw4ft/Px8OzIy0j5+/Lht27a9dOlS2+v1OvWXbqn753/+Z/vw4cP2iy++yO3lnRRsX7/22mt2WFiY/atf/cqura11psbGxr46hH4h2H6+HHdddU6w/dzc3GwPHTrU/pu/+Rv7o48+snft2mWPGDHC/qd/+qe+OoR+Idh+fumll+ywsDD7hRdesP/0pz/Z5eXl9tixY+0777yzrw6hX2hubrYPHjxoHzx40JZkr1mzxj548KBzG/+Nci4k6HTRr371K3vYsGF2eHi4/f3vf9/etWuXs2zu3Ln2xIkTA+rff/99+6/+6q/s8PBw+5ZbbrHXr1/fyy3uv4Lp64kTJ9qS2k1z587t/Yb3M8E+p7+KoNN5wfbzkSNH7ClTptgRERH20KFD7cWLF9t/+ctfernV/U+w/fzLX/7STk5OtiMiIuyEhAT77//+7+2TJ0/2cqv7l/fee++q77c3yrkwxLYZlwMAAGbiGh0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGMRdAAAgLEIOgAAwFgEHQAAYCyCDgAAMBZBBwAAGOv/AQmQuRYibjrTAAAAAElFTkSuQmCC",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"_ = plt.hist(naive_float32_values, bins=300)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Still look uniform enough to me... Let's check with a KS test:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0.00019738053970341785, 0.8306757201599806)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from scipy import stats\n",
"\n",
"ksstat, p = stats.kstest(naive_float32_values, stats.uniform(loc=0.0, scale=1.0).cdf)\n",
"ksstat, p"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So the p-value is large and we cannot reject the Null Hypothesis (the data is uniform in the [0, 1] range).\n",
"\n",
"If the bias had been large, maybe we could have computed an approximation to the empirical CDF once on such as large collections and then applied a correction to our `naive_float32_values` based on it.\n",
"\n",
"There might be more subtle issues but maybe for non-cryptographic machine learning applications, this would be enough.\n",
"\n",
"Let's do a simple sanity check:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"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>y</th>\n",
" <th>y_m1</th>\n",
" <th>y_m2</th>\n",
" <th>y_m3</th>\n",
" <th>y_m4</th>\n",
" <th>y_m5</th>\n",
" <th>y_m6</th>\n",
" <th>y_m7</th>\n",
" <th>y_m8</th>\n",
" <th>y_m9</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>0.326202</td>\n",
" <td>0.418550</td>\n",
" <td>0.899357</td>\n",
" <td>0.221441</td>\n",
" <td>0.812143</td>\n",
" <td>0.673556</td>\n",
" <td>0.827395</td>\n",
" <td>0.960765</td>\n",
" <td>0.601506</td>\n",
" <td>0.321955</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>0.694309</td>\n",
" <td>0.326202</td>\n",
" <td>0.418550</td>\n",
" <td>0.899357</td>\n",
" <td>0.221441</td>\n",
" <td>0.812143</td>\n",
" <td>0.673556</td>\n",
" <td>0.827395</td>\n",
" <td>0.960765</td>\n",
" <td>0.601506</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>0.896923</td>\n",
" <td>0.694309</td>\n",
" <td>0.326202</td>\n",
" <td>0.418550</td>\n",
" <td>0.899357</td>\n",
" <td>0.221441</td>\n",
" <td>0.812143</td>\n",
" <td>0.673556</td>\n",
" <td>0.827395</td>\n",
" <td>0.960765</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>0.235569</td>\n",
" <td>0.896923</td>\n",
" <td>0.694309</td>\n",
" <td>0.326202</td>\n",
" <td>0.418550</td>\n",
" <td>0.899357</td>\n",
" <td>0.221441</td>\n",
" <td>0.812143</td>\n",
" <td>0.673556</td>\n",
" <td>0.827395</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>0.915756</td>\n",
" <td>0.235569</td>\n",
" <td>0.896923</td>\n",
" <td>0.694309</td>\n",
" <td>0.326202</td>\n",
" <td>0.418550</td>\n",
" <td>0.899357</td>\n",
" <td>0.221441</td>\n",
" <td>0.812143</td>\n",
" <td>0.673556</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9999995</th>\n",
" <td>0.016953</td>\n",
" <td>0.217817</td>\n",
" <td>0.474217</td>\n",
" <td>0.552753</td>\n",
" <td>0.360996</td>\n",
" <td>0.887683</td>\n",
" <td>0.575834</td>\n",
" <td>0.701747</td>\n",
" <td>0.084846</td>\n",
" <td>0.187896</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9999996</th>\n",
" <td>0.262798</td>\n",
" <td>0.016953</td>\n",
" <td>0.217817</td>\n",
" <td>0.474217</td>\n",
" <td>0.552753</td>\n",
" <td>0.360996</td>\n",
" <td>0.887683</td>\n",
" <td>0.575834</td>\n",
" <td>0.701747</td>\n",
" <td>0.084846</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9999997</th>\n",
" <td>0.760984</td>\n",
" <td>0.262798</td>\n",
" <td>0.016953</td>\n",
" <td>0.217817</td>\n",
" <td>0.474217</td>\n",
" <td>0.552753</td>\n",
" <td>0.360996</td>\n",
" <td>0.887683</td>\n",
" <td>0.575834</td>\n",
" <td>0.701747</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9999998</th>\n",
" <td>0.989704</td>\n",
" <td>0.760984</td>\n",
" <td>0.262798</td>\n",
" <td>0.016953</td>\n",
" <td>0.217817</td>\n",
" <td>0.474217</td>\n",
" <td>0.552753</td>\n",
" <td>0.360996</td>\n",
" <td>0.887683</td>\n",
" <td>0.575834</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9999999</th>\n",
" <td>0.711802</td>\n",
" <td>0.989704</td>\n",
" <td>0.760984</td>\n",
" <td>0.262798</td>\n",
" <td>0.016953</td>\n",
" <td>0.217817</td>\n",
" <td>0.474217</td>\n",
" <td>0.552753</td>\n",
" <td>0.360996</td>\n",
" <td>0.887683</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>9999991 rows × 10 columns</p>\n",
"</div>"
],
"text/plain": [
" y y_m1 y_m2 y_m3 y_m4 y_m5 y_m6 \\\n",
"9 0.326202 0.418550 0.899357 0.221441 0.812143 0.673556 0.827395 \n",
"10 0.694309 0.326202 0.418550 0.899357 0.221441 0.812143 0.673556 \n",
"11 0.896923 0.694309 0.326202 0.418550 0.899357 0.221441 0.812143 \n",
"12 0.235569 0.896923 0.694309 0.326202 0.418550 0.899357 0.221441 \n",
"13 0.915756 0.235569 0.896923 0.694309 0.326202 0.418550 0.899357 \n",
"... ... ... ... ... ... ... ... \n",
"9999995 0.016953 0.217817 0.474217 0.552753 0.360996 0.887683 0.575834 \n",
"9999996 0.262798 0.016953 0.217817 0.474217 0.552753 0.360996 0.887683 \n",
"9999997 0.760984 0.262798 0.016953 0.217817 0.474217 0.552753 0.360996 \n",
"9999998 0.989704 0.760984 0.262798 0.016953 0.217817 0.474217 0.552753 \n",
"9999999 0.711802 0.989704 0.760984 0.262798 0.016953 0.217817 0.474217 \n",
"\n",
" y_m7 y_m8 y_m9 \n",
"9 0.960765 0.601506 0.321955 \n",
"10 0.827395 0.960765 0.601506 \n",
"11 0.673556 0.827395 0.960765 \n",
"12 0.812143 0.673556 0.827395 \n",
"13 0.221441 0.812143 0.673556 \n",
"... ... ... ... \n",
"9999995 0.701747 0.084846 0.187896 \n",
"9999996 0.575834 0.701747 0.084846 \n",
"9999997 0.887683 0.575834 0.701747 \n",
"9999998 0.360996 0.887683 0.575834 \n",
"9999999 0.552753 0.360996 0.887683 \n",
"\n",
"[9999991 rows x 10 columns]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"\n",
"y_series = pd.Series(naive_float32_values).rename(\"y\")\n",
"\n",
"# Build some lagged features to attempt to predict y from previously generated values\n",
"df = pd.concat(\n",
" [y_series] + [y_series.shift(i).rename(f\"y_m{i}\") for i in range(1, 10)], axis=\"columns\"\n",
").dropna()\n",
"df"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"X = df.drop(\"y\", axis=\"columns\").values\n",
"y = df[\"y\"].values"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.4185504 , 0.89935684, 0.22144106, 0.8121428 , 0.673556 ,\n",
" 0.82739544, 0.9607652 , 0.6015065 , 0.32195514],\n",
" [0.3262016 , 0.4185504 , 0.89935684, 0.22144106, 0.8121428 ,\n",
" 0.673556 , 0.82739544, 0.9607652 , 0.6015065 ],\n",
" [0.694309 , 0.3262016 , 0.4185504 , 0.89935684, 0.22144106,\n",
" 0.8121428 , 0.673556 , 0.82739544, 0.9607652 ],\n",
" [0.8969235 , 0.694309 , 0.3262016 , 0.4185504 , 0.89935684,\n",
" 0.22144106, 0.8121428 , 0.673556 , 0.82739544],\n",
" [0.23556878, 0.8969235 , 0.694309 , 0.3262016 , 0.4185504 ,\n",
" 0.89935684, 0.22144106, 0.8121428 , 0.673556 ]], dtype=float32)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X[0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0.3262016 , 0.694309 , 0.8969235 , 0.23556878, 0.91575575],\n",
" dtype=float32)"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y[0]"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[Parallel(n_jobs=4)]: Using backend LokyBackend with 4 concurrent workers.\n",
"[Parallel(n_jobs=4)]: Done 5 tasks | elapsed: 21.6s\n",
"[Parallel(n_jobs=4)]: Done 10 tasks | elapsed: 34.2s\n",
"[Parallel(n_jobs=4)]: Done 17 tasks | elapsed: 54.6s\n",
"[Parallel(n_jobs=4)]: Done 24 tasks | elapsed: 1.1min\n",
"[Parallel(n_jobs=4)]: Done 33 tasks | elapsed: 1.6min\n",
"[Parallel(n_jobs=4)]: Done 42 tasks | elapsed: 2.0min\n",
"[Parallel(n_jobs=4)]: Done 53 tasks | elapsed: 2.6min\n",
"[Parallel(n_jobs=4)]: Done 64 tasks | elapsed: 3.0min\n",
"[Parallel(n_jobs=4)]: Done 77 tasks | elapsed: 3.6min\n",
"[Parallel(n_jobs=4)]: Done 90 tasks | elapsed: 4.2min\n",
"[Parallel(n_jobs=4)]: Done 100 out of 100 | elapsed: 4.6min finished\n"
]
}
],
"source": [
"from sklearn.ensemble import RandomForestRegressor\n",
"from sklearn.model_selection import TimeSeriesSplit\n",
"from sklearn.model_selection import permutation_test_score\n",
"\n",
"\n",
"rf = RandomForestRegressor(min_samples_leaf=30, n_estimators=100)\n",
"cv = TimeSeriesSplit(n_splits=5, max_train_size=int(5e3), test_size=int(5e3))\n",
"score, permutation_scores, pvalue = permutation_test_score(\n",
" rf, X, y, cv=cv, n_permutations=100, verbose=10, n_jobs=4\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.6138613861386139"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pvalue"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So the p-value is large again, we can reject the Null Hypothesis that a `RandomForestRegressor` can predict the next RNG value from the past `X.shape[1]` generated values better than chance.\n",
"\n",
"To conclude: there is no obvious predictable signal in this RNG stream of `float32` values."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.6 ('base')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "c91744e846ab1fb46a81a92b1fa828c0e6b1381e7e12fd7b2bb300d813000458"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@smason
Copy link

smason commented Oct 24, 2022

not sure if it makes much difference, but I think your "smarter" version is preferred these days, i.e.:

(uint32_values >> 8).astype(np.float32) * np.float32(1 / (1 << 24))

the bottom of https://prng.di.unimi.it/ has some comments on some different definitions of uniformity that could apply here

I noticed that your twitter message said you wanted $[0..1]$, so you might want a -1 in there. Operator precedence is a bit annoying so it needs more brackets than you might expect:

(uint32_values >> 8).astype(np.float32) * np.float32(1 / ((1 << 24) - 1))

e.g. test with:

uint32_values = np.uint32(-1)

@ogrisel
Copy link
Author

ogrisel commented Oct 28, 2022

Thanks @smason!

@fcharras
Copy link

fcharras commented Oct 28, 2022

float32(np.uint32(x >> uint32(32)) >> uint32(8)) * (float32(1) / float32(uint32(1) << uint32(24))) seems to be better, it seems equivalent to casting an intermediate float64 to float32, without actually materializing the float64 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment