298 lines
188 KiB
Plaintext
298 lines
188 KiB
Plaintext
{
|
||||
|
|
"cells": [
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "5b95cdf9",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"# 11 — PDE solvers\n",
|
|||
|
|
"\n",
|
|||
|
|
"Fokker–Planck, HJB, Poisson."
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 1,
|
|||
|
|
"id": "f355a328",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:01.404135Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:01.403772Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:02.110007Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:02.108701Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": [
|
|||
|
|
"import numpy as np\n",
|
|||
|
|
"import matplotlib.pyplot as plt\n",
|
|||
|
|
"from optimizr import _core as opt\n",
|
|||
|
|
"plt.rcParams['figure.figsize'] = (7, 4)\n",
|
|||
|
|
"plt.rcParams['figure.dpi'] = 110\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "7bd0f13d",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"## Pure-diffusion Fokker–Planck\n",
|
|||
|
|
"\n",
|
|||
|
|
"$\\partial_t m = \\tfrac12 \\partial_{xx} m$ with Gaussian initial density should remain centred and approximately Gaussian."
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 2,
|
|||
|
|
"id": "4199167d",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:02.113654Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:02.113279Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:02.515354Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:02.513789Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"total mass at t=0: 1.0000000000000002\n",
|
|||
|
|
"total mass at t=T: 0.9999999998667097\n",
|
|||
|
|
"mean at t=T: -1.6653345369377348e-16\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"res = opt.fokker_planck_constant(\n",
|
|||
|
|
" mu=0.0, sigma_sq=1.0, init_sigma=1.0,\n",
|
|||
|
|
" x_min=-8.0, x_max=8.0, n_x=401,\n",
|
|||
|
|
" t_horizon=0.5, n_t=8000,\n",
|
|||
|
|
")\n",
|
|||
|
|
"x = np.array(res['x_grid'])\n",
|
|||
|
|
"t = np.array(res['time_grid'])\n",
|
|||
|
|
"nx = res['n_x']; nt = res['n_t']\n",
|
|||
|
|
"M = np.array(res['density']).reshape(nt + 1, nx)\n",
|
|||
|
|
"print('total mass at t=0:', np.trapezoid(M[0], x))\n",
|
|||
|
|
"print('total mass at t=T:', np.trapezoid(M[-1], x))\n",
|
|||
|
|
"print('mean at t=T:', np.trapezoid(x * M[-1], x))\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 3,
|
|||
|
|
"id": "ba80f3c4",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:02.519856Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:02.519554Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:02.912830Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:02.911483Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 770x440 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"fig, ax = plt.subplots()\n",
|
|||
|
|
"for k in [0, nt // 4, nt // 2, 3 * nt // 4, nt]:\n",
|
|||
|
|
" ax.plot(x, M[k], label=f't = {t[k]:.2f}')\n",
|
|||
|
|
"ax.set_xlim(-5, 5); ax.set_xlabel('x'); ax.set_ylabel('m(x, t)')\n",
|
|||
|
|
"ax.set_title('Pure-diffusion Fokker–Planck'); ax.grid(alpha=0.3); ax.legend()\n",
|
|||
|
|
"fig.tight_layout(); plt.show()\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "1b2ef52c",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"## 2-D Poisson eigenfunction\n",
|
|||
|
|
"\n",
|
|||
|
|
"$-\\Delta u = 2\\pi^2 \\sin(\\pi x)\\sin(\\pi y)$ on the unit square with zero Dirichlet boundary admits the exact solution $u(x,y) = \\sin(\\pi x)\\sin(\\pi y)$."
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 4,
|
|||
|
|
"id": "1f2b19fb",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:02.916630Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:02.915981Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:03.039124Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:03.037419Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"iterations = 690\n",
|
|||
|
|
"residual = 9.865621268811964e-07\n",
|
|||
|
|
"max error = 0.00013249923574043532\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"n = 65\n",
|
|||
|
|
"xs = np.linspace(0, 1, n); ys = np.linspace(0, 1, n)\n",
|
|||
|
|
"X, Y = np.meshgrid(xs, ys, indexing='ij')\n",
|
|||
|
|
"F = 2 * np.pi ** 2 * np.sin(np.pi * X) * np.sin(np.pi * Y)\n",
|
|||
|
|
"res = opt.poisson_2d_zero_boundary(F.flatten().tolist(), n, n)\n",
|
|||
|
|
"U = np.array(res['u']).reshape(n, n)\n",
|
|||
|
|
"U_exact = np.sin(np.pi * X) * np.sin(np.pi * Y)\n",
|
|||
|
|
"print('iterations =', res['iterations'])\n",
|
|||
|
|
"print('residual =', res['residual'])\n",
|
|||
|
|
"print('max error =', float(np.max(np.abs(U - U_exact))))\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 5,
|
|||
|
|
"id": "8ee99510",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:03.042379Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:03.042041Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:03.692138Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:03.690583Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABFYAAAGtCAYAAAA8rfAbAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAABDrAAAQ6wFQlOh8AADCDklEQVR4nOzde5xVdb0//tdae/ZcGGZgUMhBbooilHipRDEpuYf6NQTES6WmmMQROiBgUApm4OUg2rGDaJpmRRkXK00UIajjjzx0OXqMGFRQvKEhDjPCXPZlrd8fNDs26/Ue9po9wN4zr+fjMY/yw2d99tprr73Wms+s9Xo7vu/7EBERERERERGR0NyjvQIiIiIiIiIiIvlKEysiIiIiIiIiIi2kiRURERERERERkRbSxIqIiIiIiIiISAtpYkVEREREREREpIU0sSIiIiIiIiIi0kKaWBERERERERERaSFNrIiIiIiIiIiItJAmVkREREREREREWkgTKyIiIiIiIiIiLaSJFZEc16dPH5x//vmHbXzHcXDNNdcctvFFRERE8sljjz0Gx3GwYcOGVh/7zTffhOM4mD9/fquPLSJHjyZWJKf94x//wLe+9S0MHDgQ5eXlKCsrw4knnohLLrkEjzzyCF2mqqoK119/Pfr27Yvi4mJ06tQJgwYNwt133419+/YF+m/YsAGO46T9dOjQAQMHDsR3v/td1NfXH+63eVi9+eabmD9/Pl566aWjvSoiIiIibd5LL72E+fPn48033zzaqyIiR0jB0V4BEctbb72FQYMG4cMPP8SECRNw/fXXo7CwENu3b8cLL7yA++67D9ddd13aMo8++ihuuOEGdOjQAVdffTVOPfVU1NfXY+3atbj55pvxyCOPYPXq1TjxxBMDrzdhwgR86UtfAgDs2rULv/zlLzFv3jxs3LgRzz777BF5z4fDm2++idtuuw19+vTBGWecEfj3+vp6RCKRI79iIiIiIm3QSy+9hNtuuw3nn38++vTpk/ZvvXv3Rn19PQoK9GuYSFuib7TkrP/4j//ABx98gPvuuw/f/OY3A//+/vvvp/33hg0bMGnSJJx88slYt24djj/++NS/TZs2DStXrsRll12Giy++GH/+859RXFyctvzpp5+Or3zlK2nLDBo0CM899xz+8pe/4DOf+Uwrv8PccPB2EBERkdxQW1uL8vLy0P8Whu/72LdvHzp27Jj1WHJojuPo2kukDdKjQJKzXnvtNQDA8OHD6b8fd9xxaf89e/ZseJ6Hn//852mTKk3Gjx+PqVOnYvPmzfjRj350yNePRCIYOnRo2rocypYtW3DFFVegZ8+eKCoqQrdu3XDuuefi4YcfTuvX0NCA2267Df3790dxcTG6dOmC//f//h/+/Oc/Z/Q6Vi5K02NNjz32GABg/vz5qffwta99LfWo04GZLdZYP/vZz3D22WejtLQUpaWlOOecc/CLX/wi0K/przHvv/8+vvrVr+KYY45BSUkJPv/5z2f8fkRERNqiWCyGu+++G6eddhpKSkpQXl6OESNG4A9/+ENavwNzN1auXIlBgwahQ4cOuPjiiwH8K2/t//7v/3DhhReioqICnTp1Si1fVVWFyy+/HJ/4xCdQVFSEE088ETNnzkRtbW3a6zRlh6xduxZ33HEH+vXrh6KiIixatMh8D+eddx46depEH41+++23EYlE8NWvfjXV9uyzz2LYsGHo1q0biouL0aNHD4wZMwb//d//fcjtVVVVhX/7t3/Dqaeeik6dOqGkpAQDBw7EokWLkEwm6XtZv3497rvvvtR7OeGEE7B48eLA2Js2bcK1116LU045JXVtc9ZZZ+HRRx895Hr9+c9/huM4mD17Nv33adOmwXEc/O1vf8M111yDr33tawCAoUOHpq69mq61mstY+c1vfoMRI0agoqICxcXFOPHEEzFp0iR8+OGHh1xHETm6dMeK5Ky+ffsC2P94z1133dXsLZM7duzAn/70J5xzzjk488wzzX7f+MY3cN9992HFihWYMmXKIddh27ZtAIBjjjnmkH13796NoUOHwvM83HDDDTjhhBNQXV2NV155Bb///e8xadIkAEAymcQFF1yA9evX44ILLsCNN96I999/Hw888ADOO+88rF69OjUZkq1x48YhHo9j4cKF+PrXv44hQ4YAAD7xiU80u9ytt96K22+/HQMHDsS8efPg+z5++tOf4oorrsD27dsxd+7ctP779u3DkCFD8JnPfAa33347PvjgA9x7770YM2YMtm/fjrKyslZ5PyIiIvkikUjgggsuwO9//3tcccUVmDx5Murq6vDTn/4Uw4YNw69+9StcdNFFacv8+te/xn333YfJkyfj+uuvh+/7qX97++238YUvfAGXXHIJ7rjjjtSduy+99BI+//nPI5FIYMqUKTjxxBPxwgsv4J577sG6devw//1//x86dOiQ9jqzZs1CXV0drr76anTt2hU9e/Y038c111yD66+/HqtWrcKXv/zltH/78Y9/DM/zUpMGf/jDH3DRRRfhk5/8JGbNmoVjjjkG77//PjZu3Ij//d//TV2HWDZs2ID169fjoosuwgknnICGhgY888wzmDVrFrZv344lS5YElpk7dy5qa2vxta99DR07dsTjjz+Om266Cd27d8fll1+e6vfkk0/ib3/7GyZMmIDevXujpqYGv/zlL3Httddi165d5qQJAHz2s5/Fpz/9afz4xz/GggULEI1GU//W0NCAn/70pxg8eDBOPfVU3HDDDSgqKsJDDz2EuXPnYsCAAQD+dV1rabr26tu3L6ZOnYoePXrgrbfewlNPPYV33nkHxx57bLPLi8hR5ovkqG3btvmdOnXyAfjdunXzx48f7991113+Cy+84CeTybS+Tz31lA/Anzp16iHH7dixo3/MMcek/nv9+vU+AH/OnDn+rl27/F27dvl///vf/VtuucUH4Pfu3dtvbGw85Li//vWvfQD+L37xi2b7PfLIIz4A//rrr09r37p1q19UVOSffPLJae+vd+/e/he+8IW0vgD8q6++OjB203t59NFHm21rbqxXX33Vd13XP/300/19+/al2vfu3eufeuqpfiQS8d94441U+xe+8AUfgL9w4cK0cX/+85/7APwHH3yQbwgREZE27L777vMB+KtWrUprj8Vi/plnnumfcMIJqbY33njDB+AXFBT4r7zySmCs3r17+wD8Bx54IPBvQ4YM8R3H8V944YW09ttuu80H4N9+++2ptkcffdQH4Pft29f/+OOPM3ofNTU1focOHfwRI0YE/u2kk07ye/fu7Xue5/u+70+fPt0H4L///vsZjX2wvXv30vYrr7zSj0Qi/s6dO1NtTe/ltNNO8xsaGtLGOOaYY/zBgwcfcuxkMukPGTLE79Spkx+LxQJjr1+/PtX20EMP+QD8FStWpI3xk5/8JHCdxZZv0vRZz5s3L9W2adMmH4B/zjnnmOspIrlNjwJJzjrxxBPx8ssvY9q0aSgtLcXKlStx880347zzzsNJJ52ENWvWpPrW1NQAQNptsZZOnTql+h/ojjvuQNeuXdG1a1d88pOfxO23345Ro0Zh7dq1KCwsPOS4nTt3BgA888wz2LNnj9lv5cqVAIDbbrstrb1fv3648sor8dprr+GVV1455OsdLr/61a/geR5uvvnmtL9wlZaWYtasWUgmk/j1r3+dtozrupg+fXpa28iRIwEAr7766uFfaRERkRzzk5/8BH369MGQIUPw4Ycfpn5qampw8cUX44033gicIy+88EKceuqpdLwuXbrg+uuvT2vbtWsX/vu//xsjR47E5z73ubR/mzlzZur66WA33nhjxpkq5eXlGDduHH73u9/h7bffTrW/8MILeP3113HVVVfBcRwA/7oWWr58OeLxeEbjH6i0tDT1/xsbG/HRRx/hww8/xBe/+EUkk0n6iPGNN96IoqKitDEGDx4c2LYHjl1fX4/du3fjo48+whe/+EXU1NRg69atza7blVdeifLycvzwhz9Ma//hD3+ITp064bLLLgv1Xg/0s5/9DMD+a9ED17OJ6+pXNpFcp2+p5LTevXvj+9/
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1210x440 with 4 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"fig, axes = plt.subplots(1, 2, figsize=(11, 4))\n",
|
|||
|
|
"im0 = axes[0].imshow(U.T, origin='lower', extent=(0, 1, 0, 1), cmap='viridis')\n",
|
|||
|
|
"axes[0].set_title('SOR solution'); plt.colorbar(im0, ax=axes[0])\n",
|
|||
|
|
"im1 = axes[1].imshow((U - U_exact).T, origin='lower', extent=(0, 1, 0, 1), cmap='RdBu_r')\n",
|
|||
|
|
"axes[1].set_title('error vs analytic'); plt.colorbar(im1, ax=axes[1])\n",
|
|||
|
|
"fig.tight_layout(); plt.show()\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "f3b99b10",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"## 2-D HJB with quadratic terminal\n",
|
|||
|
|
"\n",
|
|||
|
|
"Heat-only relaxation ($H = 0$, σ² > 0) preserves a constant value, while a quadratic terminal $g(x) = ½(x²+y²)$ smooths."
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 6,
|
|||
|
|
"id": "0cf4e23e",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:03.695325Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:03.695038Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:03.772635Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:03.764763Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"V(0,0) = 0.018239022846662144\n",
|
|||
|
|
"V(±1,±1) = 0.8159229411398733 0.8159229411398735\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"res = opt.hjb_quadratic_2d(n_per_dim=21, x_min=-1.0, x_max=1.0,\n",
|
|||
|
|
" n_t=200, t_horizon=0.2, sigma_sq=0.1)\n",
|
|||
|
|
"ax_x = np.array(res['axis']); npd = res['n_per_dim']\n",
|
|||
|
|
"V = np.array(res['value']).reshape(npd, npd)\n",
|
|||
|
|
"print('V(0,0) =', V[npd // 2, npd // 2])\n",
|
|||
|
|
"print('V(±1,±1) =', V[0, 0], V[-1, -1])\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 7,
|
|||
|
|
"id": "764c58ae",
|
|||
|
|
"metadata": {
|
|||
|
|
"execution": {
|
|||
|
|
"iopub.execute_input": "2026-05-12T10:16:03.775694Z",
|
|||
|
|
"iopub.status.busy": "2026-05-12T10:16:03.775402Z",
|
|||
|
|
"iopub.status.idle": "2026-05-12T10:16:04.130581Z",
|
|||
|
|
"shell.execute_reply": "2026-05-12T10:16:04.128409Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 770x440 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"fig, ax = plt.subplots()\n",
|
|||
|
|
"im = ax.imshow(V.T, origin='lower', extent=(-1, 1, -1, 1), cmap='magma')\n",
|
|||
|
|
"ax.set_title('HJB value V(0, x, y) — quadratic terminal')\n",
|
|||
|
|
"plt.colorbar(im, ax=ax)\n",
|
|||
|
|
"fig.tight_layout(); plt.show()\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "3c13a863",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"**Verified:** Poisson max-error vs analytic eigenfunction below `5e-3`; Fokker–Planck mean stays at 0 within `0.05`."
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"metadata": {
|
|||
|
|
"kernelspec": {
|
|||
|
|
"display_name": "Python 3 (rhftlab)",
|
|||
|
|
"language": "python",
|
|||
|
|
"name": "python3"
|
|||
|
|
},
|
|||
|
|
"language_info": {
|
|||
|
|
"codemirror_mode": {
|
|||
|
|
"name": "ipython",
|
|||
|
|
"version": 3
|
|||
|
|
},
|
|||
|
|
"file_extension": ".py",
|
|||
|
|
"mimetype": "text/x-python",
|
|||
|
|
"name": "python",
|
|||
|
|
"nbconvert_exporter": "python",
|
|||
|
|
"pygments_lexer": "ipython3",
|
|||
|
|
"version": "3.11.13"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"nbformat": 4,
|
|||
|
|
"nbformat_minor": 5
|
|||
|
|
}
|