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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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": 1,
"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": 2,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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Wps3Um/GfLl169apvb1dc+fOtdfdfvvt2rJli15//XW9/PLLGjZsmCZNmqT33nuvX/vfG70Zc3Jysp555hlt375dr776qlJTUzV16lS9/fbbdo3Jj3NTU5PefPNNffOb3wxaP5gf594Y6udzfxhq53NvDPXzua9MPp+H9L+AuJEcDkfQbcuyuq27Xv3l60Nt80brbf9efvllrVixQr/4xS+UmJhor8/KylJWVpZ9e9KkSfrSl76kH/7wh/q3f/u3/ut4H4Qy5tTUVKWmptq3s7Oz1djYqLVr1+orX/lKr9q8GXrbvy1btuizn/2s5syZE7R+KDzOoTLhfO6toXw+h8KU87m3TD6fmdG5joSEBIWFhXVL7C0tLd2S/SVut/uK9eHh4Ro+fPg1a67W5o3UmzFf8sorr2jBggX6j//4D02bNu2atbfccou+/OUvD4rfDPoy5k/KysoKGo+pj7NlWfrpT38qr9eryMjIa9YOpse5N4b6+dwXQ/V87i9D6XzuC9PPZ4LOdURGRiojI0PV1dVB66urqzVx4sQr7pOdnd2tvqqqShMmTFBERMQ1a67W5o3UmzFLH//mV1hYqJdeekmzZs267nEsy1J9fb2Sk5P73Oe+6u2YL3fo0KGg8Zj4OEsff9z6/fff14IFC657nMH0OPfGUD+fe2son8/9ZSidz31h/Pl8469/HnrKysqsiIgI67nnnrOOHDlilZSUWNHR0faV6cuWLbO8Xq9d//vf/9669dZbrX/4h3+wjhw5Yj333HNWRESE9Z//+Z92zX//939bYWFh1urVq62jR49aq1evtsLDw63a2tobPr4rCXXML730khUeHm796Ec/spqamuyltbXVrlmxYoVVWVlp/e53v7MOHTpkfeMb37DCw8Otffv23fDxXUmoY96wYYNVXl5u/fa3v7UaGhqsZcuWWZKs7du32zWmPc6X/O3f/q2VmZl5xTYH++N8/vx569ChQ9ahQ4csSdb69eutQ4cOWX/4wx8syzLzfA51zCacz6GO2YTzOdQxXzKUz+eeIOj00I9+9CNr1KhRVmRkpPWlL33J2r17t71t/vz51n333RdUv2vXLuvuu++2IiMjrdtuu83atGlTtzZ//vOfW6mpqVZERIR1++23B51Qg0EoY77vvvssSd2W+fPn2zUlJSXW5z73OSsyMtIaMWKElZOTY+3Zs+cGjuj6QhnzU089ZX3+85+3hg0bZsXFxVn33HOPVVFR0a1Nkx5ny7Ks1tZWKyoqynrmmWeu2N5gf5wvfYz4as9VE8/nUMdswvkc6phNOJ9789we6udzTzgs6/9fVQcAAGAYrtEBAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAELy9ttva/bs2fJ4PHI4HHrttddC2v9Pf/qTCgsLNX78eIWHh3f7Z6KX/OhHP9K4ceMUFRWl1NRUvfjiiyH3laADAABC0t7errvuuksbN27s1f5dXV2KiopScXHxVf9h7KZNm7R8+XKtWLFC77zzjp588kk9+uijeuONN0I6Ft+MDAAAes3hcKi8vDxoVqajo0P/+I//qG3btqm1tVXp6el66qmnNHny5G77FxYWqrW1tdus0MSJEzVp0iStWbPGXldSUqKDBw+qpqamx/0LD3VAAAAA1/KNb3xDJ06cUFlZmTwej8rLyzVjxgwdPnxYY8aM6VEbgUBAw4YNC1oXFRWl/fv3q7OzUxERET1qhz9dAQCAfvO73/1OL7/8sn7+85/r3nvv1ec//3mVlpbqnnvu0fPPP9/jdnJzc/WTn/xEdXV1sixLBw8e1E9/+lN1dnbqww8/7HE7zOgAAIB+8z//8z+yLEtjx44NWh8IBDR8+PAet/NP//RPam5uVlZWlizLUlJSkgoLC/X0008rLCysx+0QdAAAQL/56KOPFBYWprq6um6B5DOf+UyP24mKitJPf/pTbd68WadPn1ZycrKeeeYZxcTEKCEhocftEHQAAEC/ufvuu9XV1aWWlhbde++9fW4vIiJCI0eOlCSVlZUpLy9Pt9zS8ytvCDoAACAkFy5c0Pvvv2/fPn78uOrr6xUfH6+xY8fqb/7mb/R3f/d3Wrdune6++259+OGH2rlzp8aPH6+vfvWrkqQjR46oo6ND586d0/nz51VfXy9J+uIXvyhJ+u1vf6v9+/crMzNTPp9P69evV0NDg1544YWQ+srHywEAQEh27dqlKVOmdFs/f/58bdmyRZ2dnfrud7+rF198UX/84x81fPhwZWdn68knn9T48eMlSbfddpv+8Ic/dGvjUiw5evSoCgoKdOzYMUVERGjKlCl66qmnlJqaGlJfCToAAMBYfLwcAAAYi6ADAACMRdABAADGIugAAABjEXQAAICxCDoAAMBYBB0AAGAsgg4AADAWQQcAABiLoAMAAIxF0AEAAMYi6AAAAGP9P9b/A+Rga91uAAAAAElFTkSuQmCC",
"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": [
"Map the uint64 values to the float32 space:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# naive_float32_values = uint64_values.astype(np.float32) / np.float32(np.iinfo(np.uint64).max)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.uint32(1)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"uint32_values = np.frombuffer(uint64_values.data, dtype=np.uint32)\n",
"\n",
"# XXX: this would also work but would waste random bytes...\n",
"# uint32_values = uint64_values.astype(np.uint32)\n",
"\n",
"float32_values = (uint32_values >> np.uint32(8)) * (\n",
" np.float32(1.0) / (np.uint32(1) << np.uint32(24))\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"assert np.isfinite(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": 7,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"_ = plt.hist(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": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0.00011932065124509172, 0.938274054695534)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from scipy import stats\n",
"\n",
"ksstat, p = stats.kstest(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": 9,
"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.401732</td>\n",
" <td>0.901828</td>\n",
" <td>0.621645</td>\n",
" <td>0.929796</td>\n",
" <td>0.065658</td>\n",
" <td>0.097534</td>\n",
" <td>0.117509</td>\n",
" <td>0.309837</td>\n",
" <td>0.565413</td>\n",
" <td>0.189782</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>0.553764</td>\n",
" <td>0.401732</td>\n",
" <td>0.901828</td>\n",
" <td>0.621645</td>\n",
" <td>0.929796</td>\n",
" <td>0.065658</td>\n",
" <td>0.097534</td>\n",
" <td>0.117509</td>\n",
" <td>0.309837</td>\n",
" <td>0.565413</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>0.557354</td>\n",
" <td>0.553764</td>\n",
" <td>0.401732</td>\n",
" <td>0.901828</td>\n",
" <td>0.621645</td>\n",
" <td>0.929796</td>\n",
" <td>0.065658</td>\n",
" <td>0.097534</td>\n",
" <td>0.117509</td>\n",
" <td>0.309837</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>0.229553</td>\n",
" <td>0.557354</td>\n",
" <td>0.553764</td>\n",
" <td>0.401732</td>\n",
" <td>0.901828</td>\n",
" <td>0.621645</td>\n",
" <td>0.929796</td>\n",
" <td>0.065658</td>\n",
" <td>0.097534</td>\n",
" <td>0.117509</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>0.737321</td>\n",
" <td>0.229553</td>\n",
" <td>0.557354</td>\n",
" <td>0.553764</td>\n",
" <td>0.401732</td>\n",
" <td>0.901828</td>\n",
" <td>0.621645</td>\n",
" <td>0.929796</td>\n",
" <td>0.065658</td>\n",
" <td>0.097534</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>19999995</th>\n",
" <td>0.496097</td>\n",
" <td>0.072483</td>\n",
" <td>0.377848</td>\n",
" <td>0.173808</td>\n",
" <td>0.063203</td>\n",
" <td>0.640214</td>\n",
" <td>0.838051</td>\n",
" <td>0.698533</td>\n",
" <td>0.970963</td>\n",
" <td>0.697886</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19999996</th>\n",
" <td>0.167435</td>\n",
" <td>0.496097</td>\n",
" <td>0.072483</td>\n",
" <td>0.377848</td>\n",
" <td>0.173808</td>\n",
" <td>0.063203</td>\n",
" <td>0.640214</td>\n",
" <td>0.838051</td>\n",
" <td>0.698533</td>\n",
" <td>0.970963</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19999997</th>\n",
" <td>0.106291</td>\n",
" <td>0.167435</td>\n",
" <td>0.496097</td>\n",
" <td>0.072483</td>\n",
" <td>0.377848</td>\n",
" <td>0.173808</td>\n",
" <td>0.063203</td>\n",
" <td>0.640214</td>\n",
" <td>0.838051</td>\n",
" <td>0.698533</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19999998</th>\n",
" <td>0.868544</td>\n",
" <td>0.106291</td>\n",
" <td>0.167435</td>\n",
" <td>0.496097</td>\n",
" <td>0.072483</td>\n",
" <td>0.377848</td>\n",
" <td>0.173808</td>\n",
" <td>0.063203</td>\n",
" <td>0.640214</td>\n",
" <td>0.838051</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19999999</th>\n",
" <td>0.478732</td>\n",
" <td>0.868544</td>\n",
" <td>0.106291</td>\n",
" <td>0.167435</td>\n",
" <td>0.496097</td>\n",
" <td>0.072483</td>\n",
" <td>0.377848</td>\n",
" <td>0.173808</td>\n",
" <td>0.063203</td>\n",
" <td>0.640214</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>19999991 rows × 10 columns</p>\n",
"</div>"
],
"text/plain": [
" y y_m1 y_m2 y_m3 y_m4 y_m5 \\\n",
"9 0.401732 0.901828 0.621645 0.929796 0.065658 0.097534 \n",
"10 0.553764 0.401732 0.901828 0.621645 0.929796 0.065658 \n",
"11 0.557354 0.553764 0.401732 0.901828 0.621645 0.929796 \n",
"12 0.229553 0.557354 0.553764 0.401732 0.901828 0.621645 \n",
"13 0.737321 0.229553 0.557354 0.553764 0.401732 0.901828 \n",
"... ... ... ... ... ... ... \n",
"19999995 0.496097 0.072483 0.377848 0.173808 0.063203 0.640214 \n",
"19999996 0.167435 0.496097 0.072483 0.377848 0.173808 0.063203 \n",
"19999997 0.106291 0.167435 0.496097 0.072483 0.377848 0.173808 \n",
"19999998 0.868544 0.106291 0.167435 0.496097 0.072483 0.377848 \n",
"19999999 0.478732 0.868544 0.106291 0.167435 0.496097 0.072483 \n",
"\n",
" y_m6 y_m7 y_m8 y_m9 \n",
"9 0.117509 0.309837 0.565413 0.189782 \n",
"10 0.097534 0.117509 0.309837 0.565413 \n",
"11 0.065658 0.097534 0.117509 0.309837 \n",
"12 0.929796 0.065658 0.097534 0.117509 \n",
"13 0.621645 0.929796 0.065658 0.097534 \n",
"... ... ... ... ... \n",
"19999995 0.838051 0.698533 0.970963 0.697886 \n",
"19999996 0.640214 0.838051 0.698533 0.970963 \n",
"19999997 0.063203 0.640214 0.838051 0.698533 \n",
"19999998 0.173808 0.063203 0.640214 0.838051 \n",
"19999999 0.377848 0.173808 0.063203 0.640214 \n",
"\n",
"[19999991 rows x 10 columns]"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"\n",
"y_series = pd.Series(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": 10,
"metadata": {},
"outputs": [],
"source": [
"X = df.drop(\"y\", axis=\"columns\").values\n",
"y = df[\"y\"].values"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0.90182781, 0.62164545, 0.92979622, 0.06565809, 0.09753442,\n",
" 0.11750877, 0.30983722, 0.565413 , 0.18978167])"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X[0]"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.4017317295074463"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y[0]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"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: 25.9s\n",
"[Parallel(n_jobs=4)]: Done 10 tasks | elapsed: 39.6s\n",
"[Parallel(n_jobs=4)]: Done 17 tasks | elapsed: 58.9s\n",
"[Parallel(n_jobs=4)]: Done 24 tasks | elapsed: 1.2min\n",
"[Parallel(n_jobs=4)]: Done 33 tasks | elapsed: 1.7min\n",
"[Parallel(n_jobs=4)]: Done 42 tasks | elapsed: 2.2min\n",
"[Parallel(n_jobs=4)]: Done 53 tasks | elapsed: 2.7min\n",
"[Parallel(n_jobs=4)]: Done 64 tasks | elapsed: 3.2min\n",
"[Parallel(n_jobs=4)]: Done 77 tasks | elapsed: 3.9min\n",
"[Parallel(n_jobs=4)]: Done 90 tasks | elapsed: 4.6min\n",
"[Parallel(n_jobs=4)]: Done 100 out of 100 | elapsed: 5.0min 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": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.43564356435643564"
]
},
"execution_count": 14,
"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
}
@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