diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..dd356787 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "python.formatting.provider": "none", +} \ No newline at end of file diff --git a/README.md b/README.md index 4b531042..f011044a 100644 --- a/README.md +++ b/README.md @@ -95,4 +95,16 @@ To test the trained policy, you can input the policy checkpoint into the trainin python train_shac.py --cfg ./cfg/shac/ant.yaml --checkpoint ./logs/Ant/shac/policy.pt --play [--render] ``` -The `--render` flag indicates whether to export the video of the task execution. If does, the exported video is encoded in `.usd` format, and stored in the `examples/output` folder. To visualize the exported `.usd` file, refer to [USD at NVIDIA](https://developer.nvidia.com/usd). \ No newline at end of file +The `--render` flag indicates whether to export the video of the task execution. If does, the exported video is encoded in `.usd` format, and stored in the `examples/output` folder. To visualize the exported `.usd` file, refer to [USD at NVIDIA](https://developer.nvidia.com/usd). + +To install Omniverse, follow the [Omniverse Install Page](https://www.nvidia.com/en-us/omniverse/download/) + +- Install [USD Composer](https://www.nvidia.com/en-us/omniverse/apps/create/) +- Run Create using: +```$ MESA_GL_VERSION_OVERRIDE=4.6 {OMNI_CREATE_PATH}/omni.create.singlegpu.sh``` where the `OMNI_CREATE_PATH` is `~/.local/share/ov/pkg/create-2022.2.2` updated to your version + + +## Debugging + +If you're getting missing cuda libs while building dflex kernels, then do `ln -s $CONDA_PREFIX/lib $CONDA_PREFIX/lib64` + diff --git a/ball_env/ball_env_analysis.ipynb b/ball_env/ball_env_analysis.ipynb new file mode 100644 index 00000000..9be08971 --- /dev/null +++ b/ball_env/ball_env_analysis.ipynb @@ -0,0 +1,2193 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 17, + "id": "65c15b4a", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.patches as patches\n", + "from numpy.linalg import norm\n", + "import seaborn as sns\n", + "sns.set()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "35916a70", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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WrUKK1asaPvabDYzwBARUb8QFRWFoqKiTv9x7056vb7T41Z7i1vDi16vh0ql6vAuS3V1dYd3V64zGo2drler1QgJCemwXqvVQqvV9lzRREREMhIVFdWnQaIvuPXPRl5eXoiPj0dmZma77ZmZmRg3blyn+yQmJnZY/+WXX2L06NHQaHjlVSIiov7O7WcbrVixAh9++CE++ugjFBQUYPny5SgrK8PixYsBXP2zz9NPP922fvHixSgtLcWKFStQUFCAjz76CNu2bcOLL77o7lKJiIhIBtx+zMucOXNQW1uL1157DZWVlYiNjcW+ffswcOBAAEBlZSXKysra1kdHR2Pfvn1Yvnw5PvjgA0RERODdd9/lZ7wQERERAEAhrh8N6yHMZjMkSYLJZEJAQEBfl0NERETd4MrrN68qTURERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESywvBCREREssLwQkRERLLC8EJERESy4tbwUldXh5SUFEiSBEmSkJKSgvr6+i73eeaZZ6BQKNrdEhIS3FkmERERyYjanQ8+b948XLhwAfv37wcALFy4ECkpKfj888+73C85ORkZGRltX3t5ebmzTCIiIpIRt4WXgoIC7N+/H7m5uXjooYcAAL/5zW+QmJiIoqIiDB069Kb7arVaGI1Gd5VGREREMua2Pxvl5ORAkqS24AIACQkJkCQJhw8f7nLfrKwsGAwGxMTEIC0tDdXV1Tdda7FYYDab292IiIjIc7ktvFRVVcFgMHTYbjAYUFVVddP9pk6diu3bt+PAgQNYv349jh07hkmTJsFisXS6fu3atW3H1EiShMjIyB7r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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ax = plt.gca()\n", + "xy = np.load(\"xy.npy\")\n", + "for n in range(xy.shape[1]):\n", + " plt.plot(xy[:, n, 0], xy[:, n, 1])\n", + "\n", + "# add wall\n", + "ax.add_patch(patches.Rectangle((1.75, 0), 0.25, 2.0, linewidth=1, edgecolor='black', facecolor='black'))\n", + "\n", + "# add target\n", + "ax.add_patch(patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue'))\n", + "\n", + "# ball\n", + "ax.add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "\n", + "plt.axis('equal')\n", + "plt.savefig(\"bounce_example.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "b2f4a329", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'norm of dynamics Jacobian')" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Jacobian analysis\n", + "jacs = np.load(\"jacs.npy\")\n", + "H = jacs.shape[0]\n", + "jacs.shape\n", + "\n", + "jac_norms = norm(jacs, axis=(2,3)).mean(axis=1)\n", + "plt.plot(np.arange(H)/8, jac_norms, label=\"full\")\n", + "\n", + "plt.legend()\n", + "plt.xlabel(\"H\")\n", + "plt.ylabel(\"norm of dynamics Jacobian\")" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "5781f035", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(73, 94), (201, 236)]" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# compute contact patches\n", + "contact_idx = np.arange(H)[jac_norms > np.median(jac_norms)]\n", + "# contact_idx = contact_idx/8\n", + "changes = np.arange(len(contact_idx)-1)[np.diff(contact_idx) > 1]\n", + "contact_ranges = []\n", + "start = contact_idx[0]\n", + "for each in changes:\n", + " end = contact_idx[each]\n", + " contact_ranges.append((start, end)) \n", + " start = contact_idx[each+1]\n", + "contact_ranges.append((start, contact_idx[-1]))\n", + "contact_ranges" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "6aa2f6a0", + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "too many indices for array: array is 2-dimensional, but 3 were indexed", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [70]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 45\u001b[0m ax[\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mset_xlabel(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mH\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 47\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(xy\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m1\u001b[39m]):\n\u001b[0;32m---> 48\u001b[0m ax[\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m1\u001b[39m]\u001b[38;5;241m.\u001b[39mplot(\u001b[43mxy\u001b[49m\u001b[43m[\u001b[49m\u001b[43m:\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m, xy[:, n, \u001b[38;5;241m1\u001b[39m])\n\u001b[1;32m 50\u001b[0m \u001b[38;5;66;03m# add wall\u001b[39;00m\n\u001b[1;32m 51\u001b[0m rect \u001b[38;5;241m=\u001b[39m patches\u001b[38;5;241m.\u001b[39mRectangle((\u001b[38;5;241m1.75\u001b[39m, \u001b[38;5;241m0\u001b[39m), \u001b[38;5;241m0.25\u001b[39m, \u001b[38;5;241m1.0\u001b[39m, linewidth\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m, edgecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mblack\u001b[39m\u001b[38;5;124m'\u001b[39m, facecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mblack\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;31mIndexError\u001b[0m: too many indices for array: array is 2-dimensional, but 3 were indexed" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsEAAAHaCAYAAADhSJSxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADRWklEQVR4nOzdd3hUVfrA8e+905PJpCckdAKhd+lNERT7igVcC3bF7sra9rfqKq4N11Wxgwo2XF3UtSEoCoogivTeS0hvk5lMvff8/phkICaBAEkmIefzPPPMzJ1bzr0zc+edc99zjiKEEEiSJEmSJElSC6JGugCSJEmSJEmS1NhkECxJkiRJkiS1ODIIliRJkiRJklocGQRLkiRJkiRJLY4MgiVJkiRJkqQWRwbBkiRJkiRJUosjg2BJkiRJkiSpxZFBsCRJkiRJktTiyCBYkiRJkiRJanGMkS6A1Djmz5/PAw88UONr1157Lffdd98JrSs+Pp4uXbpw7bXXctppp1VbZv/+/cyZM4dly5aRnZ2NpmkkJyczYMAALr74YoYMGYKiKLVu85dffuGqq67i+eefZ8KECUcs34svvsjMmTPZunVrnfepqfF4PMyaNYvBgwczZMiQo85/5ZVXsnLlyiPOM3jwYN55551jKsf999/PJ598En6uqirJycn079+fW2+9lczMzGrL/Pbbb7z//vv8/vvvFBQUYDQaad26NaNGjeKSSy4hIyPjmMogSc3Nli1bmDt3LitXriQvLw+AVq1aMXToUC655BJ69+7dKOWo6Vw4duxYBg8ezJNPPtlg2/39999ZtmwZU6ZMweFwNNh2jsfJ8Psg1R8ZBLcwTzzxBJ06daoyLSUl5YTWJYSgoKCAd999l5tvvplXXnmFsWPHhuf77rvvmDZtGvHx8UyePJkePXpgNpvZu3cv33zzDVOmTOHtt99m2LBhJ7RvlS655BJGjRpVL+uKFI/Hw8yZM7ntttvqFAQ//PDDuFyuGl977bXXWLx4MePGjTuuslitVubMmQNAMBhk3759vPLKK0yePJmvv/6a1NTU8LzPPfccr776Kv3792fq1Km0b98eTdPYunUrn3zyCW+99RabNm3CYDAcV1kkqambN28ejz32GB07duSqq66iS5cuAOzatYsvvviCiy++mEWLFtGuXbuIlG/mzJnY7fYG3cbq1auZOXMmF154YZMLgk+G3wep/sgguIXp0qVLvdVC/HFdo0aNYtCgQXz55ZfhIHjfvn3cc889dO7cmbfffrvKyXfw4MFccskl/PLLL8TGxtZLmSBU49KqVat6W19jEkLg8/mOebnOnTvXOH3hwoV8//33nHvuuUyZMuW4yqSqKv369Qs/P+WUU0hLS+Pqq6/mhx9+YNKkSQB88cUXvPrqq0yePJlHHnmkSs3+iBEjuOaaa3j//fePqwyS1BysWrWKf/zjH4wZM4YXXngBs9kcfm3YsGFcfvnlfP3111gsliOux+PxYLPZGqSMPXr0aJD1NnWVx7Q5/z5I9U/mBEtVfPfdd0yaNIm+ffvSv39/rrnmGlavXl2nZS0WCyaTCaPx0H+rt956C4/Hw8MPP1xr7cOQIUPo1q1bnbbh8/l44oknGDFiBH369OGKK65g06ZNVeZ58cUX6dq1a5VpX331Fddeey0jR46kT58+nHXWWcyYMYPy8vIq8+3fv5+7776bkSNH0qtXL4YPH86UKVPYvHlzrWX64Ycf6Nq1K+vWrQtP++abb+jatSs33nhjlXnPO+88br/99vDzrl278uijj/LBBx9w1lln0bt3bz755JNwrfjMmTPp2rUrXbt25f7776/TMaq0Y8cO7rvvPjIzM5k+fXqV13Rd54033mDChAn06tWLYcOGce+995KTk1OndcfExABUea9feeUV4uPjefDBB2tMbVEUhcsvv1zWAksnrddeew2DwcCjjz5aJQA+3FlnnVXl6sn9999P//792bp1K9deey39+/fn6quvBmDZsmVMnTqV0aNH07t3b8aPH89DDz1EUVFRtfX+8MMPXHDBBfTq1YuxY8cye/bsGrc/duzYaucSl8vFU089xdixY+nVqxejRo3i8ccfr3Z+rDxfffrpp5x11ln07duX888/n++//z48z4svvsjTTz8NwOmnnx4+f/3yyy81luftt9+ma9eu7N27t9przzzzDL169Qrvb12PR+VvwMaNG7njjjsYNGgQ48ePr/La4er6+1D5Xu3du5cbbriB/v37M2bMGJ588kn8fn+Vef1+PzNnzgyf14cMGcKVV17J77//Hp5HCMF7773HBRdcQJ8+fRg0aBB33HEH+/fvr/FYSfVP1gS3MLquEwwGq0yrDGQ+//xzpk2bxsiRI3n22Wfx+/3MmjWLK6+8krfffptTTjmlxnUJISgsLGTWrFl4PB7OPffc8Dw///wzycnJ9Vb7/Nxzz9GjRw+mT59OWVkZM2fO5Morr+TTTz+lbdu2tS63Z88eRo8ezZQpU7DZbOzatYs33niDdevWMXfu3PB8N9xwA7qu89e//pX09HSKi4tZvXo1Tqez1nUPGjQIk8nE8uXL6dOnT3i/rVYrv/76K4FAAJPJRGFhIdu3b+eyyy6rsvy3337Lb7/9xq233kpSUhJxcXHMmjWL66+/nosvvphLLrkEgISEhDofp7KyMm699VaMRiMzZ86sVqv0yCOP8OGHH3LFFVdw6qmnkpWVxfPPP8/KlSuZP39+tW1VfmY0TWPv3r08/fTTxMbGcuqppwKQm5vLjh07OPfcc49ayyVJJyNN0/jll1/o1avXMaeYBQIBpk6dyuTJk7nhhhvQNA0IXUnr378/l1xyCTExMWRlZfHWW2/x5z//mc8//xyTyQTA8uXLueWWW+jXrx/PPfccmqYxa9YsCgsLj7ptj8fDFVdcQU5ODjfffDNdu3Zl+/btvPDCC2zbto233367yp/aH374gfXr13PHHXcQFRXFrFmzuO2221iwYAFt27blkksuobS0lHfeeYeZM2eSnJwM1H616vzzz2fGjBnMnz+fu+++u8rx/N///sdpp50WPh/V9XhUuv322zn77LOZPHlytYD2cHX9fTj8vbr44ou59tpr+fXXX3n55Zex2+3cdtttQOh8ef3117Nq1Squuuoqhg4diqZprF27luzs7PC6HnroIT755BOuvPJKpk2bRmlpKS+99BKTJ0/ms88+Iykp6ajvn3SChNQi/Pe//xWZmZk13gKBgNA0TYwcOVKce+65QtO08HIul0sMGzZMTJo06ajr6tWrl3jvvfeqbLd3797i0ksvrVYeTdNEIBAI3w7fZk1WrFghMjMzxYUXXih0XQ9PP3DggOjZs6f429/+Fp72wgsviMzMzFrXpeu6CAQCYuXKlSIzM1Ns3rxZCCFEUVGRyMzMFG+//fYRy1KTyy67TFx11VXh5+PHjxdPPfWU6Natm1i5cqUQQoj//e9/IjMzU+zevTs8X2Zmphg4cKAoKSmpsr7CwkKRmZkpXnjhhWMui67r4qabbhLdunUTP/zwQ7XXd+zYITIzM8UjjzxSZfratWtFZmam+Ne//hWedt9999X4Xo8YMUL89ttv4fnWrFkjMjMzxYwZM6ptLxgMVnmvD3//JOlkkZ+fLzIzM8Xdd99d7bUjfQcqv2Mff/zxEddfed7KysoSmZmZ4ttvvw2/dskll4iRI0cKr9cbnlZWViYGDx5c7Vx42mmnifvuuy/8/LXXXhPdunUT69atqzLfggULRGZmZpVzSGZmphg+fLgoKyurst/dunUTr732WnjarFmzRGZmpti/f/8R96nSbbfdJkaPHl3ld+CHH34QmZmZYvHixcd8PCp/A55//vlqyx3v74MQh96rr776qsoyN9xwgzjzzDPDzz/55BORmZkp/vOf/9S6ndWrV4vMzEzx5ptvVpmenZ0t+vTpI55++ulal5Xqj6wJbmGeeuqpaq3zjUYjO3fuJC8vjylTpqCqh7JkoqOjOeOMM/jwww+r5akdvq7i4mK+/fZbHn30UXRd54orrjhiOW677Ta+++678PPLL7+chx566KjlP/fcc6vUSrRu3Zr+/fvXeqmt0v79+/n3v//NihUrKCwsRAgRfm3Xrl1069aNuLg42rVrx+zZs9F1PZymcfjxqM2wYcN4/fXX8Xq9FBYWsnfvXs455xxWrFjBsmXLGDRoED///DPp6el06NChyrJDhw6t15zoF198ke+//5477riDMWPGVHu98lhdeOGFVab36dOHjIwMli9fXqVGxmq18u677wKhy3e5ubnMnTuXG2+8kVmzZtG/f/8jlmfIkCGUlZWFn9elhw9JOplMnDiRLVu2hJ/fe++9XHfddVXmOfPMM6stV1hYyPPPP8+SJUvIy8tD1/Xwazt37uT000+nvLyc9evX8+c//7nKVRi73c5pp51WpXeXmnz//fd06dKF7t27V7lKOHLkSBRFYeXKlVXOI0OGDKmS2paUlERiYiJZWVl1OBI1mzhxIgsXLuTnn39m5MiRQKgXouTkZEaPHn1Mx+NwZ5xxRp22X5ffh0qKolRp+A2hNJEVK1aEn//4449YLBYuuuiiWrf5/fffoygK559/fpXjnpSURLdu3Y7a249UP2QQ3MJkZGTUmJpQXFwMEL50dbiUlBR0XcfpdFYJgv+4rtGjR5OVlcUzzzzD+eefj8PhIC0trcaT4/3338/UqVMBuPjii+tc/pouDyUlJVX5gfkjt9sd/oG466676NChA1arlZycHG677Ta8Xi8QOrm9/fbbvPTSS8yaNYsnn3ySuLg4zjvvPO66664jtqgeNmwYM2fOZNWqVRw8eJD4+Hh69OjBsGHDWL58OXfddRcrVqyosQeMmo758fruu+94+eWXOe2007jllltqnKekpASouVeQlJQUDh48WGWaqqrVPjMjR47k1FNP5cknn+TDDz8MNzT547IA77zzDsFgkI0bN/Lwww8fz25JUpMXHx+P1Wqt8Tvw7LPP4vF4yM/PD5/3Dmez2aqdX3Rd59prryUvL49bbrmFzMxMbDYbQgguvfTScANap9OJruu1nhuPpvJPe8+ePWt8vfK3oVJcXFy1ecxm83E16K00evRokpOTmT9/PiNHjqS0tJTFixdz1VVXhdsQ1PV4HK4uaSl1/X2oZLPZqqV8/XH/i4qKSElJOWIFSmWwPXz48BpfP1J6n1R/ZBAsAaETOEB+fn611/Ly8lBVtU5d3XTt2pWffvqJPXv20KdPH0aMGMF7773H+vXrqwRSx9s9UEFBQY3TajoxV1qxYgV5eXm88847DB48ODz98NrJSq1bt+af//wnALt37+brr79m5syZ+P1+Hn300Vq30bdvX6Kiovj555/Jyspi2LBhKIrCsGHDeOutt1i3bh0HDx6s8YR3pP6Rj8WuXbu49957ad++Pc8880yt6608Vnl5edVaSefl5YU/C0dis9lo27Zt+M9HamoqXbp0YdmyZfh8vio/Et27dwc4Yk6eJDV3BoOBoUOHsmzZMvLy8qoEYJX5sAcOHKhx2Zq+q9u2bWPLli08+eSTVa7a/LEBmcPhQFGUWs+NRxMfH4/FYgmf92p6vaEZDAYuuOAC3nnnHZxOJ1988QV+v5+JEyeG56nr8ThWx/L7UFcJCQmsWrUKXddrDYTj4+NRFIX33nuvxkaUtTWslOqX7B1CAqBjx46kpqbyxRdfVLkUVF5ezsKFC+nXr1+duuypDIoqT5xXX301NpuNRx99tNZ+bI/FH8uXlZXF6tWrq5y8/qjyB+aPJ5V58+YdcVsdO3YM1zj8sQeKPzKZTOGUhxUrVoSD3VNOOQWDwcDzzz8fDorrorKsf6yFqI3L5eK2225D13VmzpwZ7r2hJkOHDgXgf//7X5Xp69atY+fOneHXj8TtdrNv3z4SExPD026++WaKi4t54oknqrxHktRS3HjjjWiaxsMPP0wgEDihddX1vBUVFUWfPn1YuHBhldpIl8tVpdeG2px66qns37+fuLg4evfuXe3Wpk2bYy57ZZmPpXZ44sSJ+Hw+vvjiC+bPn0///v2rpO4d73n8aBpivaNGjcLn8zF//vxa5zn11FPD6WU1Hfc/9mAhNQxZEywBoUvef/3rX5k2bRo33XQTkyZNwu/3M3v2bJxOJ/fcc0+1ZbZv3x5uxVxSUsLChQtZtmwZ48ePD1/KadeuHc8++yz33HMP559/PpMnT6Znz56YzWYKCwtZtmwZQJ07by8qKuLWW2/l0ksvpaysjBdffBGz2cxNN91U6zL9+/cnNjaWhx9+mNtuuw2j0cjnn39ebcSgLVu28NhjjzFhwgTat2+PyWRixYoVbN26tVpXZzUZNmxYeBSmyiDYarXSv39/fvrpJ7p27VolaDwSu91O69at+e677xg2bBixsbHEx8fX+oN03333sXPnTq699lrcbjdr1qypNo/ZbKZHjx506tSJSZMm8e6776KqajiN5fnnnw/3/3s4XdfD69N1ndzcXN555x1KS0vDraEhlK+9fft2Xn31VbZs2cLEiRNp3749uq6Tk5PDZ599BoTyzCXpZDRw4EAeeughpk+fzsSJE7n00kvp3LkzqqqSn5/PwoULgbqd7zp16hQ+fwohiI2N5fvvvw+fMw935513cv3113PNNddw7bXXomkab7zxBjabLZz+VJspU6awcOFCrrjiCq6++mq6du2KrutkZ2fz008/ce2119K3b99jOg6VI0nOmTOHCy+8EKPRSMeOHY+43xkZGfTv35/XX3+d7OxsHnvsseM+Hseirr8Px+Lcc89l/vz5PPLII+zevZshQ4YghGDt2rVkZGRwzjnnMHDgQCZNmsSDDz7Ihg0bGDRoEDabjfz8fFatWkVmZiZ//vOfT2jfpKOTQbAUdt5552Gz2Xj99de5++67MRgM9O3bl7lz5zJgwIBq8x8+dHJMTAxt2rThgQceqPbFPf300/n888+ZM2cO8+fP56WXXkLX9XDXaS+99FK1Bg21ufvuu1m/fj0PPPAALpeLPn368K9//euI6RXx8fG89tprPPXUU/z1r3/FZrNx+umn89xzz1W5rJacnEy7du14//33w/3ltm3blvvuu48rr7zyqGWrrOXt0KEDrVu3Dk8fPnw4v/zyS625X7V5/PHHefrpp5k6dSp+v58LL7yw1qFOv/32WwDefPNN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dYZJsA9Onjwp/sd//Ic4ZswYceLEieKLL74Y8ZNs++v06dPisGHDOvxTUVHhPu6zzz4Tf/azn4kjR44Ub7nlFvHtt98O46gDZ8qUKYqOf/PmzeLMmTPFcePGiWPHjhVnzJghvvrqq+5fAFzkGn9HOkqCRVH+58CX696TTz4pDhs2zOuxH374oXjbbbeJI0eOFH/605+Ke/bsCdWwJfEnRtf7oaM/U6ZMCXUIXerJa3jlMZGWBItiz+L7+9//Lt5xxx3iyJEjxUmTJom/+c1vRIvFEqqh+8zfGGtra8Wnn35anDJlipiVlSXedttt4rp168SmpqZQDr9bvuQcwbzOCKIYYTMnExEREREFGXuCiYiIiEhxmAQTERERkeIwCSYiIiIixWESTERERESKwySYiIiIiBSHSTARERERKQ6TYCIiIiJSHCbBRF348MMPMXz4cBw+fLjD/Q899FDELCNKREREvmMSTERERESKwySYiIiIiBSHSTARERERKY4m3AMgigYOhwM2m81ruyiKYRgNERER9RSTYCIf3HPPPZ3u69evXwhHQkRERIHAJJjIB//5n/+J9PR0r+1FRUX44YcfwjAiIiIi6gkmwUQ+SE9PR1ZWltf2hIQEJsFERERRiDfGEREREZHiMAkmIiIiIsVhEkxEREREisMkmIiIiIgURxA50SkRERERKQwrwURERESkOEyCiYiIiEhxmAQTERERkeIwCSYiIiIixWESTERERESKwySYiIiIiBSHSTARERERKQ6TYCIiIiJSHCbBRERERKQ4TIKJiIiISHGYBBMRERGR4jAJJiIiIiLF+f8B9EzDwWF1yZcAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Correlation between stiffness and bias/variance\n", + "def norm_variance(arr: np.ndarray):\n", + " assert len(arr.shape) == 3, arr.shape\n", + " return np.mean(\n", + " norm(arr - np.mean(arr, axis=1, keepdims=True), ord=2, axis=-1) ** 2,\n", + " axis=1,\n", + " )\n", + "\n", + "data = np.load(\"outputs/grads/bounce_grads_40.npz\")\n", + "fobgs = data[\"fobgs\"]\n", + "zobgs = data[\"zobgs\"]\n", + "loss = data[\"losses\"]\n", + "baseline = data[\"baseline\"]\n", + "hh = np.arange(fobgs.shape[0])\n", + "m=data['m']\n", + "N = fobgs.shape[1]\n", + "std = data['std']\n", + "\n", + "f, ax = plt.subplots(2, 2, figsize=(8, 5))\n", + "diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + "bias_l2 = norm(diff, ord=2, axis=-1)\n", + "bias_l1 = norm(diff, ord=1, axis=-1)\n", + "ax[0, 0].plot(hh, bias_l2, label=\"L2 Bias\")\n", + "ax[0, 0].plot(hh, bias_l1, label=\"L1 Bias\")\n", + "for (start, end) in contact_ranges:\n", + " ax[0, 0].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + "ax[0, 0].set_title(\"FoBG bias wrt ZoBG\")\n", + "ax[0, 0].legend()\n", + "ax[0, 0].set_xlabel(\"H\")\n", + "# ax[0, 0].set_yscale(\"log\")\n", + "\n", + "ax[0, 1].plot(hh, norm_variance(zobgs), label=\"ZoBGs\")\n", + "ax[0, 1].plot(hh, norm_variance(fobgs), label=\"FoBGs\")\n", + "ax[0, 1].plot(hh, hh**3 * m / (N * std**2), label=\"Lemma 3.10\")\n", + "for (start, end) in contact_ranges:\n", + " ax[0, 1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + "ax[0, 1].set_yscale(\"log\")\n", + "ax[0, 1].set_xlabel(\"H\")\n", + "ax[0, 1].set_title(\"Gradient variance\")\n", + "ax[0, 1].legend()\n", + "\n", + "\n", + "ax[1, 0].plot(np.arange(H)/8, jac_norms)\n", + "ax[1, 0].set_ylabel(\"f grad norm\")\n", + "ax[1, 0].set_xlabel(\"H\")\n", + "\n", + "for n in range(xy.shape[1]):\n", + " ax[1,1].plot(xy[:, n, 0], xy[:, n, 1])\n", + "\n", + "# add wall\n", + "rect = patches.Rectangle((1.75, 0), 0.25, 1.0, linewidth=1, edgecolor='black', facecolor='black')\n", + "ax[1,1].add_patch(rect)\n", + "\n", + "# add target\n", + "rect = patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue')\n", + "ax[1,1].add_patch(rect)\n", + "\n", + "ax[1,1].axis('equal')\n", + "ax[1,1].set_xlim((-3, 3))\n", + "\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"outputs/bias_var_with_contact.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "5baede07", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Correlation between stiffness and bias/variance\n", + "def norm_variance(arr: np.ndarray):\n", + " assert len(arr.shape) == 3, arr.shape\n", + " return np.mean(\n", + " norm(arr - np.mean(arr, axis=1, keepdims=True), ord=2, axis=-1) ** 2,\n", + " axis=1,\n", + " )\n", + "\n", + "import seaborn as sns\n", + "sns.set()\n", + "\n", + "data = np.load(\"outputs/grads/bounce_grads_40.npz\")\n", + "fobgs = data[\"fobgs\"]\n", + "zobgs = data[\"zobgs\"]\n", + "loss = data[\"losses\"]\n", + "baseline = data[\"baseline\"]\n", + "hh = np.arange(fobgs.shape[0])\n", + "m=data['m']\n", + "N = fobgs.shape[1]\n", + "std = data['std']\n", + "\n", + "f, ax = plt.subplots(3, 1, figsize=(3, 6))\n", + "\n", + "ldata = np.load(\"landscapes_0.1.npy\", allow_pickle=True)\n", + "xy = ldata[0]['trajectories'][0]\n", + "\n", + "for n in range(xy.shape[1]):\n", + " ax[0].plot(xy[:, n, 0], xy[:, n, 1])\n", + "\n", + "# add wall\n", + "rect = patches.Rectangle((1.75, 0), 0.25, 1.3, linewidth=1, edgecolor='black', facecolor='black')\n", + "ax[0].add_patch(rect)\n", + "\n", + "# add target\n", + "rect = patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue')\n", + "ax[0].add_patch(rect)\n", + "ax[0].add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "ax[0].axhline(0, c='black')\n", + "\n", + "ax[0].axis('equal')\n", + "# ax[0].set_xticks([])\n", + "# ax[0].set_yticks([])\n", + "\n", + "diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + "bias_l2 = norm(diff, ord=2, axis=-1)\n", + "bias_l1 = norm(diff, ord=1, axis=-1)\n", + "ax[1].plot(hh, bias_l2, label=\"L2 Bias\")\n", + "# ax[0, 0].plot(hh, bias_l1, label=\"L1 Bias\")\n", + "for (start, end) in contact_ranges:\n", + " ax[1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + "ax[1].set_ylabel(\"First-order bias\")\n", + "# ax[0, 0].legend()\n", + "ax[1].set_xlabel(\"H\")\n", + "# ax[0, 0].set_yscale(\"log\")\n", + "\n", + "# ax[0, 1].plot(hh, norm_variance(zobgs), label=\"ZoBGs\")\n", + "# ax[0, 1].plot(hh, norm_variance(fobgs), label=\"FoBGs\")\n", + "# ax[0, 1].plot(hh, hh**3 * m / (N * std**2), label=\"Lemma 3.10\")\n", + "# for (start, end) in contact_ranges:\n", + "# ax[0, 1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + "# ax[0, 1].set_yscale(\"log\")\n", + "# ax[0, 1].set_xlabel(\"H\")\n", + "# ax[0, 1].set_title(\"Gradient variance\")\n", + "# ax[0, 1].legend()\n", + "\n", + "\n", + "ax[2].plot(np.arange(H)/8, jac_norms)\n", + "ax[2].set_ylabel(r\"$||\\nabla f ||$\")\n", + "ax[2].set_xlabel(\"H\")\n", + "\n", + "# for n in range(xy.shape[1]):\n", + "# ax[1,1].plot(xy[:, n, 0], xy[:, n, 1])\n", + "\n", + "# # add wall\n", + "# rect = patches.Rectangle((1.75, 0), 0.25, 1.0, linewidth=1, edgecolor='black', facecolor='black')\n", + "# ax[1,1].add_patch(rect)\n", + "\n", + "# # add target\n", + "# rect = patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue')\n", + "# ax[1,1].add_patch(rect)\n", + "\n", + "# ax[1,1].axis('equal')\n", + "# ax[1,1].set_xlim((-3, 3))\n", + "\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"outputs/ball_bias_and_stiffness.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "dd2ecab1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[-0.5 , 1. , 5. , -5. ],\n", + " [-0.5 , 1. , 5.2049813 , -4.999932 ],\n", + " [-0.5 , 1. , 5.078213 , -5.07909 ],\n", + " ...,\n", + " [-0.5 , 1. , 4.7166967 , -4.864081 ],\n", + " [-0.5 , 1. , 5.0760508 , -5.174295 ],\n", + " [-0.5 , 1. , 4.9392433 , -4.8431816 ]],\n", + "\n", + " [[-0.48958334, 0.98954076, 5. , -5.0204306 ],\n", + " [-0.48915628, 0.98954093, 5.2049813 , -5.0203624 ],\n", + " [-0.48942038, 0.989376 , 5.078213 , -5.0995207 ],\n", + " ...,\n", + " [-0.49017355, 0.98982394, 4.7166967 , -4.8845115 ],\n", + " [-0.48942488, 0.98917764, 5.0760508 , -5.1947255 ],\n", + " [-0.4897099 , 0.98986745, 4.9392433 , -4.863612 ]],\n", + "\n", + " [[-0.4791667 , 0.97903895, 5. , -5.040861 ],\n", + " [-0.47831255, 0.9790393 , 5.2049813 , -5.040793 ],\n", + " [-0.47884077, 0.97870946, 5.078213 , -5.1199512 ],\n", + " ...,\n", + " [-0.4803471 , 0.9796053 , 4.7166967 , -4.904942 ],\n", + " [-0.47884977, 0.97831273, 5.0760508 , -5.215156 ],\n", + " [-0.47941983, 0.97969234, 4.9392433 , -4.8840427 ]],\n", + "\n", + " ...,\n", + "\n", + " [[ 0.78539014, 1.8144109 , -4.468969 , 1.2254395 ],\n", + " [ 0.67556524, 1.8114289 , -4.6194615 , 1.220969 ],\n", + " [ 0.73402846, 1.838152 , -4.57442 , 1.2555249 ],\n", + " ...,\n", + " [ 0.9396887 , 1.7915875 , -4.254525 , 1.2221131 ],\n", + " [ 0.741359 , 1.8756628 , -4.5435767 , 1.3044019 ],\n", + " [ 0.82470906, 1.7670962 , -4.386485 , 1.1741996 ]],\n", + "\n", + " [[ 0.7760798 , 1.8169214 , -4.468969 , 1.205009 ],\n", + " [ 0.66594136, 1.81393 , -4.6194615 , 1.2005384 ],\n", + " [ 0.7244984 , 1.8407252 , -4.57442 , 1.2350943 ],\n", + " ...,\n", + " [ 0.9308251 , 1.794091 , -4.254525 , 1.2016826 ],\n", + " [ 0.7318932 , 1.8783377 , -4.5435767 , 1.2839713 ],\n", + " [ 0.81557053, 1.7694999 , -4.386485 , 1.153769 ]],\n", + "\n", + " [[ 0.7667694 , 1.8193892 , -4.468969 , 1.1845784 ],\n", + " [ 0.6563175 , 1.8163886 , -4.6194615 , 1.1801078 ],\n", + " [ 0.7149683 , 1.8432558 , -4.57442 , 1.2146637 ],\n", + " ...,\n", + " [ 0.92196155, 1.796552 , -4.254525 , 1.181252 ],\n", + " [ 0.72242737, 1.8809701 , -4.5435767 , 1.2635407 ],\n", + " [ 0.806432 , 1.7718611 , -4.386485 , 1.1333385 ]]],\n", + " dtype=float32)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ldata[0]['trajectories'][0]" + ] + }, + { + "cell_type": "markdown", + "id": "6f87f860", + "metadata": {}, + "source": [ + "## Takeways:\n", + "- different type of contact have different stiffness and thus bias\n", + "\n", + "# Exploring how different contact approximation affects jacobians\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "184bb658", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 ke:10000.0 kf:1.0 kd:10.0 mu:0.9 margin:10.0 \n", + "1 ke:30000.0 kf:3.0 kd:30.0 mu:0.9 margin:10.0 \n", + "2 ke:50000.0 kf:5.0 kd:50.0 mu:0.9 margin:10.0 \n", + "3 ke:100000.0 kf:10.0 kd:100.0 mu:0.9 margin:10.0 \n" + ] + }, + { + "data": { + "text/plain": [ + "(-2.0949999999999998, 2.195, -0.1, 2.1)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "data = np.load(\"jacobians.npy\", allow_pickle=True)\n", + "\n", + "f, ax = plt.subplots(1, 2, figsize=(8, 3))\n", + "max_jac_norm = []\n", + "\n", + "for i in range(len(data)):\n", + " print(f\"{i} ke:{data[i]['soft_contact_ke']} kf:{data[i]['soft_contact_kf']} kd:{data[i]['soft_contact_kd']} mu:{data[i]['soft_contact_mu']} margin:{data[i]['soft_contact_margin']} \")\n", + " xyz = data[i]['trajectories'].mean(axis=1)\n", + " ax[0].plot(xyz[:, 0], xyz[:, 1], label=i)\n", + " \n", + " jacs = data[i][\"jacobians\"]\n", + " jac_norms = norm(jacs, axis=(2,3)).mean(axis=1)\n", + " ax[1].plot(np.arange(len(jacs))/8, jac_norms)\n", + " max_jac_norm.append(np.max(jac_norms))\n", + " \n", + "ax[0].legend()\n", + " \n", + "# add wall\n", + "ax[0].add_patch(patches.Rectangle((1.75, 0), 0.25, 2.0, linewidth=1, edgecolor='black', facecolor='black'))\n", + "\n", + "# add target\n", + "ax[0].add_patch(patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue'))\n", + "\n", + "# ball\n", + "ax[0].add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "\n", + "ax[0].axis(\"equal\")" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "a36fb402", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 ke:10000.0 kf:1.0 kd:10.0 mu:0.9 margin:10.0 \n", + "1 ke:30000.0 kf:3.0 kd:30.0 mu:0.9 margin:10.0 \n", + "2 ke:50000.0 kf:5.0 kd:50.0 mu:0.9 margin:10.0 \n", + "3 ke:100000.0 kf:10.0 kd:100.0 mu:0.9 margin:10.0 \n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# what does this mean for bias?\n", + "\n", + "data = np.load(\"outputs/grads/bounce_grads_40_sim_sweep.npy\", allow_pickle=True)\n", + "data = data[[0, 2, 3, 5]]\n", + "f, ax = plt.subplots(1, 2, figsize=(8, 3))\n", + "H = 40\n", + "bias = []\n", + "stiffness = []\n", + "\n", + "for i in range(len(data)):\n", + " print(f\"{i} ke:{data[i]['soft_contact_ke']} kf:{data[i]['soft_contact_kf']} kd:{data[i]['soft_contact_kd']} mu:{data[i]['soft_contact_mu']} margin:{data[i]['soft_contact_margin']} \")\n", + " fobgs = data[i][\"fobgs\"]\n", + " zobgs = data[i][\"zobgs\"]\n", + " loss = data[i][\"losses\"]\n", + " baseline = data[i][\"baseline\"]\n", + " \n", + " diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + " bias_l2 = norm(diff, ord=2, axis=-1)\n", + " ax[0].plot(np.arange(H), bias_l2, label=i)\n", + " bias.append(bias_l2[-1])\n", + " \n", + "ax[0].legend()\n", + "ax[0].set_ylabel(\"FoBG Bias\")\n", + "ax[0].set_xlabel(\"Horizon H\")\n", + "\n", + "ax[1].plot(max_jac_norm, bias)\n", + "ax[1].set_ylabel(\"FoBG Bias (end of trajectory)\")\n", + "ax[1].set_xlabel(r\"$|| \\nabla f ||$\")\n", + "\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "bf8b0e2a", + "metadata": {}, + "source": [ + "## Takeaways\n", + "\n", + "Increased dynamical stiffness directly correlates to increased bias!\n", + "\n", + "# Optimisation\n", + "(with limited number of samples)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "58d72bc2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABVoAAAEcCAYAAAA2iQ+FAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3wU1drA8d9s7+kJ6ZTQe++C2LFde8derlfBdkXRa3uvhWsXxAJWFBQRe++Iiqj0XgIJ6T3b2+y8f2yyYU0oaYRyvp/PsrtTz5wsO7PPnPMcSVEUBUEQBEEQBEEQBEEQBEEQBKHFVB1dAEEQBEEQBEEQBEEQBEEQhMOdCLQKgiAIgiAIgiAIgiAIgiC0kgi0CoIgCIIgCIIgCIIgCIIgtJIItAqCIAiCIAiCIAiCIAiCILSSCLQKgiAIgiAIgiAIgiAIgiC0kgi0CoIgCIIgCIIgCIIgCIIgtJIItAqCIAiCIAiCIAiCIAiCILSSCLQKgiAIgiAIgiAIgiAIgiC0kgi0CoIgCIIgCIIgCIIgCIIgtJIItAqHjCVLltCzZ88mHzNnzqSgoICePXuyZMmSdi3HXXfdxaRJk/a73CeffMLrr7/ebuWYNGkSd911V7tt/+233263ujxYfytBEITD1erVq5k6dSrjxo2jX79+jB07lqlTp7Jq1apWbXdv3+3t8b3c3t/127dvZ9asWRQUFDSad6Dn6vbQs2dPZs2a1SH77sjjFgRBEARBEPZP09EFEIS/e/TRR+natWvUtOTkZBITE3n33XfJysrqoJJF+/TTT9m2bRtXXHFFu2x/9uzZWCyWdtk2wMKFC4mLi+Pss89u820nJycfUn8rQRCEQ8n8+fN55JFHGDBgAHfccQfp6ekUFRWxYMECLr74Yu655x4uvfTSFm17b9/t7fG93N7f9du3b2f27NmMGDGCjIyMqHk33ngjU6ZMaZf9CoIgCIIgCEJLiUCrcMjp3r07/fv3b3LeoEGDDm5h2ogsy8iyjE6nO+B1+vTp044lah97Hufh+rcSBEFoT3/99RePPPIIEyZMYPbs2Wg0DZdip556KjfddBMPP/wwvXv3ZujQoW223/b4Xu7I73pxI08QBEEQBEE4FInUAcJho6kuirNmzaJnz55s27aN2267jaFDhzJmzBjuvvtuHA5H1Ppvv/02l1xyCaNHj2bQoEGcfvrpzJ07l0Ag0OyyXHbZZfz4448UFhZGpTjYs5xz585lzpw5TJo0if79+7N8+XJ8Ph+PPfYYZ555JkOHDmXEiBFccMEFfPvtt4320VTqAKfTycyZM5k0aRL9+vVj/PjxPPzww7jd7qjlQqEQ8+fP58wzz2TAgAEMGzaM888/n++++y6y7W3btrFixYpI2ffsilhUVMQdd9zB6NGj6devH6eccgqvvvoqoVCo0d+jqePcW3fSXbt2cfvtt0dt9+23325U9jlz5nDSSSdFyn766afzxhtvNPvvJAiCcKh5+eWXkSSJBx54ICrICqDRaLj//vuRJIm5c+dGptef6zZu3MhNN93EkCFDGDp0KHfccQdVVVWR5fb13b6vc+jmzZuZOnVq5Lz06KOPEgwGyc3N5eqrr2bw4MFMmjQpqkx72+beUgD17NkzkgJg3bp13HrrrUyaNIkBAwYwadIkbrvtNgoLCyPbWbJkCdOmTQNgypQpkW3U76upLvQ+n48nn3wy6hz54IMPYrfbo5abNGkS119/PUuXLuWss85iwIABnHzyySxevPgA/4rRqqqqeOCBB5g8eTKDBw9m9OjRTJkyhT///LPJ+nrllVd47bXXmDRpEoMHD+aCCy5g9erVjba7ZMkSTjrppMj58sMPP2xy/wsWLOCMM85g8ODBDB48mJNPPpmnnnoqapnS0lL+85//MGHCBPr168e4ceOYOnUqFRUVkbo70OuTnj178tBDD/HOO+9Eyjd58mQ+++yzRsuWl5dz3333ccwxx9CvXz8mTZrE7NmzCQaDB1i7giAIgiAIhxfRolU45IRCoUYX4H//Mfp3N998M5MnT+bcc89l69atPPnkk0A4DUG9/Px8TjvtNDIyMtBqtWzevJkXX3yR3NzcqOUOxP33389//vMfdu/ezezZs5tcZv78+XTu3Jnp06djsVjIzs7G7/dTW1vLVVddRUpKCoFAgF9//ZWbb76ZRx99lH/84x973afH4+HSSy+lpKSEG264IRJgfu6559i6dSuvv/46kiQB4R+gH3/8Meeeey5Tp05Fq9WycePGyI/Y2bNnM3XqVKxWK/fffz9ApLVtVVUVF154IYFAgGnTppGens6PP/7IzJkzyc/P54EHHtjvcTZl+/btXHjhhaSmpjJ9+nSSkpJYtmwZ//3vf6muruamm24CYN68ecyePZt//vOfDBs2LPJj/++Bc0EQhMONLMv8/vvv9OvXj06dOjW5TGpqKn379mX58uXIsoxarY7Mu+mmmzj55JO58MIL2b59O88++yw7duxg0aJFaLXafX6378stt9zCGWecwYUXXsgvv/zCvHnzCAaD/Prrr1x88cVcffXVfPLJJzzxxBNkZ2dz4okn7nVb7777btR7r9fLnXfeSSgUIjY2FoDCwkK6dOnCqaeeSkxMDOXl5SxcuJBzzz2Xzz77jPj4eCZOnMhtt93GU089xX333Uffvn2BvbdkVRSFG2+8keXLl3PdddcxbNgwtmzZwqxZs1i9ejXvvvtuVF1s3ryZmTNncu2115KYmMh7773HPffcQ3Z2NsOHD99vne2ppqYGCP99EhMTcbvdfPPNN1x22WW8/vrrjBw5Mmr5t99+m65duzJjxgwAnn32Wa677jq+++47rFYrEA6y3n333Rx33HHcddddOBwOZs+ejd/vR6VqaCfx2Wef8eCDD3LZZZcxffp0VCoVeXl5bN++PbJMaWkp55xzDsFgMHL9UF1dzbJly6itrSUxMbHZ1yfff/89v//+O1OnTsVoNLJgwQJuu+021Go1J598MhAOsp533nmoVCr+9a9/kZWVxapVq3jhhRcoLCxs9rWXIAiCIAjC4UAEWoVDzvnnn99o2oYNG/a5zrnnnss111wDwJgxY8jLy+P999/nkUceiQQf77777sjyoVCIYcOGERsby4wZM7jrrruIiYk54DLm5ORgs9n22W1Sr9fzyiuvoNVqo6bv+cNClmVGjx6N3W7njTfe2Gegdf78+WzZsoVFixZFUiuMHj2alJQUpk6dytKlS5kwYQJ//vknH330ETfccAO33nprZP1jjjkm8rpPnz4YDAYsFkuj8r/22muUlpby3nvvMWDAAADGjx+PLMu88847XH755XTp0mWfx9nUwCWPPvooZrOZhQsXRnLPjh07Fr/fz8svv8xll11GTEwMK1eupEePHtx8882RdcePH7/XehEEQThcVFdX4/F4GuUb/buMjAzWrl1LTU0NCQkJkeknnHACd955JwDjxo0jISGBO+64gy+++IIzzjhjn9/t+3LBBRdw5ZVXAuFz6C+//MJbb73F7NmzOeGEEwAYMWIEP/74I5988sk+A6177leWZW6++WYcDgdvvfVW5Lv/5JNPjgTj6pebOHEiY8eO5dNPP2XKlCnEx8dHbtzl5OTs93iWLVvGsmXL+Pe//x25Hhg7diydOnXi1ltv5cMPP4y6vqiurmbhwoWkpaUBMHz4cJYvX84nn3zS7EBr165do25CyrLMuHHjKCwsZP78+Y0CrWazmZdeeikSRE9OTua8885j6dKlnHrqqYRCIZ5++mn69u3L888/H7mOGTp0KCeddBLJycmRba1cuRKbzca9994bmTZ69Oio/T377LPU1NTw0Ucf0a1bt8j0yZMnR15brdZmXZ9UV1ezePFiEhMTAZgwYQKnnXYaTz31VORvO2vWLGpra/nss88i9Tx69GgMBgMzZ87k6quvJicn58AqWRAEQRAE4TAhUgcIh5yZM2eyePHiqMf+WrT+vftgz5498fl8VFZWRqZt3LiRG264gZEjR9K7d2/69u3L9OnTkWWZXbt2tflxTJo0qVGQFeCLL77gwgsvZPDgwfTp04e+ffuyePFiduzYsc/t/fDDD3Tv3p3evXsTDAYjj3HjxiFJEitWrABg6dKlAFxyySUtKvfy5cvJycmJBFnrnX322SiKwvLlyw/oOPfk8/lYvnw5J5xwAgaDIar8xxxzDD6fL9Jtsn///mzevJkHHniAn3/+GafT2aLjEARBOFwpigIQCbDVO/3006Pen3LKKWg0Gn7//fdW7W/ixIlR77t164YkSVE36DQaDdnZ2VHd+/fnoYce4qeffuLZZ5+NtEgFcLlcPP7445xwwgn06dOHPn36MHjwYNxu937PhXtTf276+yBgp5xyCiaTid9++y1qeu/evSPBPwjfNOzcuTNFRUUt2v/ChQs566yz6N+/f+Tc/ttvvzV5PBMnToxqqdyrVy+ASN3u3LmTsrIyTjvttKjPQHp6OoMHD47aVv/+/bHb7dx22218++23Uakk6i1dupSRI0dGBVmb0pzrk9GjR0eCrABqtZrJkyeTl5dHSUkJAD/++CMjR44kOTm50XkfiFy3CIIgCIIgHElEi1bhkNOtW7e9Doa1N/XdEevVdw/0er1AOOfoJZdcQpcuXZgxYwbp6eno9XrWrl3LQw89FFmuLSUlJTWa9vXXX3PLLbdw8sknc80115CYmIharWbhwoW8//77+9xeZWUleXl5UT9W91RdXQ2Eu/6r1eom938gampqSE9PbzS9vgVNfRfJegeyn5qaGoLBIPPnz2f+/PlNLlNf/uuvvx6TycTHH3/MO++8g1qtZtiwYdxxxx3N/lwIgiAcSuLi4jAajU22+t9TYWEhRqOxUU+Lv3/fajQaYmNjG30vN9ff96PVajEajej1+kbTD/Tm15w5c3jnnXd4+OGHowK2ALfffjvLly/nxhtvpH///pjNZiRJ4rrrrsPn87XoGGpqatBoNMTHx0dNlySJxMTERnX09+sGCF87tGT/r732Go899hgXXngh06ZNIy4uDpVKxbPPPktubm6j5fd2zVK/7/rz4Z6BzHqJiYlRwe5//OMfyLLMe++9x9SpUwmFQvTv359bbrmFsWPHRraXkpKyz2No7vXJ3soG4b9Fp06dqKys5IcfftjvdYsgCIIgCMKRRARahaPCt99+i9vtZtasWVFBxM2bN7fbPv/eEgng448/JiMjg2eeeSZq/oEM9BQXF4der+eRRx7Z63yA+Ph4ZFmmvLw8qnvhgYqNjaW8vLzR9LKysqj91GvqOP/OZrOhVqs588wzufjii5tcpr4rrUaj4corr+TKK6/Ebrfz66+/8vTTT3PNNdfw448/YjQam3tIgiAIhwS1Ws3IkSP5+eefKSkpaTJPa0lJCRs2bOCYY46JavUI4ZyXewbMgsEgNTU1TQYNO9KSJUt49tlnufnmmzn33HOj5jkcDn788Uduuukmrrvuusj0+hyhLRUbG0swGKSqqioq2KooChUVFe16o+7jjz9mxIgRPPjgg1HTXS5Xi7ZXf56tH6hqT01NO+ecczjnnHNwu9388ccfzJo1i+uvv56vvvqK9PR04uLiKC0t3e8xNOf6ZF9lq/88xsXF0bNnT2655ZYmt9GSaxRBEARBEIRDnUgdIBwV6n807DkQhqIoLFq0qMXb1Ol0zW4JK0kSWq026kdMeXk533333X7XnThxIrt37yY2Npb+/fs3etQHKutbDi1cuLBF5R89ejTbt29vlBf3ww8/RJKkRrnmDoTRaGTkyJFs3LiRnj17Nln+vwdwIRygPfnkk7n44oupqalpVpdVQRCEQ9F1112Hoig88MADyLIcNU+WZR544AEURYkKQtb75JNPot5/8cUXBINBRowYEZnWknNTW1q6dCn/+c9/OOeccyKDHO5JkiQURWk0SNd7773XqD7+3jtlX+rzkn788cdR07/66ivcbnejvKVtSZKkRsezefPmSEqc5urSpQtJSUl8+umnkTQSEG7pvGrVqr2uZzKZmDBhAjfccAOBQCAyINYxxxzD77//3mTr2j2PoTnXJ7/99ltUsFWWZT7//HOysrIiNxAmTpzI1q1bycrKavK8v79WtoIgCIIgCIcj0aJVOCqMGTMGrVbLbbfdxjXXXIPf72fhwoXY7fYWb7NHjx58/fXXLFiwgH79+iFJ0n5bzEycOJGvv/6aBx54gJNOOomSkhLmzJlDcnLyfvPEXn755Xz99ddceumlXHHFFfTs2ZNQKERxcTHLli3jqquuYuDAgQwbNowzzzyTF154gcrKSiZOnIhOp2Pjxo0YjUYuu+yySPk/++wzPv/8czIyMtDr9fTs2ZMrrriCDz/8kOuvv56pU6eSlpbGjz/+yIIFC7jooouiBsJqjnvuuYeLL76YSy65hIsuuoj09HRcLhf5+fl8//33vPnmmwDccMMNdO/enX79+hEfH09hYSFvvPEG6enpkYFRBEEQDldDhw5lxowZPPLII5HvxLS0NIqKiliwYAFr1qxhxowZDBkypNG633zzDWq1mrFjx7Jt2zaeffZZevXqxSmnnBJZZm/f7QfD7t27mTZtGhkZGZx99tmNAo19+vTBYrEwfPhwXnnlFeLi4khPT2fFihUsXrwYm80WtXz37t0BWLRoEWazGb1eT0ZGRpM35saOHcu4ceN44okncDqdDBkyhC1btvDcc8/Rp08fzjzzzHY77okTJzJnzhyee+45hg8fzs6dO5kzZw4ZGRmNgscHQqVSMW3aNO69917+9a9/cf7552O325k9e3ajLvv33nsvBoOBIUOGkJSURHl5OS+//DJWqzVyTTJt2jSWLl3KpZdeyvXXX0+PHj1wOBz8/PPPXHHFFXTr1q3Z1ydxcXFcfvnl3HjjjRiNRhYsWEBubi5PP/10ZJmpU6fy66+/cuGFF3LZZZfRpUsX/H4/BQUFLF26lAcffLDJVt2CIAiCIAiHMxFoFY4K3bp1Y9asWTzzzDPcfPPNxMbGctppp3HFFVdw7bXXtmibU6ZMYdu2bTz99NM4HA4URWHLli37XOecc86hsrKSd955h/fff5/MzEyuu+46SkpKmD17dqPl92xZYjKZePvtt3n55Zd59913KSgowGAwkJqaypgxY6JSIjz22GP06dOH999/nyVLlmAwGMjJyeH666+PLHPzzTdTXl7Ovffei8vlIj09ne+//574+HjeeecdnnzySZ588klcLhcZGRn8+9//joxK3RI5OTksWbKEOXPm8Mwzz1BVVYXVaiU7O5sJEyZElhs5ciRfffUV7733Hk6nk6SkJMaMGcONN96430G3BEEQDgeXXXYZ/fv359VXX2XmzJnU1NQQExPD0KFDWbBgQaMBj+rNmjWLWbNmsXDhQiRJYtKkScyYMSOqNeXevtsPhqKiItxuN7t27WpyQMbvvvuOjIwMnnzySR5++GEef/xxgsEgQ4YM4bXXXos6RwFkZmYyY8YM3nzzTaZMmYIsyzz66KONBryC8Plyzpw5zJo1iyVLlvDiiy8SGxvLmWeeyW233daoxWlbuuGGG/B4PCxevJh58+aRk5PDAw88wLffftviAZ/OO+88AObNm8dNN91Eeno6119/PX/88UfUNocNG8aSJUv44osvqK2tJS4ujqFDhzJz5sxICoWUlBQWL17Mc889x9y5c6mpqYksV9/Nv7nXJ5MmTSInJ4dnnnmG4uJiMjMzeeKJJ5g8eXJkmeTkZBYvXsycOXN45ZVXKC0txWw2k56ezvjx4xsF1gVBEARBEI4EkrJnnyRBEA4ZI0aM4JxzzmH69OkdXRRBEAShA82aNYvZs2fz22+/NRrsSRAOtp49e3LJJZdw3333dXRRBEEQBEEQDjmiRasgHGI2b97M0qVLqa2tZdCgQR1dHEEQBEEQBEEQBEEQBOEAiMGwBOEQ8/DDD/Pmm29y1VVXceKJJ3Z0cQRBOEiWLFlCz549Gz2eeOKJ/a77wQcfcPLJJ9O/f39OO+00vvjii4NQYkEQBEEQBEEQBGFPInWAIAiCIBwClixZwt133828efOwWq2R6SkpKaSmpu51vS+//JJp06Zx3XXXMXbsWL799lveeust5s2bx7hx4w5G0QVBEARBEARBEARE6gBBEARBOKT07du3WXk4n332WU4++WRuv/12AEaNGsXOnTt57rnnRKBVEARBEARBEAThIBKpAwRBEAThMLV7925yc3M57bTToqafdtpprF27lqqqqg4qmSAIgiAIgiAIwtFHBFoFQRAE4RBy2mmn0bt3b4477jheeuklZFne67K5ubkAdO3aNWp6t27dUBQlMl8QBEEQhJb59ttvOe+88xgyZAhjxozhpptuEudXQRAEYa9E6gBBEARBOAQkJSVx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QisRERERERERERGRjlhoJSIiIiIiIiIiItIRC61EREREREREREREOmKhlYiIiIiIiIiIiEhHLLQSERERERERERER6YiFViIiIiIiIiIiIiIdsdBKRERERCZFpVKhrq5O7DCIiIiIyMiw0EpEREREJmXKlClYv3692GEQERHpzaFDhyCXy3Hp0iUAQFpaGrZu3SpyVK3HERwcjLVr13ZxRESdy0LsAIiIiIiIutLOnTthY2MjdhhERESdJi0tDUlJSVi+fHm3jWPbtm3o1auXCFERdR4WWomIiIjIpMjlcrFDICIiMkh3796FtbW1XtalUCj0sh6i7oRTBxARERERERERGYm1a9ciKSkJQP2Xiw0/BQUFAABBEJCUlITHHnsMI0eOhJ+fH1asWIHr169rrOepp55CaGgovvnmG8ybNw/e3t7qqXdSU1OxaNEiTJgwASNHjsTUqVOxadMmVFVVaR1Hc1MH/Pzzz1izZg38/f3h6emJqVOnYvfu3RpzqxcUFEAulyMxMRF79uxBcHAwRo0ahblz5+LChQv6TSZRO3FEKxERERERERGRkYiKikJVVRX+9a9/ITk5Wd3u4OAAAHjppZfwySef4KmnnsKaNWtw+/ZtbN++HfPmzcM///lP9O/fX/2akpISxMTEYPHixVi1ahXMzOrH6/34448ICAjAwoULYW1tjby8POzatQsXL17E3r17tYrjfrdu3cK8efNQU1ODlStXwtHRESdPnsTGjRuRn5+Pv/3tbxrLJyUlwcXFRV38/fvf/46nn34ax48fh62tre6JJOoAFlqJiIiIyKgVFxfjkUcewaRJkxAfH69uP3HiBKKjo9UfHomIiIzBkCFD1MVSHx8fjecuXLiAgwcPYu3atYiIiFC3P/jgg3jkkUewZ88exMTEqNvLysrw9ttvw9/fX2M9UVFR6n8LggBfX1+4urpi/vz5uHr1Ktzc3FqNozl79uxBUVERUlJSMHLkSADAxIkToVKp8OGHH2LhwoUYPny4enkbGxskJCTA3NwcQH0Bd86cOTh16hSmT5+uRaaI9I9TBxARERGRUXNwcEBkZCSOHDmCy5cvAwAyMjKwcuVKPPHEEyyyEhGRyThx4gQkEglmzJiB2tpa9U///v3h5uaGs2fPaizfu3fvJkVWALh+/Tqee+45jB8/Hu7u7vDw8MD8+fMBAHl5eR2K7cyZMxgxYoS6yNpg1qxZEAQBZ86c0WifNGmSusgKAG5ubgCAwsLCDv3/RPrAEa1EREREZPQiIyORnJyMTZs2YfXq1Vi2bBmmT5+OF154QezQiIiIukxpaSkEQcC4ceOafd7Z2Vnj8YABA5osU1lZiSeffBKWlpZ49tlnMWzYMFhZWeHGjRuIjo7GvXv3OhRbWVkZHB0dm7Q3TDVQVlam0d6nTx+Nx1KpFACgVCo79P8T6QMLrURERERk9KytrfHss89i/fr1WLhwIQIDAxEXFweJRCJ2aERERF2mb9++kEgkSEpKUhcmf+/+tubOk2fOnEFxcTH27duHMWPGqNsrKip0iq1Pnz4oKSlp0l5cXKyOnai749QBRERERGQShg0bpv73hg0bNC43JCIiMiYNBdP7R5dOmjQJgiCgqKgIXl5eTX7kcnmb624ovt5flP3www+1jqM5/v7+yMnJwZUrVzTaDx8+DIlEgoceeqjNdRCJjSNaiYiIiMjoZWVlYenSpfD19UVmZiY+/vhjhIeHix0WERFRp5DJZACAXbt2ISAgAGZmZpDL5Rg9ejTmzp2L9evX4/Lly/Dz84O1tTVKSkpw7tw5yGQyPPnkk62ue9SoUejduzdefvllREdHw8LCAp999hmys7O1jqO50bR/+tOfcPjwYfz5z3/GihUrMHjwYJw8eRL79+/HE088oXEjLKLuioVWIiIiIjJqeXl5iIyMhI+PD3bs2IGVK1di69atmDFjBmxtbcUOj4iISO9CQ0ORmZmJ/fv3Y/v27RAEAcePH4eTkxNiY2Ph7e2N5ORkHDhwAHV1dXBwcICvr2+TG1E1p2/fvkhISMDGjRsRExMDa2trhISEID4+HjNnztQ6jvvZ29vjww8/xObNm7F582ZUVlbCyckJMTExiIiI0FtuiDqTRBAEQewgiIiIiIg6Q0FBAcLDw+Hk5ITExERYWVkhNzcXYWFhiIiIQExMjNghEhEREZGR4BytRERERGSUiouLERERgX79+iEhIQFWVlYAAFdXV8yePRt79+5FQUGByFESERERkbHgiFYiIiIiIiIiIiIiHXFEKxEREREREREREZGOWGglIiIiIiIiIiIi0hELrUREREREREREREQ6YqGViIiIiIiIiIiISEcstBIRERERERERERHpiIVWIiIiIiIiIiIiIh2x0EpERERERERERESkIxZaiYiIiIiIiIiIiHTEQisRERERERERERGRjlhoJSIiIiIiIiIiItIRC61EREREREREREREOmKhlYiIiIiIiIiIiEhHLLQSERERERERERER6ej/AS2GOOOrP117AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "from matplotlib import cm\n", + "from matplotlib import colors\n", + "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import matplotlib.colors as mcolors\n", + "\n", + "\n", + "f, ax = plt.subplots(1, 3, figsize=(14, 3))#, subplot_kw={\"projection\": \"3d\"})\n", + "\n", + "XX = np.linspace(0, 15, 30)\n", + "YY = np.linspace(-10, 5, 30)\n", + "X, Y = np.meshgrid(XX, YY)\n", + "data = np.load(\"landscapes_0.1.npy\", allow_pickle=True)[0]\n", + "Z = data.pop(\"landscape\").reshape(30,30)\n", + "\n", + "\n", + "xy = data['trajectories'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1], c='g')\n", + "\n", + "xy = data['zero_trajectories'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1], c='r')\n", + " \n", + "# visualisations\n", + "ax[0].add_patch(patches.Rectangle((1.75, 0.0), 0.25, 1.3, linewidth=1, edgecolor='black', facecolor='black'))\n", + "ax[0].add_patch(patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue'))\n", + "ax[0].add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "ax[0].axhline(0, c='black')\n", + "ax[0].axis(\"equal\")\n", + "ax[0].set_title(\"Final trajectories\")\n", + "\n", + "\n", + "ax[1].contour(X, Y, Z, levels=20)\n", + "# surf = ax[0].plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)\n", + "\n", + "x_idx, y_idx = np.unravel_index(np.argmin(Z), Z.shape)\n", + "ax[1].scatter(XX[x_idx], YY[y_idx], marker='x', label=\"gobal min\")\n", + "\n", + "xy = data['trajectories'].mean(2)[:, 0, [2,3]]\n", + "ax[1].plot(xy[:, 0], xy[:, 1], c='g', linewidth=3, label=\"first-order\")\n", + "\n", + "xy = data['zero_trajectories'].mean(2)[:, 0, [2,3]]\n", + "ax[1].plot(xy[:, 0], xy[:, 1], c='r', linewidth=3, label=\"zero-order\")\n", + "\n", + "ax[1].set_xlabel(r\"$\\dot{x}$\")\n", + "ax[1].set_ylabel(r\"$\\dot{y}$\")\n", + "ax[1].legend()\n", + "ax[1].set_title(\"Optimization landscape\")\n", + "\n", + "\n", + "losses = data['losses'].mean(-1)\n", + "ax[2].plot(losses, c='g', label=f\"first-order\")\n", + "z_losses = data['zero_losses'].mean(-1)\n", + "ax[2].plot(z_losses, c='r', label=f\"zero-order\")\n", + "ax[2].set_xlabel(\"Iteration\")\n", + "ax[2].set_ylabel(\"Loss\")\n", + "ax[2].legend()\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"optimization.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "97cb77da", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 final first-order 10.6520 zero-order 0.0106\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "import matplotlib.colors as mcolors\n", + "\n", + "colors = list(mcolors.TABLEAU_COLORS.values())\n", + "\n", + "data = np.load(\"landscapes_0.1.npy\", allow_pickle=True)\n", + "\n", + "for i in range(len(data[:1])):\n", + " losses = data[i]['losses'].mean(-1)\n", + " plt.plot(losses, c=colors[i], label=f\"{i} first\")\n", + " z_losses = data[i]['zero_losses'].mean(-1)\n", + " plt.plot(z_losses,'--', c=colors[i], label=f\"{i} zero\")\n", + " print(f\"{i} final first-order {losses.min():.4f} zero-order {z_losses.min():.4f}\")\n", + " \n", + "plt.legend()\n", + "# plt.ylim((-0.1, 2))" + ] + }, + { + "cell_type": "markdown", + "id": "aa00406e", + "metadata": {}, + "source": [ + "## Takeaways:\n", + "\n", + "* as expected, the cost landscape becomes increasingly less convex as we make contact more stiff\n", + "* when using the short wall first-order grads seem to just circle around and don't find the solution\n", + "* regardless of stiffness or sample size if we can guarantee similar contact for all trajectories sampled around the nominal one, then first-order gradients always perform better\n", + "* if we hit the ball on the edge and we encounter stiff contact all pointing in different directions, then first-order gradients fail and bounce around while zero-order still work rather well. Unfortunately this is a very specific case" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "b21bd368", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(-5.7295408010482785,\n", + " 9.554042315483093,\n", + " -0.20691573562507984,\n", + " 4.323848991139675)" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ax = plt.gca()\n", + "\n", + "xy = np.load(\"trajecotry.npy\")\n", + "for i in range(xy.shape[1]):\n", + " ax.plot(xy[:, i, 0], xy[:, i, 1])\n", + " \n", + "# add wall\n", + "ax.add_patch(patches.Rectangle((1.75, 0), 0.25, 2.0, linewidth=1, edgecolor='black', facecolor='black'))\n", + "\n", + "# add target\n", + "ax.add_patch(patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue'))\n", + "\n", + "# ball\n", + "ax.add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "\n", + "plt.axis('equal')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "962f8609", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqoAAAE6CAYAAADeC3C0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAACn9klEQVR4nOzdd5xcVf3/8df02Z0+Ozvbe80mm2wa6Z1ACCC9qxRRELFX/IqCDcXyxYIg6g9RKUpvEiAhoaX3TdndbK8zO733ub8/kHwNBIVAMinn+XjM4+HcuXvv59wZju+ce++5MkmSJARBEARBEAThOCPPdQGCIAiCIAiCcDgiqAqCIAiCIAjHJRFUBUEQBEEQhOOSCKqCIAiCIAjCcUkEVUEQBEEQBOG4JIKqIAiCIAiCcFwSQVUQBEEQBEE4LomgKgiCIAiCIByXRFAVBEEQBEEQjksiqAqCIAjHtY0bN3LJJZdQUlKCWq2muLiYiy++mA0bNnyo7f7ud7/jz3/+87uW9/f3I5PJDvvZkToa2/x3+/bt47bbbqO/v/9dn11zzTVUV1cflf3+J9FolNtuu41169Ydle2vW7cOmUx21LY/OjrKbbfdxs6dO4/K9m+77TZkMtlR2fbJRARVQRAE4bj1m9/8hnnz5jE8PMydd97J6tWr+fnPf87IyAjz58/nt7/97RFv+72CaklJCRs2bODss8/+EJUf/W3+u3379nH77bcfNqjeeuutPPnkk0dlv/9JNBrl9ttvP2pBctq0aWzYsIFp06Ydle2Pjo5y++23H7Wgev3113/of2ydCpS5LkAQBEEQDufNN9/kS1/6EitXruTJJ59Eqfy//8u6/PLLueCCC/jiF7/I1KlTmTdv3ke2X41Gw+zZsz+y7R2tbb5fdXV1OdnvBxWNRsnPz3/f6xuNxpwd0w/j7XaWl5dTXl6e63KOf5IgCIIgHIfOPvtsSaFQSENDQ4f9fHBwUFIoFNI555xzcNn3vvc9CZC2b98uXXDBBZLBYJCMRqN01VVXSePj4wfXq6qqkoBDXlVVVZIkSVJfX58ESPfff/+7trtr1y7p4osvloxGo2SxWKQvf/nLUiqVkjo6OqQzzzxT0uv1UlVVlfTTn/70kFoPt8137v/fX319fZIkSdKWLVukyy67TKqqqpK0Wq1UVVUlXX755VJ/f//B7dx///2H3cbb+7r66qsPtu1tsVhM+ta3viVVV1dLKpVKKi0tlW666SbJ5/Mdsl5VVZV09tlnSy+88II0depUSavVSk1NTdKf/vSn//DN/V973/m6+uqrDzme27Ztky666CLJbDZLxcXF77vNkiRJa9eulQBp7dq1hyzfsmWLdO6550oWi0XSaDRSW1ub9Pe///1dNQ4PD0uf/vSnpfLyckmlUkklJSXSRRddJDkcjoPbfufre9/73sG/f/rpp6XZs2dLeXl5kl6vl04//XRp/fr1h+zjP7Xz7c/e6ZFHHpFmz54t5efnSzqdTjrjjDOk7du3H7JOT0+PdNlll0klJSWSWq2W7Ha7tHTpUmnHjh3/8Xs5EYkRVUEQBOG4k8lkWLt2LTNmzHjPUaeKigqmT5/OK6+8QiaTQaFQHPzsggsu4NJLL+XGG29k79693Hrrrezbt49NmzahUql48sknufjiizGZTPzud78D3hr1/G8uvfRSPv7xj3PDDTfw8ssvc+edd5JKpVi9ejU33XQTX/va13jooYf45je/SX19PRdeeOF7buudp31jsRif+MQnyGQyWK1W4K1rW5uamrj88suxWq2MjY1xzz33MHPmTPbt24fNZuPss8/mxz/+Md/+9re5++67D54Kf6+RVEmSOP/881mzZg233HILCxYsYPfu3Xzve99jw4YNbNiw4ZBjsWvXLr761a/yrW99i6KiIv74xz/yqU99ivr6ehYuXHjYfZSUlLBq1SpWrFjBpz71Ka6//noACgsLD1nvwgsv5PLLL+fGG28kEom87za/l7Vr17JixQpmzZrFvffei8lk4pFHHuGyyy4jGo1yzTXXADAyMsLMmTNJpVJ8+9vfZvLkyXg8Hl588UV8Ph/Tpk3j/vvv59prr+U73/nOwUs23v4tPvTQQ1x11VWcccYZPPzwwyQSCe68804WL17MmjVrmD9//n9t5+H8+Mc/5jvf+c7B/SaTSX72s5+xYMECNm/eTEtLCwArV64kk8lw5513UllZidvtZv369fj9/vfc9gkr10lZEARBEN7J4XBIgHT55Zf/x/Uuu+wyCZCcTqckSf83SvXlL3/5kPUefPBBCZD+9re/HVw2ceJEadGiRe/a5n8aUf3FL35xyLptbW0SID3xxBMHl6VSKamwsFC68MIL/+M2/106nZbOO+88Sa/XS9u2bXvP9qbTaSkcDks6nU761a9+dXD5o48+etjRRUl694jqqlWrJEC68847D1nv73//uwRI991338Flb49qDgwMHFwWi8Ukq9Uq3XDDDe9ZpyRJksvletco5NvePp7f/e53/+M2JOm923y4EdXm5mZp6tSpUiqVOmQb55xzjlRSUiJlMhlJkiTpuuuuk1QqlbRv37733O+WLVsO+51lMhmptLRUam1tPbg9SZKkUCgk2e12ae7cue+rne8cUR0cHJSUSqX0+c9//pD1QqGQVFxcLF166aWSJEmS2+2WAOmuu+56z9pPJuJmKkEQBOGEJUkSwLvunr7qqqsOeX/ppZeiVCpZu3bth9rfOeecc8j7CRMmIJPJOOussw4uUyqV1NfXMzAw8L63e/PNN/P888/z6KOPHnJzUDgcPjg6q1QqUSqV6PV6IpEI+/fvP6I2vPLKKwAHRxffdskll6DT6VizZs0hy9va2qisrDz4XqvV0tjY+IHa914uuuiidy070jZ3d3fT0dFx8LtPp9MHXytXrmRsbIzOzk4AXnjhBZYsWcKECRM+cM2dnZ2Mjo7yiU98Arn8/2KUXq/noosuYuPGjUSj0f/aznd68cUXSafTfPKTnzykdq1Wy6JFiw7elGa1Wqmrq+NnP/sZv/zlL9mxYwfZbPYDt+NEIU79C4IgCMcdm81Gfn4+fX19/3G9/v5+8vPzD54qf1txcfEh75VKJQUFBXg8ng9V1zv3o1aryc/PR6vVvmt5MBh8X9v84Q9/yL333suf/vQnVqxYcchnV155JWvWrOHWW29l5syZGI1GZDIZK1euJBaLHVEbPB4PSqXyXafhZTIZxcXF7zpGBQUF79qGRqM54v3/u5KSknctO9I2O51OAL72ta/xta997bDruN1uAFwu1xHfyPT28Tlc7aWlpWSzWXw+3yE3hh1u3Xd6u/6ZM2ce9vO3Q7FMJmPNmjV8//vf58477+SrX/0qVquVq666ih/96EcYDIYP3KbjmQiqgiAIwnFHoVCwZMkSVq1axfDw8GFDxfDwMNu2beOss8465PpUAIfDQVlZ2cH36XQaj8dz2NCVS3/+85+59dZbue2227juuusO+SwQCPDcc8/xve99j29961sHlycSCbxe7xHvs6CggHQ6jcvlOiSsSpKEw+F4z6B0NLxzJPzDtPnta1dvueWW97w2uKmpCXjrWtnh4eEjqvnt39DY2Ni7PhsdHUUul2OxWA5Z/n7mS327/scee4yqqqr/uG5VVRV/+tOfAOjq6uIf//gHt912G8lkknvvvfd9teNEIU79C4IgCMelW265BUmSuOmmm8hkMod8lslk+OxnP4skSdxyyy3v+tsHH3zwkPf/+Mc/SKfTLF68+OCyj2pU8EitWrWKT3/601x33XV873vfe9fnMpkMSZLedZPXH//4x3cdj7fXeT/tWbZsGQB/+9vfDln++OOPE4lEDn7+YX2Qmt72Qdr8Tk1NTTQ0NLBr1y5mzJhx2Nfbo41nnXUWa9euPXgpwAepv6mpibKyMh566KGDl54ARCIRHn/8cebMmfOBptl625lnnolSqaSnp+c96z+cxsZGvvOd79Da2sr27ds/8H6Pd2JEVRAEQTguzZs3j7vuuosvfelLzJ8/n5tvvpnKykoGBwe5++672bRpE3fddRdz5859198+8cQTKJVKli9ffvCu/ylTpnDppZceXKe1tZVHHnmEv//979TW1qLVamltbT0mbevr6+OSSy6htraWa6+9lo0bNx7y+dSpUzEajSxcuJCf/exn2Gw2qqurefXVV/nTn/6E2Ww+ZP1JkyYBcN9992EwGNBqtdTU1Bx2BHn58uWceeaZfPOb3yQYDDJv3ryDd/1PnTqVT3ziEx9JGw0GA1VVVTz99NMsW7YMq9V6sB3v5YO0+XB+//vfc9ZZZ3HmmWdyzTXXUFZWhtfrZf/+/Wzfvp1HH30UgO9///u88MILLFy4kG9/+9u0trbi9/tZtWoVX/nKV2hubqauro68vDwefPBBJkyYgF6vp7S0lNLSUu68806uuuoqzjnnHG644QYSiQQ/+9nP8Pv9/OQnPzmi41VdXc33v/99/ud//ofe3l5WrFiBxWLB6XSyefNmdDodt99+O7t37+bmm2/mkksuoaGhAbVazSuvvMLu3bsPGYU+aeTwRi5BEARB+K82bNggXXzxxVJRUZGkVColu90uXXjhhe+as1KSDp238txzz5X0er1kMBikK6644uDMAG/r7++XzjjjDMlgMLzveVRdLtch27j66qslnU73rjoWLVokTZw48eD7d27zvebpfPv19jyqw8PD0kUXXSRZLBbJYDBIK1askPbs2SNVVVUdnJP0bXfddZdUU1MjKRSK9zWP6je/+U2pqqrq4Byin/3sZ99z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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "data = np.load(\"optim.npz\")\n", + "\n", + "f, ax = plt.subplots(1, 2, figsize=(8, 3))\n", + "\n", + "ax[0].plot(data['losses'].mean(-1))\n", + "ax[0].set_ylabel(\"loss\")\n", + "ax[0].set_xlabel(\"iter\")\n", + "\n", + "for xy in data['trajectories'].mean(2):\n", + " ax[1].plot(xy[:,0], xy[:,1])\n", + "\n", + "# add wall\n", + "rect = patches.Rectangle((1.75, 0), 0.25, 1.0, linewidth=1, edgecolor='black', facecolor='black')\n", + "ax[1].add_patch(rect)\n", + "\n", + "# add target\n", + "rect = patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue')\n", + "ax[1].add_patch(rect)\n", + "\n", + "ax[1].axis('equal')\n", + "ax[1].set_xlim((-3, 3))\n", + "ax[1].set_title(\"Optimization trajectories\")\n", + "plt.savefig(\"stochastic_optimization.pdf\")" + ] + }, + { + "cell_type": "markdown", + "id": "125e5a14", + "metadata": {}, + "source": [ + "# Comparing clipped gradients" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "64c410a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAApUAAAE6CAYAAABH86kBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAACHdElEQVR4nOzdd3hTZfvA8e/JbtrSQUuhtGWPMsoG2VMQFQUcKKiAoiIi8uJAnIj6oqK8+BPBLYgLFUGcCCigsvcqYFkttFDK6G6a5JzfH6GR0ha603F/ritXk5PnnHPnpDm586yjaJqmIYQQQgghRAnoPB2AEEIIIYSo/CSpFEIIIYQQJSZJpRBCCCGEKDFJKoUQQgghRIlJUimEEEIIIUpMkkohhBBCCFFiklQKIYQQQogSk6RSCCGEEEKUmCSVQgghhBCixCSpLGMLFixAUZR8b48//niJtxUcHEyfPn348ccf813n6NGjTJo0icjISLy9vbFYLNSvX5+77rqLP/74g6tdUGnNmjUoisK333571fimT5+OoihFek0VTUZGBtOnT2fNmjWFKt+nT58C39+cW58+fYocx5gxY3JtQ6/XExYWxu23387evXvzXeevv/7izjvvJCIiArPZjLe3Ny1btuSxxx7jwIEDRY5BiMpm9+7d3HfffTRq1AgvLy+8vLxo0qQJDz74IFu3bi23OPI7F9avX58xY8aU6X7Xr1/P9OnTuXDhQpnupziqwveDuDqDpwOoLj755BOaN2+ea1loaGiJtqVpGqdOnWLu3LkMGTKE5cuXM2TIEHe55cuXM3LkSIKCghg/fjzt27fHbDYTExPDt99+S79+/Vi1ahX9+/cv0WvLMW7cOK677rpS2ZanZGRk8OKLLwIUKhmcN28eKSkp+T43c+ZMli9fzrBhw4oVi5eXF7///jsADoeDmJgYXn75Zbp160Z0dDR169Z1l3322Wd55ZVX6Nq1K88++yxNmjTB4XCwe/duFi5cyOzZs3E4HOj1+mLFIkRF99577zFx4kSaNWvGo48+SsuWLVEUhejoaL788ks6depETEwMjRo18kh8S5cupUaNGmW6j/Xr1/Piiy8yZswY/P39y3RfRVUVvh9EIWiiTH3yyScaoG3ZsqXMtpWRkaGZzWbtzjvvdC+LiYnRrFar1qlTJy05OTnf7f3xxx/azp07r7jPP/74QwO0b775psTxV2SqqmoZGRnamTNnNEB74YUXSrS9JUuWaIqi5HpPimL06NGat7d3nuWrV6/WAO29995zL/viiy80QBs/frymqmqedVRV1ebOnas5HI5ixSJERffXX39pOp1OGzJkiGaz2fIt8/XXX2snT5684nbS09NLJZ4XXnhB88TX66xZszRAO3r0aLnvuyCldUxF5SDN3xXE8uXL6dq1K1arFV9fX6699lo2bNhQqHUtFgsmkwmj0eheNnv2bDIyMpg3b16Bv4779OlDmzZtCrWPrKwspkyZQu3atfHy8qJ3797s2LEjV5n8mjcWL17MwIEDqVOnDl5eXkRGRvLUU0+Rnp6eq9yRI0e44447CA0NxWw2ExISQv/+/dm5c2eBMf30008oisKWLVvcy5YsWYKiKNxwww25ykZFRXHLLbe4HyuKwsSJE3n33XeJjIzEbDazcOFCgoODAXjxxRfdTc9FbbLav38/o0ePpnXr1nz44Ye5nlNVlddff53mzZtjNpupVasW99xzDydOnCjUtv38/AByvdcvv/wyQUFB/O9//8u3eUlRFB5++GGppRRV1n//+1/0ej3vvfceJpMp3zK33XZbrtahMWPG4OPjw549exg4cCC+vr7uVpuVK1dy8803ExYWhsVioXHjxjz44IMkJSXl2e5PP/1E27ZtMZvNNGjQgDfeeCPf/efX/J2SksLjjz9OgwYNMJlM1K1bl8mTJ+c5P+acrxYtWkRkZCRWq5U2bdrk6vY0ffp0nnjiCQAaNGjgPn8V1JVnzpw5KIpCTExMnuemTp2KyWRyv97CHo+c74Dt27dz6623EhAQ4K4ZLsn3Q857FRMTw/XXX4+Pjw/h4eE89thj2Gy2XGVtNhszZswgMjISi8VCzZo16du3L+vXr3eX0TSNefPm0bZtW7y8vAgICODWW2/lyJEj+R4rUXjS/F1OnE4nDocj1zKDwXX4v/jiC0aNGsXAgQP58ssvsdlsvP766/Tp04fVq1fTo0ePfLelaRqnT59m1qxZpKenM3LkSHeZlStXUqdOHTp27Fgq8T/99NO0b9+eDz/8kOTkZKZPn06fPn3YsWMHDRs2LHC9f/75h+uvv57Jkyfj7e3NgQMHeO2119i8ebO7aRfg+uuvx+l08vrrrxMREUFSUhLr16+/Yt+g3r17YzQaWbVqFZ06dQJg1apVeHl5sXbtWux2O0ajkcTERPbu3ctDDz2Ua/1ly5bx559/8vzzz1O7dm0CAwP59ddfue6667jvvvsYN24cgDvRLIzk5GSGDRuGwWDgu+++w2q15nr+oYce4v3332fixInceOONHDt2jOeee441a9awfft2goKCcpXP+Z/Jaf5+4oknCAgIcCfN8fHx7N+/nzvvvBOLxVLoOIWoKpxOJ3/88QcdO3akTp06RVo3Ozubm266iQcffJCnnnrK/Xk7fPgwXbt2Zdy4cfj5+XHs2DFmz55Njx492LNnj/tH3erVq7n55pvp2rUrX331lfscdvr06avuOyMjg969e3PixAmefvppoqKi2LdvH88//zx79uxh1apVuZKwn376iS1btjBjxgx8fHx4/fXXGTZsGAcPHqRhw4aMGzeOc+fO8fbbb/Pdd9+5j0WLFi3y3f9dd93F1KlTWbBgAS+//HKu4/nZZ58xZMgQ9/mosMcjx/Dhw7njjjsYP358ngTxUoX9fgCw2+3cdNNN3HfffTz22GOsW7eOl156CT8/P55//nnAdZ4cPHgwf/75J5MnT6Zfv344HA42btxIbGws3bp1A+DBBx9kwYIFTJo0iddee41z584xY8YMunXrxq5duwgJCbnq+ycK4Omq0qoup8k6v5vdbtecTqcWGhqqtW7dWnM6ne71UlNTtVq1amndunW76rbMZrM2b968XPu1WCzaNddckycep9Op2e129+3SfeYnp/m7ffv2uZpWjx07phmNRm3cuHHuZVdr8lFVVbPb7dratWs1QNu1a5emaZqWlJSkAdqcOXOuGEt+evToofXr18/9uHHjxtoTTzyh6XQ6be3atZqmadrnn3+uAdqhQ4fc5QDNz89PO3fuXK7tlaT5W1VVbciQIZpOp9N++umnPM9HR0drgDZhwoRcyzdt2qQB2tNPP+1eNnr06Hzf6zp16mh//fWXu9zGjRs1QHvqqafy7M/hcOR6r/NrGheisjt16pQGaHfccUee5670Gcj5jH388cdX3H7Oeev48eMaoH3//ffu57p06aKFhoZqmZmZ7mUpKSlaYGBgnnNhvXr1tNGjR7sfz5w5U9PpdHm6M3377bcaoP3888/uZYAWEhKipaSk5HrdOp1OmzlzpntZUZu/hw8froWFheX6Hvj55581QPvhhx+KfDxyvgOef/75POsV9/tB0/59r77++utc61x//fVas2bN3I8//fRTDdA++OCDAvezYcMGDdDefPPNXMvj4uI0Ly8v7cknnyxwXXF10vxdTj799FO2bNmS62YwGDh48CDx8fHcfffd6HT/vh0+Pj7ccsstbNy4kYyMjAK39csvvzB69Ggefvhh5s6de9U4hg8fjtFodN8mTZpUqPhHjhyZ61dzvXr16NatG3/88ccV1zty5AgjR46kdu3a6PV6jEYjvXv3BiA6OhqAwMBAGjVqxKxZs5g9ezY7duxAVdVCxdW/f3/+/vtvMjMzOX78ODExMdxxxx20bduWlStXAq7ay4iICJo0aZJr3X79+hEQEFCo/RTG9OnT+eGHH5g+fTrXX399nudzjtXlTWCdO3cmMjKS1atX51ru5eXlfp83bdrEd999R9OmTbn++usL1TWiZs2aud7rJUuWFP/FCVEJdejQIddn4M0338xT5tJuMTkSExMZP3484eHhGAwGjEYj9erVA/49b6Wnp7NlyxaGDx+eq5XA19c314DJgvz444+0atWKtm3b4nA43LdBgwbl22zdt29ffH193Y9DQkKoVasWx48fL9SxyM/YsWM5ceIEq1atci/75JNPqF27NoMHD3YvK8zxuFR+xzQ/hfl+yKEoSp7jGhUVlev1//LLL1gsFu69994C9/njjz+iKAp33XVXruNeu3Zt2rRpU+iZP0T+pPm7nERGRubbFH327FmAfJttQkNDUVWV8+fP52pGvXxb1113HcePH+fJJ5/krrvuwt/fn4iIiHxPNm+++SbPPvssgLvJuDBq166d77Jdu3YVuE5aWho9e/bEYrHw8ssv07RpU6xWK3FxcQwfPpzMzEzAdbJYvXo1M2bM4PXXX+exxx4jMDCQUaNG8corr+Q6kV5uwIABvPjii/z1118cP36coKAg2rVrx4ABA1i1ahUvvfQSq1evZsCAAXnWLWpT2ZUsX76cl156iSFDhriP7+Wu9l5f/n7pdLo8/zODBg0iPDycKVOmsGHDBsLDwwHyfa/XrFmDw+Fg27ZtjB8/vlivS4iKLigoCC8vr3w/A1988QUZGRkkJCRw00035XnearXm6XOuqioDBw4kPj6e5557jtatW+Pt7Y2qqlxzzTXu89b58+dRVbXAc+PVnD59mpiYmDxNxzku769Ys2bNPGXMZrM7nuIYPHgwderU4ZNPPmHgwIGcP3+e5cuX8+ijj7r7YBf2eFyqMOfWwn4/5LBarXm6+JjNZrKystyPz5w5Q2hoaK4KmsudPn0aTdMKbOK+UncucXWSVHpYzokiISEhz3Px8fHodLpC1aZFRUWxYsUKDh06ROfOnbn22mt555132Lp1a67EpLjTaZw6dSrfZfmd6HL8/vvvxMfHs2bNGvevTyDffpL16tXjo48+AuDQoUN8/fXXTJ8+nezsbN59990C99GlSxd8fHxYtWoVx44do3///iiKQv/+/XnzzTfZsmULsbGx+SaVpTVn2sGDB7n77rtp3LgxixYtKnC7l77XYWFhuZ6Lj4/P058yP1arlUaNGrmT+dDQUFq2bMnKlSvJysrKddJt27Yt4Dp5C1FV6fV6+vXrx2+//UZCQkKuhCanP+GxY8fyXTe/z+revXvZtWsXCxYsYPTo0e7llw9oCQgIQFGUAs+NV5OTDH/88ccFPl/W9Ho9d999N//3f//HhQsX+OKLL7DZbIwdO9ZdprDH41KFObcW5fuhsIKDg/nrr79QVbXAxDIoKAhFUfjzzz8xm815ns9vmSg8af72sGbNmlG3bl2++OKLXBORp6ens2TJEveI8KvJGSWdM6jkP//5D1arlYcffpjU1NQSx/nll1/miu/48eOsX7/+inM55pxYLv+Qvvfee1fcV9OmTXn22Wdp3bo127dvv2JZo9FIr169WLlyJb///jvXXnstAD179sRgMPDss8+6k8zCyIm1sL/+U1NTGTZsGKqqsnTpUvfo7Pz069cPgM8++yzX8i1bthAdHV2oGNPS0oiJiaFWrVruZc888wxJSUlMmTLlqpPZC1EVTZs2DafTyfjx47Hb7SXaVmHPW97e3nTu3JnvvvsuV21ZamoqP/zww1X3c+ONN3L48GFq1qxJx44d89zq169f5NiLev4CVxN4VlYWX375JQsWLKBr16655lQu7nn8aspiu4MHDyYrK4sFCxYUWObGG29E0zROnjyZ73Fv3bp1sfcvpKbS43Q6Ha+//jqjRo3ixhtv5MEHH8RmszFr1iwuXLjAq6++mmedvXv3ukcpnj17lu+++46VK1cybNgwGjRoALhqJL/88kvuvPNOWrduzUMPPeSe/DwxMZHffvsNoNCT8SYmJjJs2DDuv/9+kpOTeeGFF7BYLEybNq3Adbp160ZAQADjx4/nhRdewGg08vnnn+dpMt+9ezcTJ07ktttuo0mTJphMJn7//Xd2797NU089ddXY+vfvz2OPPQbgrpH08vKiW7du/Pbbb0RFReVKwq7E19eXevXq8f3339O/f38CAwMJCgoq8AR/zz33EB0dzeOPP05qaiobN27MU8ZsNtOuXTuaNWvGAw88wNtvv41Op2Pw4MHu0d/h4eH85z//ybWeqqru7amqysmTJ/m///s/zp8/z/Tp093l7rzzTvbt28crr7zCrl27GDNmDE2aNEFVVeLi4li0aJH7tQlRFXXv3p133nmHRx55hPbt2/PAAw/QsmVLdDodCQkJ7v7EhTnfNW/enEaNGvHUU0+haRqBgYH88MMP7j7al3rppZe47rrruPbaa3nsscdwOp289tpreHt7c+7cuSvuZ/LkySxZsoRevXrxn//8h6ioKFRVJTY2lt9++43HHnuMLl26FOk45CREb731FqNHj8ZoNNKsWbMrfvabN29O165dmTlzJnFxcbz//vvFPh5FUdjvh6K48847+eSTTxg/fjwHDx6kb9++qKrKpk2biIyM5I477qB79+488MADjB07lq1bt9KrVy+8vb1JSEjgr7/+cn9fimLy4CChaqGwk58vW7ZM69Kli2axWDRvb2+tf//+2t9//53vti69+fn5aW3bttVmz56tZWVl5dnu4cOHtUceeURr1qyZ5uXlpZnNZq1evXrabbfdpi1duvSqI4JzRn8vWrRImzRpkhYcHKyZzWatZ8+e2tatW3OVzW903/r167WuXbtqVqtVCw4O1saNG6dt375dA7RPPvlE0zRNO336tDZmzBitefPmmre3t+bj46NFRUVp//vf/wo1YfeuXbs0QGvSpEmu5a+88ooGaFOmTMmzDqA9/PDD+W5v1apVWrt27TSz2awBuUZs5redq93q1avnLu90OrXXXntNa9q0qWY0GrWgoCDtrrvu0uLi4nJtN7/R37Vq1dJ69+6tLV26NN9Y1q1bp40YMUILCwvTjEajZrVatRYtWmgPPfRQnvdKiKpo586d2tixY7UGDRpoZrNZs1gsWuPGjbV77rlHW716da6yBV1gQNM0bf/+/dq1116r+fr6agEBAdptt92mxcbG5jszxPLly7WoqCjNZDJpERER2quvvprvufDy0d+apmlpaWnas88+qzVr1kwzmUyan5+f1rp1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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "data = {\"normal\": \"outputs/grads/bounce_grads_40.npz\",\n", + " \"clip\": \"outputs/grads/bounce_grads_40_clip_5.0.npz\",\n", + " \"norm\": \"outputs/grads/bounce_grads_40_5.0.npz\"}\n", + "\n", + "f, ax = plt.subplots(1, 2, figsize=(8, 3))\n", + "\n", + "\n", + "for (k,v) in data.items():\n", + " data = np.load(v)\n", + " fobgs = data[\"fobgs\"]\n", + " zobgs = data[\"zobgs\"]\n", + " loss = data[\"losses\"]\n", + " baseline = data[\"baseline\"]\n", + " hh = np.arange(fobgs.shape[0])\n", + " m=data['m']\n", + " N = fobgs.shape[1]\n", + " std = data['std']\n", + "\n", + " diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + " bias_l2 = norm(diff, ord=2, axis=-1)\n", + " ax[0].plot(hh, bias_l2, label=k)\n", + " ax[0].set_title(\"FoBG bias wrt ZoBG\")\n", + " ax[0].set_xlabel(\"H\")\n", + "\n", + " ax[1].plot(hh, norm_variance(fobgs), label=k)\n", + " ax[1].set_yscale(\"log\")\n", + " ax[1].set_xlabel(\"H\")\n", + " ax[1].set_title(\"Gradient variance\")\n", + " ax[1].legend()\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[0].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "ax[0].legend()\n", + "ax[1].plot(hh, norm_variance(zobgs), label=\"ZoBGs\")\n", + "ax[1].legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "d98e6be7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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n5s6daqSkpKiOByO4LrGxkYlJiamw/k6MzNTueGGG4KPn3jiCUWtVivr169vt917772nAMpnn30WXAcoiYmJSmNjY7v3rVarlSeeeCK47umnn1YA5dChQ8d9T20uv/xyJS0trd13lc8++0wBlE8++aTHn0fb95SHHnqow+t6+x1GUY78rN599912r7nggguU4cOHBx//61//UgDlb3/7W5fH+fbbbxVAeeaZZ9qtLyoqUkwmk/J///d/Xb5WiJ6SIdYirP71r3+xfv36dotWq2Xv3r2UlpZy/fXXo1Yf+TW1Wq18//vfZ+3atdjt9i739fnnn3PDDTdw55138uc///mE7bj88svR6XTB5Sc/+Um32n/NNde0u1KcmZnJ9OnT+eabb477uoMHD3LNNdeQlJSERqNBp9Mxc+ZMAHbv3g1ATEwMOTk5PP300zz77LNs3rwZv9/frXbNmTOH1atX43A4KCwsJD8/n6uuuorx48ezePFiINCrnJGRQW5ubrvXnnvuuSGdg/3www/zySef8PDDD3PBBRd0eL7tszp2CNsZZ5zByJEjWbp0abv1JpMp+HNet24dH3zwAcOGDeOCCy7o1vD72NjYdj/r999/v/dvTgghRMhNmjSp3f/pZ555psM2R08PalNZWcntt99Oeno6Wq0WnU5HZmYmcOTc2tLSwvr167n88svbjTCy2WztEnp25b///S+jR49m/PjxeL3e4DJ//vxOh0bPnj0bm80WfJyYmEhCQgKFhYXd+iw6c9NNN1FcXMySJUuC61555RWSkpJYsGBBcF13Po+jdfaZdqY732HaqFSqDp/r2LFj273/zz//HKPRyI9+9KMuj/nf//4XlUrFdddd1+5zT0pKYty4cd2usiFEd8gQaxFWI0eO7HS4c01NDUCnw65SUlLw+/3U1dW1G6p77L7OP/98CgsL+b//+z+uu+46oqKiyMjI6PSk9Mwzz/Dggw8CBIcld0dSUlKn67Zu3drla5qbmzn77LMxGo089thjDBs2DLPZTFFREZdffjkOhwMInFSWLl3Ko48+ylNPPcW9995LTEwM1157LY8//ni7E+6xzjvvPB555BFWrVpFYWEhcXFxTJgwgfPOO48lS5bwm9/8hqVLl3Leeed1eG1Ph7odz8cff8xvfvMbLrroouDne6wT/ayP/Xmp1eoOvzPz588nPT2dhQsX8u2335Keng7Q6c962bJleL1eNm7cyO23396r9yWEEOLkxMXFYTKZOv0//eabb2K32ykrK+Piiy/u8LzZbO6QR8Tv9zNv3jxKS0v5f//v/zFmzBgsFgt+v58zzzwzeG6tq6vD7/d3ef4+kYqKCvLz8zsMT25z7Pze2NjYDtsYDIZge3pjwYIFJCcn88orrzBv3jzq6ur4+OOPueeee4J5Nbr7eRytO+f/7n6HaWM2mztMdTIYDO0SgFZVVZGSktKuQ+RYFRUVKIrS5TDq401rE6KnJEAWA1LbCaWsrKzDc6WlpajV6m71co4dO5Yvv/ySffv2ccYZZzB37lz+8pe/sGHDhnZBVm/LR5SXl3e6rrMTYpuvv/6a0tJSli1bFrziCnQ6rzgzM5N//OMfAOzbt493332Xhx9+GLfbzV//+tcujzF16lSsVitLliyhoKCAOXPmoFKpmDNnDs888wzr16/n8OHDnQbIoap3uHfvXq6//nqGDh3K66+/3uV+j/5ZH5sps7S0tMP8486YzWZycnKCFyZSUlLIy8tj8eLFOJ3Odifn8ePHA4GTvBBCiPDQaDSce+65fPXVV5SVlbULztrm3xYUFHT62s7OJzt27GDr1q28+uqr3HDDDcH1xyazio6ORqVSdXn+PpG2wP6f//xnl8/3NY1Gw/XXX88f//hH6uvrefPNN3G5XNx0003Bbbr7eRytO+f/nnyH6a74+HhWrVqF3+/vMkiOi4tDpVKxcuVKDAZDh+c7WydEb8kQazEgDR8+nNTUVN58800URQmub2lp4f333w9mtj6RtmzPbQmlfvazn2E2m7nzzjtpamo66Xa+9dZb7dpXWFjImjVrjlsruO0EdOw/85deeum4xxo2bBgPPvggY8aMYdOmTcfdVqfTcc4557B48WK+/vpr5s6dC8DZZ5+NVqvlwQcfDAbM3dHW1u5e8W5qauKyyy7D7/ezaNGiYJbpzpx77rkAvPHGG+3Wr1+/nt27d3erjc3NzeTn55OQkBBc96tf/Yrq6moWLlzY7mckhBBiYLj//vvx+XzcfvvteDyek9pXd8+tFouFM844gw8++KBdL2ZTUxOffPLJCY/zve99jwMHDhAbG8vkyZM7LFlZWT1ue0/PsRAYZu10Onnrrbd49dVXmTZtGiNGjAg+39vvGifSF/tdsGABTqeTV199tcttvve976EoCiUlJZ1+7mPGjOn18YU4lvQgiwFJrVbz1FNPce211/K9732P2267DZfLxdNPP019fT1PPvlkh9fs2LEjmMmypqaGDz74gMWLF3PZZZeRnZ0NBHqK33rrLa6++mrGjBnDj3/8YyZOnIjBYKCyspKvvvoKoMsSUMeqrKzksssu43//939paGjg17/+NUajkfvvv7/L10yfPp3o6Ghuv/12fv3rX6PT6fj3v//dYVj2tm3buOuuu7jiiivIzc1Fr9fz9ddfs23bNu67774Ttm3OnDnce++9AMGeYpPJxPTp0/nqq68YO3Zsu4DyeGw2G5mZmXz00UfMmTOHmJgY4uLiuvwi8MMf/pDdu3fz85//nKamJtauXdthG4PBwIQJExg+fDi33norf/rTn1Cr1SxYsCCYxTo9PZ2f/exn7V7n9/uD+/P7/ZSUlPDHP/6Ruro6Hn744eB2V199NTt37uTxxx9n69at3HjjjeTm5uL3+ykqKuL1118PvjchhBD9b8aMGfzlL3/h7rvvZuLEidx6663k5eWhVqspKysL5ojozjl5xIgR5OTkcN9996EoCjExMXzyySfBvBtH+81vfsP555/P3Llzuffee/H5fPzud7/DYrFQW1t73OP89Kc/5f333+ecc87hZz/7GWPHjsXv93P48GG++uor7r33XqZOndqjz6EtuHv++ee54YYb0Ol0DB8+/LjnpxEjRjBt2jSeeOIJioqKePnll3v9efREd7/D9MTVV1/NK6+8wu23387evXuZPXs2fr+fdevWMXLkSK666ipmzJjBrbfeyk033cSGDRs455xzsFgslJWVsWrVquB3OiFCIowJwsRprC3z9LFZII/14YcfKlOnTlWMRqNisViUOXPmKKtXr+50X0cvkZGRyvjx45Vnn31WcTqdHfZ74MAB5e6771aGDx+umEwmxWAwKJmZmcoVV1yhLFq06ISZjduyWL/++uvKT37yEyU+Pl4xGAzK2WefrWzYsKHdtp1lgFyzZo0ybdo0xWw2K/Hx8cott9yibNq0SQGUV155RVEURamoqFBuvPFGZcSIEYrFYlGsVqsyduxY5Q9/+IPi9XqP2z5FUZStW7cqgJKbm9tu/eOPP64AysKFCzu8BlDuvPPOTve3ZMkSZcKECYrBYFCAdlk9O9vPiZbMzMzg9j6fT/nd736nDBs2TNHpdEpcXJxy3XXXKUVFRe3221kW64SEBGXmzJnKokWLOm3LihUrlCuvvFJJS0tTdDqdYjablVGjRik//vGPO/yshBBC9L8tW7YoN910k5Kdna0YDAbFaDQqQ4cOVX74wx8qS5cubbftDTfcoFgslk73s2vXLmXu3LmKzWZToqOjlSuuuEI5fPhwp1UYPv74Y2Xs2LGKXq9XMjIylCeffLLT8/WxWawVRVGam5uVBx98UBk+fLii1+uVyMhIZcyYMcrPfvYzpby8PLhdV+fUzvZ5//33KykpKYparVYA5Ztvvjn+h6Yoyssvv6wAislkUhoaGnr9ebS976qqqg776O13GEXp+mfV2T4dDofy0EMPKbm5uYper1diY2OVc889V1mzZk277f75z38qU6dOVSwWi2IymZScnBzlhz/8oZzPRUipFEXGHgohhBBCCCGEEDIHWQghhBBCCCGEQAJkIYQQQgghhBACkABZCCGEEEIIIYQAJEAWQgghhBBCCCEACZCFEEIIIYQQQghAAmQhhBBCCCGEEAIAbX8f0O/3U1pais1mQ6VS9ffhhRBCiHYURaGpqYmUlBTUarluHApyrhdCCDHQdPd83+8BcmlpKenp6f19WCGEEOK4ioqKSEtLC3czTglyrhdCCDFQneh83+8Bss1mAwINi4iI6O/Di96w22H1atDrwWAI7b5dLnC7YcYMMJtDu28xePXl71yoye/woNfY2Eh6enrw/CROnpzrByE514twkPO96EfdPd/3e4DcNtQqIiJCTpqDhVYLFgvYbGA0hnbfTic0NUFEhPyzEUf05e9cqMnv8ClDhgKHjpzrByE514twkPO9CIMTne9lspUQQgghhBBCCIEEyEIIIYQQQgghBCABshBCCCGEEEIIAYRhDnJ3+Hw+PB5PuJsh2rhcoNFAH8zP0xD4JZSZf0IIIYQQQohwG3ABcnNzM8XFxSiKEu6miDZ+P8TGglod2iBZUcBsxgwkezzoQ7dnIYQQQgghhOixARUg+3w+iouLMZvNxMfHS0bRgcLnC6ThD3GArABuj4eq6moOlZaSa7Mdt2i3EEIIIYQQQvSlARUgezweFEUhPj4ek8kU7uaINj4feL19MszapNejU6korKrC7XZjHOgp/oUQQgghhBCnrAHZXSc9x6cXtfy8hRBCCCGEEAPAgAyQhRBCCCGEEEKI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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# comparing different clipping ranges\n", + "data = {\"no clipping\": \"outputs/grads/bounce_grads_40.npz\",\n", + " \"1\": \"outputs/grads/bounce_grads_40_1.0.npz\",\n", + " \"2\": \"outputs/grads/bounce_grads_40_2.0.npz\",\n", + " \"3\": \"outputs/grads/bounce_grads_40_3.0.npz\",\n", + " \"4\": \"outputs/grads/bounce_grads_40_4.0.npz\",\n", + " \"5\": \"outputs/grads/bounce_grads_40_5.0.npz\",\n", + " \"6\": \"outputs/grads/bounce_grads_40_6.0.npz\"}\n", + "\n", + "f, ax = plt.subplots(1, 2, figsize=(12, 4))\n", + "\n", + "\n", + "for (k,v) in data.items():\n", + " data = np.load(v)\n", + " fobgs = data[\"fobgs\"]\n", + " zobgs = data[\"zobgs\"]\n", + " loss = data[\"losses\"]\n", + " baseline = data[\"baseline\"]\n", + " hh = np.arange(fobgs.shape[0])\n", + " m=data['m']\n", + " N = fobgs.shape[1]\n", + " std = data['std']\n", + "\n", + " diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + " bias_l2 = norm(diff, ord=2, axis=-1)\n", + " ax[0].plot(hh, bias_l2, label=k)\n", + " ax[0].set_title(\"FoBG bias wrt ZoBG\")\n", + " ax[0].set_xlabel(\"H\")\n", + "\n", + " ax[1].plot(hh, norm_variance(fobgs), label=k)\n", + " ax[1].set_yscale(\"log\")\n", + " ax[1].set_xlabel(\"H\")\n", + " ax[1].set_title(\"Gradient variance\")\n", + " ax[1].legend()\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[0].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "ax[0].legend()\n", + "ax[1].plot(hh, norm_variance(zobgs), label=\"ZoBGs\")\n", + "ax[1].legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "35fe23a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA8gAAAGHCAYAAABsw3uSAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADkyklEQVR4nOzdd3hUZfbA8e/0kkx670BCqKF3FBABsYJl7aKrq9gV3bX+XLCx6qq4urbVVVfXriiuIk0B6b1IDwTSe29T7++PISMxCSQhYVLO53nuM3Pv3HJmUu6c+773vCpFURSEEEIIIYQQQohuTu3tAIQQQgghhBBCiI5AEmQhhBBCCCGEEAJJkIUQQgghhBBCCEASZCGEEEIIIYQQApAEWQghhBBCCCGEACRBFkIIIYQQQgghAEmQhRBCCCGEEEIIQBJkIYQQQgghhBACkARZCCGEEEIIIYQAJEEWXvL++++jUqkanR588MHT3ldoaCgTJ07kf//7X6PbpKWlcc8999C3b198fHwwGo0kJCRw3XXX8fPPP6MoykmPuXLlSlQqFV9++eUp45s7dy4qlapF76mjqa6uZu7cuaxcubJZ60+cOLHJn2/dNHHixBbHceONN9bbh0ajISYmhj/84Q/8+uuvjW6zZs0arr76auLi4jAYDPj4+NC/f38eeOAB9u/f3+IYhBBCtK1du3Zx880306tXL0wmEyaTiaSkJG677Ta2bNlyxuJo7HydkJDAjTfe2K7HXbduHXPnzqW0tLRdj9MaXeE7jBAtpfV2AKJ7e++99+jTp0+9ZVFRUae1L0VRyM3N5bXXXuOiiy5i0aJFXHTRRZ71Fi1axDXXXENISAizZ89m6NChGAwGUlNT+fLLLznnnHNYvnw5kydPPq33VueWW27hvPPOa5N9eUt1dTXz5s0DaFZi+/rrr1NeXt7oa/Pnz2fRokXMnDmzVbGYTCZ++uknABwOB6mpqTz99NOMHTuWffv2ER0d7Vn38ccf55lnnmHMmDE8/vjjJCUl4XA42LVrFx988AEvvfQSDocDjUbTqliEEEKcnrfeeou77rqL5ORk7r33Xvr3749KpWLfvn188sknjBgxgtTUVHr16uWV+BYuXIifn1+7HmPdunXMmzePG2+8kYCAgHY9Vkt1he8wQrSUJMjCqwYMGMDw4cPbZV/nnXcegYGBfPLJJ54E+fDhw1x99dX079+f5cuX1zvpTZgwgZtvvpmVK1cSGBjYJjEBxMTEEBMT02b7O5MURaG2trbF2/Xr16/R5V9//TXfffcdV199Nffee2+rYlKr1YwePdozP378eOLi4pg8eTLff/89t956KwCffPIJzzzzDLNnz+b111+vdwV8ypQpzJkzh9dff71VMQghhDh9a9eu5Y477uCCCy7gyy+/RK/Xe14755xzuPPOO/niiy8wmUwn3U91dTVms7ldYhwyZEi77Lejq/tMO/N3GCFaS7pYiw5t0aJFjBkzBrPZjMViYcqUKaxfv75Z2xqNRvR6PTqdzrPspZdeorq6mtdff73JK8ITJ05k0KBBzTpGbW0tc+bMISIiApPJxIQJE9i+fXu9dRrrnvTZZ58xdepUIiMjMZlM9O3bl4cffpiqqqp66x05coSrrrqKqKgoDAYD4eHhTJ48mR07djQZ0/fff49KpWLz5s2eZV999RUqlYoLLrig3ropKSlcdtllnnmVSsVdd93Fm2++Sd++fTEYDHzwwQeEhoYCMG/ePE/35pZ2Odu7dy+zZs1i4MCBvPPOO/Vec7lcPP/88/Tp0weDwUBYWBg33HADmZmZzdq3v78/QL2f9dNPP01ISAgvv/xyo93DVCoVd955p7QeCyGElzz77LNoNBreeuutesnxia644op6PctuvPFGfH192b17N1OnTsVisXh6fC1btoxLLrmEmJgYjEYjiYmJ3HbbbRQWFjbY7/fff8/gwYMxGAz06NGDv//9740ev7Eu1uXl5Tz44IP06NEDvV5PdHQ09913X4NzeN059cMPP6Rv376YzWYGDRpU7/avuXPn8uc//xmAHj16eM6xTd3StGDBAlQqFampqQ1ee+ihh9Dr9Z7329zPo+57yrZt27j88ssJDAz0tNifzneYup9Vamoq559/Pr6+vsTGxvLAAw9gtVrrrWu1WnnyySfp27cvRqOR4OBgJk2axLp16zzrKIrC66+/zuDBgzGZTAQGBnL55Zdz5MiRRj8rIVpLWpCFVzmdThwOR71lWq371/Ljjz/m2muvZerUqXzyySdYrVaef/55Jk6cyIoVKxg/fnyj+1IUhby8PF544QWqqqq45pprPOssW7aMyMjINmu1fvTRRxk6dCjvvPMOZWVlzJ07l4kTJ7J9+3Z69uzZ5HaHDh3i/PPP5777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81x77bVceOGF3HbbbVitVl544QVKS0v529/+1mCbX3/91VPJsqioiK+//pply5Yxc+ZMevToAbhbij/55BOuvvpqBg4cyO23387QoUMxGAzk5+ezdOlSgCaHgPq9/Px8Zs6cyZ/+9CfKysr461//itFo5JFHHmlym7FjxxIYGMjs2bP561//ik6n47///W+Dbtm7du3irrvu4oorriApKQm9Xs9PP/3Erl27ePjhh08Z2+TJk3nggQcAPC3FJpOJsWPHsnTpUlJSUuollCdjsViIj4/n22+/ZfLkyQQFBRESEtLkF4EbbriBffv28eCDD1JRUcGGDRsarGMwGBgyZAjJycnceuutvPrqq6jVaqZPn+6pYh0bG8v9999fbzuXy+XZn8vlIisri3/84x+UlJQwd+5cz3pXX301e/bs4ZlnnmHnzp3ceOONJCUl4XK5yMjI4MMPP/S8NyGEEGfeuHHj+Oc//8ndd9/N0KFDufXWW+nfvz9qtZqcnBxPjYjmnJP79OlDr169ePjhh1EUhaCgIL777jtP3Y0TPfXUU5x33nlMmTKFBx54AKfTyXPPPYePjw/FxcUnPc59993HV199xdlnn839999PSkoKLpeL9PR0li5dygMPPMCoUaNa9DnUJXevvPIKs2bNQqfTkZycfNLzU58+fRgzZgzz588nIyODt99+u9WfR0s09ztMS1x99dW89957zJ49mwMHDjBp0iRcLhcbN26kb9++XHXVVYwbN45bb72Vm266iS1btnD22Wfj4+NDTk4Oa9as8XynE6JNeLFAmOjG6ipP/74K5O998803yqhRoxSj0aj4+PgokydPVtauXdvovk6c/P39lcGDBysvvfSSUltb22C/hw8fVu6++24lOTlZMZlMisFgUOLj45UrrrhCWbhw4SkrG9dVsf7www+Ve+65RwkNDVUMBoNy1llnKVu2bKm3bmMVINetW6eMGTNGMZvNSmhoqHLLLbco27ZtUwDlvffeUxRFUfLy8pQbb7xR6dOnj+Lj46P4+voqKSkpyssvv6w4HI6TxqcoirJz504FUJKSkuotf+aZZxRAmTNnToNtAOXOO+9sdH/Lly9XhgwZohgMBgWoV9Wzsf2caoqPj/es73Q6leeee07p3bu3otPplJCQEOW6665TMjIy6u23sSrWYWFhyoQJE5SFCxc2Gsvq1auV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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# comparing different clipping ranges\n", + "data = {\"no clipping\": \"outputs/grads/bounce_grads_40.npz\",\n", + " \"1\": \"outputs/grads/bounce_grads_40_clip_1.0.npz\",\n", + " \"2\": \"outputs/grads/bounce_grads_40_clip_2.0.npz\",\n", + " \"3\": \"outputs/grads/bounce_grads_40_clip_3.0.npz\",\n", + " \"4\": \"outputs/grads/bounce_grads_40_clip_4.0.npz\",\n", + " \"5\": \"outputs/grads/bounce_grads_40_clip_5.0.npz\",\n", + " \"6\": \"outputs/grads/bounce_grads_40_clip_6.0.npz\"}\n", + "\n", + "f, ax = plt.subplots(1, 2, figsize=(12, 4))\n", + "\n", + "\n", + "for (k,v) in data.items():\n", + " data = np.load(v)\n", + " fobgs = data[\"fobgs\"]\n", + " zobgs = data[\"zobgs\"]\n", + " loss = data[\"losses\"]\n", + " baseline = data[\"baseline\"]\n", + " hh = np.arange(fobgs.shape[0])\n", + " m=data['m']\n", + " N = fobgs.shape[1]\n", + " std = data['std']\n", + "\n", + " diff = zobgs.mean(axis=1) - fobgs.mean(axis=1)\n", + " bias_l2 = norm(diff, ord=2, axis=-1)\n", + " ax[0].plot(hh, bias_l2, label=k)\n", + " ax[0].set_title(\"FoBG bias wrt ZoBG\")\n", + " ax[0].set_xlabel(\"H\")\n", + "\n", + " ax[1].plot(hh, norm_variance(fobgs), label=k)\n", + " ax[1].set_yscale(\"log\")\n", + " ax[1].set_xlabel(\"H\")\n", + " ax[1].set_title(\"Gradient variance\")\n", + " ax[1].legend()\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[0].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "for (start, end) in contact_ranges:\n", + " ax[1].axvspan(start/8, end/8, alpha=0.2, color='red')\n", + " \n", + "ax[0].legend()\n", + "ax[1].plot(hh, norm_variance(zobgs), label=\"ZoBGs\")\n", + "ax[1].legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "052646ed", + "metadata": {}, + "outputs": [ + { + "ename": "FileNotFoundError", + "evalue": "[Errno 2] No such file or directory: 'optim.npz'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [29]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m data_files \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfirst-order\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moptim.npz\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 2\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mzero-order\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moptim_clip.npz\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 3\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnormalised\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moptim_norm.npz\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m k,v \u001b[38;5;129;01min\u001b[39;00m data_files\u001b[38;5;241m.\u001b[39mitems():\n\u001b[0;32m----> 6\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mload\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mzero-order\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mplot(data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlosses\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;241m0\u001b[39m], label\u001b[38;5;241m=\u001b[39mk)\n", + "File \u001b[0;32m~/.miniconda3/envs/rl_bench/lib/python3.10/site-packages/numpy/lib/npyio.py:417\u001b[0m, in \u001b[0;36mload\u001b[0;34m(file, mmap_mode, allow_pickle, fix_imports, encoding)\u001b[0m\n\u001b[1;32m 415\u001b[0m own_fid \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 416\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 417\u001b[0m fid \u001b[38;5;241m=\u001b[39m stack\u001b[38;5;241m.\u001b[39menter_context(\u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mos_fspath\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 418\u001b[0m own_fid \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m 420\u001b[0m \u001b[38;5;66;03m# Code to distinguish from NumPy binary files and pickles.\u001b[39;00m\n", + "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'optim.npz'" + ] + } + ], + "source": [ + "data_files = {\"first-order\": \"optim.npz\",\n", + " \"zero-order\": \"optim_clip.npz\",\n", + " \"normalised\": \"optim_norm.npz\"}\n", + "\n", + "for k,v in data_files.items():\n", + " data = np.load(v)\n", + " if k == 'zero-order':\n", + " plt.plot(data['losses'][0], label=k)\n", + " else:\n", + " plt.plot(data['losses'].mean(-1), label=k)\n", + " \n", + "plt.xlabel(\"Iteration\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.legend()\n", + "plt.savefig(\"optim_comparison.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "ff76e41f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/JAnn3yS22+/HX9/fyZNmsS9997r6dCEEEII4YU8XrwAdOzYkffee8/TYQghhBCiBfD4bSMhhBBCiMaQ4kUIIYQQLYoUL0IIIYRoUaR4EUIIIUSLIsWLEEIIIVoUKV6EEEII0aJI8SKEEEKIFkWKFyGEEEK0KIrmXPO6dTm6lLm76XSqz62uepQv5w6+nb/k7pu5g2/nL7k3f+6qqqAoda11XVOrLV6EEEII0TrJbSMhhBBCtChSvAghhBCiRZHiRQghhBAtihQvQgghhGhRpHgRQgghRIsixYsQQgghWhQpXoQQQgjRokjxIoQQQogWRYoXIYQQQrQoUrwIIYQQokWR4kUIIYQQLYoUL0IIIYRoUaR4EUIIIUSLovd0AC3Bvn37ePLJJ1m7di0BAQGcffbZ3Hvvvfj7+3s6NLdauHAhCxYsYOvWrRQVFZGQkMBll13GpZdeiqo66twHH3yQr7/+uta5s2bNYuTIkc0dstvMnz+fhx56qNb+6dOnc++99zq3ly1bxksvvcSePXto27Yt11xzDZdffnlzhtokrrzySv76668633vxxRc5++yzW8W1P3DgAO+99x4bN25k165ddOrUie+//77WcQ29zu+99x6ffvopOTk5JCcnc//99zN48ODmSMUl9eVvs9mYPXs2y5YtY/fu3dhsNpKTk7ntttsYOnRojbbGjBlDZmZmrc/YtGkTfn5+TZ5LYzXk2jfmZ7wlXfuG5N61a9cTnr9ixQpiYmIA77nuUrzUo7i4mKuvvprY2FheffVV8vPzefrppyksLOT555/3dHhu9f777xMbG8v9999PZGQkq1ev5qmnniI9PZ0HHnjAeVxCQkKt3JOSkpo73Cbx7rvvEhIS4txu06aN8/X69eu55ZZbOOecc3jwwQdZt24dTz75JEajkYsuusgT4brNY489RmlpaY19H374IT///HONX1ot/drv2rWLZcuW0bt3b+x2O5qm1Tqmodf5vffe46WXXuJf//oX3bp144svvmD69Ol88cUXJ/1F4En15V9RUcE777zDueeey3XXXYder+frr79m2rRpvPXWW4wePbrG8WeddRbXXnttjX1Go7HJ83BFQ649NOxnvKVd+4bk/vnnn9fa98ADDxAQEOAsXI7yiuuuiZN65513tN69e2t5eXnOfd99952WnJys7d6924ORud/xOR71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I7p27cpDDz3E1q1byc/Px2Aw0KNHD2644YYag11boieffJIVK1Zw5MgR7HY7HTp04KKLLuLKK6+s8T+pZcuW8eKLLzqnjZ82bVqrWB4AHOM8hg0bxtVXX11rjENhYWGruPYZGRm1Hv8+6qOPPnJO796Q66xpmnOK+NzcXJKTk7nvvvsYMmRIk+fhqvryj4uLO+H7gPOR2A0bNvDCCy+wa9cuSkpKCAkJYciQIdxxxx21HiH2FvXl3ph/31ratW/oz/1vv/3GjTfeyMyZM+u8s+BN112KFyGEEEK0KK2jr1sIIYQQPkOKFyGEEEK0KFK8CCGEEKJFkeJFCCGEEC2KFC9CCCGEaFGkeBFCCCFEiyLFixBCCCFaFClehBANNn/+fLp27crmzZsBx2Ru3jDD7sniGDNmDA8++GAzRySEaEpSvAghXLZs2TJef/11T4dx0jhef/11brnllmaOSAjRlGRtIyGE1zGbzXWureKK1rJophDiGOl5EUK45MEHH+TTTz8FoGvXrs6vjIwMwLH+y6effso555xDr169GDhwIHfccQfp6ek12rnyyiuZNGkSa9as4dJLL6V37948/PDDgGNl72uvvZbhw4fTq1cvJkyYwPPPP095eXmD46jrttGhQ4e49957GTp0KD169GDChAnMnj0bu93uPCYjI4OuXbvy3nvv8f777zNmzBj69u3LJZdcUmPRUiFE85OeFyGES2655RbKy8v56aef+Pzzz537Y2JiAHj00Uf5+uuvufLKK7n33nspKirijTfe4NJLL+Xbb78lKirKeU5OTg733Xcf119/Pf/617+cqzfv37+fkSNHcvXVVxMQEMDevXuZNWsWmzZt4qOPPmpQHP+Un5/PpZdeSlVVFXfeeSdxcXH89ttvPPvssxw8eJDHH3+8xvGffvopnTp1chZUr7zyCjfccAOLFy8mJCTk1P8ghRCNJsWLEMIliYmJzgKkT58+Nd7bsGED8+bN48EHH2TatGnO/QMGDOCss87i/fffr7FydWFhIS+//DJDhw6t0c7xY1U0TaNfv34kJSVxxRVXsH37dlJSUk4aR13ef/99srKy+OKLL+jVqxcAI0aMwGaz8dlnn3H11VfTsWNH5/FBQUG888476HQ6wFEUXXTRRSxfvpyzzz67AX9SQgh3k+JFCOF2S5cuRVEUpkyZgtVqde6PiooiJSWFv/76q8bxoaGhtQoXgPT0dF5++WVWrVpFXl4emqY539u7dy8pKSmNjm3VqlV07tzZWbgcdf755zN37lxWrVpVo3g5/fTTnYUL4PzMzMzMRn+2EMI9pHgRQrjd0ULjtNNOq/P9hISEGtvR0dG1jikrK2Pq1Kn4+flx11130aFDB/z9/Tly5Ai33XYbFRUVLsVWWFhIXFxcrf1HbzMVFhbW2B8WFlZj22g0AlBZWenS5wshTp0UL0IItwsPD0dRFD799FPnL/vj/XOfoii1jlm1ahXZ2dl8/PHHDBo0yLm/pKTklGILCwsjJyen1v7s7Gxn7EII7yZPGwkhXHa0CPlnL8jpp5+OpmlkZWXRs2fPWl9du3att+2jBc0/C53PPvuswXHUZejQoezevZutW7fW2P/NN9+gKAqDBw+utw0hhGdJz4sQwmXJyckAzJo1i5EjR6KqKl27dqV///5ccsklPPzww2zZsoWBAwcSEBBATk4Oa9euJTk5malTp5607b59+xIaGspjjz3Gbbfdhl6vZ8GCBezYsaPBcdTV63PNNdfwzTffcOONN3LHHXcQGxvLb7/9xpw5c7jssstqjHcRQngnKV6EEC6bNGkS69atY86cObzxxhtomsbixYuJj49nxowZ9O7dm88//5y5c+dit9uJiYmhX79+tQbL1iU8PJx33nmHZ599lvvuu4+AgADGjh3LSy+9xHnnndfgOP4pIiKCzz77jBdeeIEXXniBsrIy4uPjue+++2o8GSWE8F6KdvzwfSGEEEIILydjXoQQQgjRokjxIoQQQogWRYoXIYQQQrQoUrwIIYQQokWR4kUIIYQQLYoUL0IIIYRoUaR4EUIIIUSLIsWLEEIIIVoUKV6EEEII0aJI8SKEEEKIFkWKFyGEEEK0KFK8CCGEEKJF+X/QBFdzcyeLyAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "data = np.load(\"bounce_optimization.npz\")\n", + "\n", + "plt.plot(data[\"losses\"].mean(-1), linewidth=2, label=\"first-order\")\n", + "plt.plot(data[\"losses_clip\"].mean(-1), linewidth=2, label=\"zero-order\")\n", + "plt.plot(data[\"losses_norm\"].mean(-1), linewidth=2, label=\"normalised\")\n", + "\n", + "# for k,v in data_files.items():\n", + "# data = np.load(v)\n", + "# plt.plot(data['losses'].mean(-1), label=k)\n", + " \n", + "plt.xlabel(\"Iteration\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.legend()\n", + "plt.savefig(\"optim_comparison.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "8b93cf7f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2UAAAEcCAYAAABHxBxzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADEhUlEQVR4nOzdd3wURf/A8c/uXku79BBICCWhB0ITQpdiAQULYhcRe+WxIoiP5VHUnwo2UAFRQUFRVIqKoiCKgkon9ACBEEJ6v8vlbm9/f1xy4UgoAVIuzPv18iW7O7s7c7lc9nsz8x1J0zQNQRAEQRAEQRAEoV7I9V0BQRAEQRAEQRCEC5kIygRBEARBEARBEOqRCMoEQRAEQRAEQRDqkQjKBEEQBEEQBEEQ6pEIygRBEARBEARBEOqRCMoEQRAEQRAEQRDqkQjKBEEQBEEQBEEQ6pEIygRBEARBEARBEOqRCMoEQRAEQRAEQRDqkQjKzpNvvvmGdu3aVfvfa6+9xpEjR2jXrh3ffPNNrdbj6aefZsiQIactt2zZMj755JNaq8eQIUN4+umna+36n3/+ea29lnX1sxIEQRAEQRAEAF19V6CxeeWVV2jdurXHvoiICMLCwvjyyy+JiYmpp5p5Wr58Ofv27WPcuHG1cv333nsPf3//Wrk2wMKFCwkODubaa68979eOiIhoUD8rQRAEQRAEoXETQdl51qZNGzp37lztsa5du9ZtZc4TVVVRVRWDwXDG53Ts2LEWa1Q7jm+nt/6sBEEQBEEQBO8jhi/WkeqGxL377ru0a9eOffv28dhjj9GjRw/69u3LpEmTKCoq8jj/888/55ZbbqFPnz507dqVkSNHMnv2bOx2e43rctttt/Hbb7+RlpbmMczy+HrOnj2bmTNnMmTIEDp37sz69eux2Wy8+uqrXHXVVfTo0YNevXpxww038Msvv1S5R3XDF4uLi3nttdcYMmQI8fHxDBgwgJdffhmLxeJRzul0Mn/+fK666iq6dOlCz549uf766/n111/d1963bx///POPu+7HD9k8evQoTzzxBH369CE+Pp7hw4czd+5cnE5nlZ9Hde082fDFlJQUHn/8cY/rfv7551XqPnPmTC677DJ33UeOHMmnn35a45+TIAiCIAiCcGEQPWXnmdPpxOFweOzT6U79Mj/88MOMGDGC6667jr179/Lmm28CrqGQFQ4fPsyVV15JdHQ0er2e3bt388EHH3DgwAGPcmfiueee49lnnyU1NZX33nuv2jLz58+nZcuWTJw4EX9/f1q0aEFZWRkFBQWMHz+eJk2aYLfb+euvv3j44Yd55ZVXuPrqq096T6vVyq233sqxY8e477773MHoO++8w969e/nkk0+QJAlwzYtbunQp1113HY888gh6vZ6dO3eSlpYGuIZGPvLIIwQEBPDcc88BuHvxcnNzufHGG7Hb7UyYMIGoqCh+++03XnvtNQ4fPszzzz9/2nZWJzk5mRtvvJGmTZsyceJEwsPDWbt2LS+99BJ5eXk89NBDAMyZM4f33nuP+++/n549e+JwODhw4ECVIFsQBEEQBEEQKoig7Dy7/vrrq+zbsWPHKc+57rrruOuuuwDo27cvhw4dYvHixUydOtUdqEyaNMld3ul00rNnT4KCgpg8eTJPP/00gYGBZ1zHuLg4zGbzKYfpGY1GPvroI/R6vcf+4wNAVVXp06cPhYWFfPrpp6cMyubPn8+ePXtYtGiRe3hnnz59aNKkCY888gi///47gwYNYsOGDSxZsoT77ruPRx991H3+wIED3f/u2LEjJpMJf3//KvX/+OOPycjI4KuvvqJLly4ADBgwAFVV+eKLL7j99ttp1arVKdt55MiRKvV/5ZVX8PPzY+HChe65cv369aOsrIxZs2Zx2223ERgYyKZNm2jbti0PP/yw+9wBAwac9HURBEEQBEEQBBGUnWevvfYasbGxHvtO11N2YrbEdu3aYbPZyMnJISwsDICdO3fyzjvvsHnzZvLz8z3Kp6SkkJCQcO6VP6FOJwZkAD/++COffvope/bs8Rh2aDQaT3m91atX06ZNGzp06ODRk9i/f38kSeKff/5h0KBB/P777wDccsstZ1Xv9evXExcX5w7IKlx77bUsXLiQ9evXewRlJ2vn8Ww2G+vXr+emm27CZDJ51H/gwIF89tlnbNmyhUGDBtG5c2d+++03nn/+eYYOHUq3bt1qNeGJIAiCIAiC4P1EUHaexcbGnjTRx8kEBQV5bFcMxSstLQVcc6RuueUWWrVqxeTJk4mKisJoNLJt2zZefPFFd7nzKTw8vMq+n3/+mf/85z9cfvnl3HXXXYSFhaEoCgsXLmTx4sWnvF5OTg6HDh2iU6dO1R7Py8sDXMMPFUWp9v5nIj8/n6ioqCr7IyIi3MePdyb3yc/Px+FwMH/+fObPn19tmYr633vvvfj6+rJ06VK++OILFEWhZ8+ePPHEEzV+XwiCIAiCIAgXBhGUeYFffvkFi8XCu+++6xFw7N69u9buWTFs8nhLly4lOjqat956y+P4mSSxCA4Oxmg0MnXq1JMeBwgJCUFVVbKystyBVE0EBQWRlZVVZX9mZqbHfSpU184Tmc1mFEXhqquu4uabb662THR0NODqFb3jjju44447KCws5K+//mL69Oncdddd/Pbbb/j4+NS0SYIgCIIgCEIjJ7IveoGKwOH4lPSaprFo0aKzvqbBYKhxD5skSej1eo9AJisry50V8VQuvvhiUlNTCQoKonPnzlX+qwhqKuaOLVy48Kzq36dPH5KTk6vM4/vuu++QJInevXuftq4n8vHxoXfv3uzcuZN27dpVW/8Tgz1wBXOXX345N998M/n5+e5EJYIgCIIgCIJwPNFT5gX69u2LXq/nscce46677qKsrIyFCxdSWFh41tds27YtP//8MwsWLCA+Ph5Jkk47vO7iiy/m559/5vnnn+eyyy7j2LFjzJw5k4iICFJSUk557u23387PP//Mrbfeyrhx42jXrh1Op5P09HTWrl3L+PHjSUhIoGfPnlx11VW8//775OTkcPHFF2MwGNi5cyc+Pj7cdttt7vp///33/PDDD0RHR2M0GmnXrh3jxo3ju+++49577+WRRx6hWbNm/PbbbyxYsICbbrrJYz5ZTTzzzDPcfPPN3HLLLdx0001ERUVRUlLC4cOHWbVqFfPmzQPgvvvuo02bNsTHxxMSEkJaWhqffvopUVFRJ83sKAiCIAiCIFzYRFDmBWJjY3n33Xd56623ePjhhwkKCuLKK69k3Lhx3H333Wd1zbFjx7Jv3z6mT59OUVERmqaxZ8+eU54zevRocnJy+OKLL1i8eDHNmzfnnnvu4dixY9Wm1j++R83X15fPP/+cWbNm8eWXX3LkyBFMJhNNmzalb9++HsMyX331VTp27MjixYv55ptvMJlMxMXFce+997rLPPzww2RlZTFlyhRKSkqIiopi1apVhISE8MUXX/Dmm2/y5ptvUlJSQnR0NE8++SR33HHHWb1W4MpY+c033zBz5kzeeustcnNzCQgIoEWLFgwaNMhdrnfv3vz000989dVXFBcXEx4eTt++fXnggQdOm1BEEARB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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# effects of clipping when we're operating at the edge of contact\n", + "\n", + "%matplotlib inline\n", + "\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "from matplotlib import cm\n", + "from matplotlib import colors\n", + "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import matplotlib.colors as mcolors\n", + "\n", + "\n", + "f, ax = plt.subplots(1, 3, figsize=(9, 3))#, subplot_kw={\"projection\": \"3d\"})\n", + "\n", + "data = np.load(\"bounce_optimization_edge.npz\")\n", + "\n", + "xy = data['trajectories'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1], c='g')\n", + "\n", + "xy = data['z_trajectories'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1], c='r')\n", + "\n", + "xy = data['trajectories_clip'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1])\n", + "\n", + "xy = data['trajectories_norm'].mean(2)[:, :, [0,1]]\n", + "ax[0].plot(xy[-1, :, 0], xy[-1, :, 1])\n", + " \n", + "# visualisations\n", + "ax[0].add_patch(patches.Rectangle((1.75, 0.0), 0.25, 1.3, linewidth=1, edgecolor='black', facecolor='black'))\n", + "ax[0].add_patch(patches.Rectangle((-1.9, 1.4), 0.1, 0.1, linewidth=1, edgecolor='blue', facecolor='blue'))\n", + "ax[0].add_patch(patches.Circle((-0.5, 1.0), 0.1, edgecolor='red', facecolor='red'))\n", + "ax[0].axhline(0, c='black')\n", + "ax[0].axis(\"equal\")\n", + "ax[0].set_title(\"Final trajectories\")\n", + "\n", + "\n", + "losses = data['losses'].mean(-1)\n", + "ax[1].plot(losses, c='g', label=f\"first-order\")\n", + "z_losses = data['z_losses'].mean(-1)\n", + "ax[1].plot(z_losses, c='r', label=f\"zero-order\")\n", + "z_losses = data['losses_clip'].mean(-1)\n", + "ax[1].plot(z_losses, label=f\"zero-order\")\n", + "z_losses = data['losses_norm'].mean(-1)\n", + "ax[1].plot(z_losses, label=f\"zero-order\")\n", + "ax[1].set_xlabel(\"Iteration\")\n", + "ax[1].legend()\n", + "\n", + "ax[2].plot(data['norms'], label=\"first-order\")\n", + "ax[2].plot(data['z_norms'], label=\"zero-order\")\n", + "\n", + "plt.tight_layout()\n", + "# plt.savefig(\"edge_optimizaiton_clipping.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "2505211c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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h7F169bwtoMHD2L79u0YMGAAtW2CdO3aFRs2bJBsq6qqwqFDh8J9BbWxeZjdlmVlZaivr8fixYvD++3fvx+rVq1qm7Ex6ROLU5BQYrRLLrmELVq0iH3yySds3LhxlBhNB/fccw/r378/e+mll9jq1asl/wslPgol6rn77rvZsmXL2KxZszQT9bzyyits6dKl7I477kirpEd6WbZsmWpiNGpjY/j9fnbeeeexU089lX355Zds/vz57Nxzz2Unnngia2pqYoxR2ybCm2++yfr378/uv//+cGK0c845h40ZM4YdPHgwvB+1sT7cbjebO3cumzt3Lrv88stZWVlZ+O9QTiiz2/K6665jJ5xwApszZw5bsGABJUZrC7Zv385++9vfsuHDh7Nx48axBx54oE0a/GhnypQprH///or/W7ZsWXi/BQsWsLPPPpsNHjyYnXLKKeytt96KKksURfbSSy+xk046iQ0ZMoSdf/75bOnSpW15OUcFSoKEMWrjeDl8+DC744472OjRo9nw4cPZ7373O1ZRUSHZh9o2PkRRZO+99x47++yz2YgRI9jEiRPZ73//e0UBQW0cmz179rR5f9vQ0MBmzpzJxowZw0aMGMF+//vfs7179yb1OkMIjKVBdhmCIAiCIFKatIwhIQiCIAgitSBBQhAEQRCE5ZAgIQiCIAjCckiQEARBEARhOSRICIIgCIKwHBIkBEEQBEFYDgkSgiAIgiAshwQJQRAEQRCWQ4KEIAiCIAjLIUFCEARBEITlkCAhCIIgCMJy/h947+EEq2w/YQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(data['losses'].var(axis=0))\n", + "plt.plot(data['z_losses'].var(axis=0))" + ] + }, + { + "cell_type": "markdown", + "id": "f42eaa12", + "metadata": {}, + "source": [ + "## Takeaways:\n", + "\n", + "* clipping doesn't work" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ball_env/bounce_env.py b/ball_env/bounce_env.py new file mode 100644 index 00000000..a9459679 --- /dev/null +++ b/ball_env/bounce_env.py @@ -0,0 +1,479 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +########################################################################### +# Example Sim Grad Bounce +# +# Shows how to use Warp to optimize the initial velocity of a particle +# such that it bounces off the wall and floor in order to hit a target. +# +# This example uses the built-in wp.Tape() object to compute gradients of +# the distance to target (loss) w.r.t the initial velocity, followed by +# a simple gradient-descent optimization step. +# +########################################################################### + +import os +import numpy as np +from typing import List +from tqdm import tqdm, trange + +import warp as wp +import warp.sim as sim +import warp.sim.render + +wp.init() + + +class Bounce: + # control frequency + frame_dt = 1.0 / 60.0 + + sim_time = 0.0 + render_time = 0.0 + sim_substeps = 8 + + train_iters = 250 + train_rate = 0.01 + + def __init__( + self, + num_envs=32, + num_steps=200, + start_state=np.array([-0.5, 1.0, 0.0]), + start_vel=np.array([5.0, -5.0, 0.0]), + render=False, + profile=False, + adapter=None, + soft_contact_ke=1e4, # stiffness + soft_contact_kf=1e0, # stiffness of friction + soft_contact_kd=1e1, # damping + soft_contact_mu=0.9, # friction coefficient + soft_contact_margin=1e1, + std=1e-1, + ): + self.device = wp.get_device(adapter) + self.profile = profile + self.render = render + self.num_envs = num_envs + + self.start_state = start_state + self.start_vel = start_vel + self.std = std + + # sim frequency + self.frame_steps = num_steps + self.sim_steps = self.frame_steps * self.sim_substeps + self.sim_dt = self.frame_dt / self.sim_substeps + + noise = self.noise() + builder: sim.ModelBuilder = wp.sim.ModelBuilder() + for i in range(self.num_envs): + builder.add_particle(pos=start_state, vel=start_vel + noise[i], mass=1.0) + + # for a large number of environments + builder.soft_contact_max = 256 * 1024 + + # high wall + builder.add_shape_box(body=-1, pos=(2.0, 0.65, 0.0), hx=0.25, hy=0.65, hz=0.5) + + # short wall + # builder.add_shape_box(body=-1, pos=(2.0, 0.5, 0.0), hx=0.25, hy=0.5, hz=0.5) + + self.model: sim.Model = builder.finalize(self.device) + self.model.ground = True + + self.model.soft_contact_ke = soft_contact_ke + self.model.soft_contact_kf = soft_contact_kf + self.model.soft_contact_kd = soft_contact_kd + self.model.soft_contact_mu = soft_contact_mu + self.model.soft_contact_margin = soft_contact_margin + + self.integrator = wp.sim.SemiImplicitIntegrator() + + self.target = (-2.0, 1.5, 0.0) + self.loss = wp.zeros( + self.num_envs, dtype=wp.float32, device=self.device, requires_grad=True + ) + self.l = wp.zeros(1, dtype=wp.float32, device=self.device, requires_grad=True) + + # allocate sim states for trajectory + self.states: List[wp.sim.State] = [] + self.contact_count = wp.zeros(self.sim_steps, dtype=int, device=self.device) + self.prev_contact_count = np.zeros(self.sim_steps) + for i in range(self.sim_steps + 1): + self.states.append(self.model.state(requires_grad=True)) + + if self.render: + self.stage = wp.sim.render.SimRenderer( + self.model, + os.path.join( + os.path.dirname(__file__), "outputs/example_sim_grad_bounce.usd" + ), + scaling=40.0, + ) + + def noise(self): + noise = np.random.normal(0.0, self.std, (self.num_envs, 2)) + noise[0] = 0.0 # for baseline + self.noise_ = np.append(noise, np.zeros((self.num_envs, 1)), axis=1) + return self.noise_ + + def reset(self, start_state=None, start_vel=None): + if start_state is None: + start_state = self.start_state + + if start_vel is None: + start_vel = self.start_vel + + # replicate array if necessary and assign + q = np.array(start_state) + if q.ndim == 1: + q = np.tile(q, (self.num_envs, 1)) + q = wp.array(q, dtype=wp.vec3) + self.model.particle_q.assign(q) + + # replicate array if necessary and assign + qd = np.array(start_vel) + if qd.ndim == 1: + qd = np.tile(qd, (self.num_envs, 1)) + qd += self.noise() + qd = wp.array(qd, dtype=wp.vec3) + self.model.particle_qd.assign(qd) + + # only need to reset first state as everything is forward simulated from it + self.states[0] = self.model.state(requires_grad=True) + + self.loss.zero_() + self.l.zero_() + + @wp.kernel + def loss_kernel( + pos: wp.array(dtype=wp.vec3), + target: wp.vec3, + loss: wp.array(dtype=float), + ): + i = wp.tid() # gets current thread id + delta = pos[i] - target + loss[i] = wp.dot(delta, delta) + + @wp.kernel + def sum_kernel( + losses: wp.array(dtype=float), + loss: wp.array(dtype=float), + ): + i = wp.tid() + wp.atomic_add(loss, 0, losses[i]) + + @wp.kernel + def mean_kernel( + x_in: wp.array(dtype=wp.vec3), x_out: wp.array(dtype=wp.vec3), num_envs: float + ): + i = wp.tid() + wp.atomic_add(x_out, 0, x_in[i] / num_envs) + + @wp.kernel + def step_kernel( + x: wp.array(dtype=wp.vec3), grad: wp.array(dtype=wp.vec3), alpha: float + ): + tid = wp.tid() # gets current thread id + + # gradient descent step + x[tid] = x[tid] - grad[0] * alpha + + @wp.kernel + def count_contact_changes_kernel( + contact_count: wp.array(dtype=int), + contact_count_id: int, + contact_count_copy: wp.array(dtype=int), + ): + wp.atomic_add(contact_count_copy, contact_count_id, contact_count[0]) + + def count_contact_changes(self, i): + # count contact changes + wp.launch( + self.count_contact_changes_kernel, + dim=self.num_envs, + inputs=[self.model.soft_contact_count, i], + outputs=[self.contact_count], + device=self.device, + ) + + def compute_loss(self): + # run control loop + for i in range(self.sim_steps): + self.states[i].clear_forces() + wp.sim.collide(self.model, self.states[i]) + self.integrator.simulate( + self.model, self.states[i], self.states[i + 1], self.sim_dt + ) + self.count_contact_changes(i) + + # compute loss on final state + wp.launch( + self.loss_kernel, + dim=self.num_envs, + inputs=[self.states[-1].particle_q, self.target, self.loss], + device=self.device, + ) + + return self.loss + + def sum_loss(self): + wp.launch( + self.sum_kernel, + dim=self.num_envs, + inputs=[self.loss, self.l], + device=self.device, + ) + return self.l + + def render_iter(self, iter): + # render every 16 iters + if iter % 16 > 0: + return + + # draw trajectory + traj_verts = [self.states[0].particle_q.numpy()[0].tolist()] + + for i in range(0, self.sim_steps, self.sim_substeps): + traj_verts.append(self.states[i].particle_q.numpy()[0].tolist()) + + self.stage.begin_frame(self.render_time) + self.stage.render(self.states[i]) + self.stage.render_box( + pos=self.target, + rot=wp.quat_identity(), + extents=(0.1, 0.1, 0.1), + name="target", + ) + self.stage.render_line_strip( + vertices=traj_verts, + color=wp.render.bourke_color_map(0.0, 7.0, self.loss.numpy()[0]), + radius=0.02, + name=f"traj_{iter}", + ) + self.stage.end_frame() + + self.render_time += self.frame_dt + + self.stage.save() + + def check_grad(self): + param = self.states[0].particle_qd + + # initial value + x_c = param.numpy().flatten() + + # compute numeric gradient + x_grad_numeric = np.zeros_like(x_c) + + for i in range(len(x_c)): + eps = 1.0e-3 + + step = np.zeros_like(x_c) + step[i] = eps + + x_1 = x_c + step + x_0 = x_c - step + + param.assign(x_1) + l_1 = self.compute_loss().numpy()[0] + + param.assign(x_0) + l_0 = self.compute_loss().numpy()[0] + + dldx = (l_1 - l_0) / (eps * 2.0) + + x_grad_numeric[i] = dldx + + # reset initial state + param.assign(x_c) + + # compute analytic gradient + tape = wp.Tape() + with tape: + l = self.compute_loss() + + tape.backward(l) + + x_grad_analytic = tape.gradients[param] + + print(f"numeric grad: {x_grad_numeric}") + print(f"analytic grad: {x_grad_analytic}") + + if self.render: + self.render_iter(0) + + tape.zero() + + def trajectory(self): + xy = [] + for s in self.states: + pos = s.particle_q.numpy()[:, :2] + vel = s.particle_qd.numpy()[:, :2] + xy.append(np.concatenate((pos, vel), axis=-1)) + return np.array(xy) + + def train( + self, + iters, + clip=False, + norm=False, + zero_order=False, + tol=1e-4, + ): + losses = [] + trajectories = [] + grad_norms = [] + with trange(iters) as t: + for i in t: + tape = wp.Tape() + + with wp.ScopedTimer("Forward", active=self.profile): + with tape: + self.compute_loss() + self.sum_loss() + + with wp.ScopedTimer("Backward", active=self.profile): + if not zero_order: + tape.backward(self.l) + + if self.render: + with wp.ScopedTimer("Render", active=self.profile): + self.render_iter(i) + + with wp.ScopedTimer("Step", active=self.profile): + # need to take the mean of first qd + x = self.states[0].particle_qd + x = x.numpy() + x = x.mean(axis=0) + + # need to take the mean of gradients + if zero_order: + loss = self.loss.numpy() + baseline = loss[0] + x_grad = ( + 1 + / self.std**2 + * (loss[..., None] - baseline) + * self.noise_ + ) + x_grad = x_grad.mean(axis=0) + else: + x_grad = tape.gradients[self.states[0].particle_qd] + x_grad = x_grad.numpy() + x_grad = x_grad.mean(axis=0) + + if clip: + x_grad = np.clip(x_grad, -clip, clip) + + if norm: + x_grad = norm * x_grad / np.linalg.norm(x_grad) + + losses.append(self.loss.numpy()) + trajectories.append(self.trajectory()) + + grad_norm = np.linalg.norm(x_grad) + grad_norms.append(grad_norm) + + t.set_postfix( + loss=self.l.numpy()[0].round(4) / self.num_envs, + grad_norm=grad_norm, + ) + + # apply it to the initial state + x = x - x_grad * self.train_rate + + # then add noise again and set to environments + x = x + self.noise() + self.states[0].particle_qd = wp.from_numpy( + x, dtype=wp.vec3, requires_grad=True + ) + + # clear grad and loss for next iteration + tape.zero() + self.loss = wp.zeros_like(self.loss, requires_grad=True) + self.l = wp.zeros_like(self.l, requires_grad=True) + + # early stopping + # if len(losses) > 2: + # if np.abs(losses[-2].mean() - losses[-1].mean()) < tol: + # print("Early stopping at iter", i) + # break + + return np.array(losses), np.array(trajectories), np.array(grad_norms) + + def train_graph(self, iters, clip=False, norm=False, tol=1e-4): + # capture forward/backward passes + wp.capture_begin() + + tape = wp.Tape() + with tape: + self.compute_loss() + self.sum_loss() + + tape.backward(self.l) + + self.graph = wp.capture_end() + + # replay and optimize + losses = [] + trajectories = [] + last_l = 0.0 + + for i in range(iters): + with wp.ScopedTimer("Step", active=self.profile): + # forward + backward + wp.capture_launch(self.graph) + # # count number of contact changes: + # contact_count = self.contact_count.numpy() + # contact_changes = np.sum( + # np.abs(contact_count - self.prev_contact_count) + # ) + # self.prev_contact_count = contact_count + # self.contact_count.zero_() + + # gradient descent step + x = self.states[0].particle_qd + self.x_grad = wp.zeros(1, dtype=wp.vec3) + + wp.launch( + self.mean_kernel, + dim=len(x), + inputs=[x.grad, self.x_grad, self.num_envs], + device=self.device, + ) + + print(f"Iter: {i} Loss: {self.l.numpy() - last_l}") + print(f"Grad: {self.x_grad}") + last_l = self.l.numpy() + + wp.launch( + self.step_kernel, + dim=len(x), + inputs=[x, self.x_grad, self.train_rate], + device=self.device, + ) + + # logging + losses.append(self.loss.numpy()) + trajectories.append(self.trajectory()) + + # clear grads for next iteration + tape.zero() + + if len(losses) > 2: + if np.abs(losses[-1].mean() - losses[-2].mean()) < tol: + print("Early stopping at iter", i) + break + + if self.render: + with wp.ScopedTimer("Render", active=self.profile): + self.render_iter(i) + + return np.array(losses), np.array(trajectories) diff --git a/ball_env/grad_bounce.py b/ball_env/grad_bounce.py new file mode 100644 index 00000000..9ff01b89 --- /dev/null +++ b/ball_env/grad_bounce.py @@ -0,0 +1,92 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm + +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + N = 128 + H = 40 + clip = 5.0 + + w = np.random.normal(0.0, std, (N, m)) + w[0] = 0.0 # for baseline + ww = np.append(w, np.zeros((N, 1)), axis=1) + + fobgs = [] + zobgs = [] + losses = [] + baseline = [] + + for h in tqdm(range(1, H + 1)): + env = Bounce(num_envs=N, num_steps=h, profile=False, render=False) + + param = env.states[0].particle_qd + + tape = wp.Tape() + with tape: + loss = env.compute_loss() + l = env.sum_loss() + tape.backward(l) + fobg = tape.gradients[param].numpy() + fobg = fobg[:, :2] + # gradient clipping by value + # print(np.sum(np.abs(fobg) > clip)) + # fobg = np.clip(fobg, -clip, clip) + + # gradient clipping by norm + # if np.any(fobg > clip): + # fobg = clip * fobg / np.linalg.norm(fobg) + tape.zero() + loss = loss.numpy() + + losses.append(loss) + baseline.append(loss[0]) + zobg = 1 / std**2 * (loss[..., None] - loss[0]) * w + zobgs.append(zobg) + fobgs.append(fobg) + + # env.render_iter(0) # render last interation + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "bounce_grads_{:}".format(H, clip) + filename = f"{directory}/{filename}" + print("Saving to", filename) + np.savez( + filename, + h=np.arange(0, H), + zobgs=zobgs, + fobgs=fobgs, + losses=losses, + baseline=baseline, + std=std, + n=n, + m=m, + ) + + # bounce.check_grad() + # bounce.train_graph() + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_jacobians.py b/ball_env/grad_bounce_jacobians.py new file mode 100644 index 00000000..1f18b2af --- /dev/null +++ b/ball_env/grad_bounce_jacobians.py @@ -0,0 +1,87 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + N = 128 + H = 40 + + env = Bounce(num_envs=N, num_steps=H, std=std) + + # get last model jacobians + print("Computing dynamics jacobians") + jacs = [] + + for i in tqdm(range(env.sim_steps)): + tape = wp.Tape() + with tape: + env.states[i].clear_forces() + + env.integrator.simulate( + env.model, env.states[i], env.states[i + 1], env.sim_dt + ) + + # For each timestep compute the jacobian + # due to the way backprop works, we have to compute it per output dimension + jacobian = np.empty((N, 6, 6), dtype=np.float32) + for out_idx in range(3): + # env.state[i].particle_q should be [N, ] shape + # we want them to be + # select which row of the Jacobian we want to compute + select_index = np.zeros(3) + select_index[out_idx] = 1.0 + e = wp.array(np.tile(select_index, N), dtype=wp.vec3) + # pass input gradients to the output buffer to apply selection + tape.backward(grads={env.states[i + 1].particle_q: e}) + dq_dq = tape.gradients[env.states[i].particle_q] + dq_dqd = tape.gradients[env.states[i].particle_qd] + tape.zero() + tape.backward(grads={env.states[i + 1].particle_qd: e}) + dqd_dq = tape.gradients[env.states[i].particle_q] + dqd_dqd = tape.gradients[env.states[i].particle_qd] + jacobian[:, out_idx, :3] = dq_dq.numpy() + jacobian[:, out_idx, 3:6] = dqd_dq.numpy() + jacobian[:, out_idx + 3, :3] = dq_dqd.numpy() + jacobian[:, out_idx + 3, 3:6] = dqd_dqd.numpy() + tape.zero() + + jacs.append(jacobian) + + jacs = np.array(jacs) + print("Jacobian has shape", jacs.shape) + + xy = [] + for state in env.states: + xy.append(state.particle_q.numpy()) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "jacobians" + filename = f"{directory}/{filename}" + print("Saving to", filename) + + np.savez(filename, xy=xy, jacobians=jacs, std=std, n=n, m=m, H=H) + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_jacobians_sweep.py b/ball_env/grad_bounce_jacobians_sweep.py new file mode 100644 index 00000000..073d03fb --- /dev/null +++ b/ball_env/grad_bounce_jacobians_sweep.py @@ -0,0 +1,119 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + N = 128 + H = 40 + + w = np.random.normal(0.0, std, (N, m)) + w[0] = 0.0 # for baseline + w = np.append(w, np.zeros((N, 1)), axis=1) + + # iterate over different parametarisations + sweeps = [ + { + "soft_contact_ke": 1e4, + "soft_contact_kf": 1e0, + "soft_contact_kd": 1e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 3e4, + "soft_contact_kf": 3e0, + "soft_contact_kd": 3e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 5e4, + "soft_contact_kf": 5e0, + "soft_contact_kd": 5e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 1e5, + "soft_contact_kf": 1e1, + "soft_contact_kd": 1e2, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + ] + + results = [] + + for i, params in enumerate(sweeps): + print("Sweep", i) + + env = Bounce(num_envs=N, num_steps=H, std=std, **params) + + # get last model jacobians + jacs = [] + for i in tqdm(range(env.sim_steps)): + tape = wp.Tape() + with tape: + env.states[i].clear_forces() + wp.sim.collide(env.model, env.states[i]) + env.integrator.simulate( + env.model, env.states[i], env.states[i + 1], env.sim_dt + ) + + # For each timestep compute the jacobian + # due to the way backprop works, we have to compute it per output dimension + jacobian = np.empty((N, 6, 6), dtype=np.float32) + for out_idx in range(3): + select_index = np.zeros(3) + select_index[out_idx] = 1.0 + e = wp.array(np.tile(select_index, N), dtype=wp.vec3) + tape.backward(grads={env.states[i + 1].particle_q: e}) + dq_dq = tape.gradients[env.states[i].particle_q] + dq_dqd = tape.gradients[env.states[i].particle_qd] + tape.zero() + tape.backward(grads={env.states[i + 1].particle_qd: e}) + dqd_dq = tape.gradients[env.states[i].particle_q] + dqd_dqd = tape.gradients[env.states[i].particle_qd] + jacobian[:, out_idx, :3] = dq_dq.numpy() + jacobian[:, out_idx, 3:6] = dqd_dq.numpy() + jacobian[:, out_idx + 3, :3] = dq_dqd.numpy() + jacobian[:, out_idx + 3, 3:6] = dqd_dqd.numpy() + tape.zero() + + jacs.append(jacobian) + + result = {"jacobians": np.array(jacs), "trajectories": env.trajectory()} + result.update(params) + results.append(result) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "bounce_grads_{:}".format(H, clip) + filename = f"{directory}/{filename}" + print("Saving to", filename) + + np.save("jacobians_sweep", results) + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_optimize_clipping.py b/ball_env/grad_bounce_optimize_clipping.py new file mode 100644 index 00000000..f331ba17 --- /dev/null +++ b/ball_env/grad_bounce_optimize_clipping.py @@ -0,0 +1,65 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm + +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + N = 1024 + H = 40 + + env = Bounce(std=std, num_envs=N, num_steps=H, profile=False, render=False) + z_losses, z_trajectories, z_norms = env.train(200, zero_order=True) + + env = Bounce(std=std, num_envs=N, num_steps=H, profile=False, render=False) + losses, trajectories, norms = env.train(200) + + env = Bounce(std=std, num_envs=N, num_steps=H, profile=False, render=False) + losses_clip, trajectories_clip, norms_clip = env.train(200, clip=1.0) + + env = Bounce(std=std, num_envs=N, num_steps=H, profile=False, render=False) + losses_norm, trajectories_norm, norms_norm = env.train(200, norm=1.0) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "bounce_optimization_edge" + filename = f"{directory}/{filename}" + print("Saving to", filename) + np.savez( + "bounce_optimization_edge.npz", + z_losses=z_losses, + z_trajectories=z_trajectories, + z_norms=z_norms, + losses=losses, + trajectories=trajectories, + norms=norms, + losses_clip=losses_clip, + trajectories_clip=trajectories_clip, + norms_clip=norms_clip, + losses_norm=losses_norm, + trajectories_norm=trajectories_norm, + norms_norm=norms_norm, + ) + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_optimize_sample_sweep.py b/ball_env/grad_bounce_optimize_sample_sweep.py new file mode 100644 index 00000000..55c848ac --- /dev/null +++ b/ball_env/grad_bounce_optimize_sample_sweep.py @@ -0,0 +1,96 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm +import torch +from time import time + +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 0.5 + n = 2 + m = 2 + ndim = 30 + N = 50 + H = 40 + + params = { + "soft_contact_ke": 5e4, + "soft_contact_kf": 5e0, + "soft_contact_kd": 5e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + } + + results = [] + + print("Collecting landscape") + sweeps = [10, 20, 50, 100, 200, 500, 1000] + for i, N in tqdm(enumerate(sweeps)): + print("Sweep {:}/{:}".format(i + 1, len(sweeps))) + + temp_N = ndim * ndim + env = Bounce(num_envs=temp_N, num_steps=H, std=0.0, **params) + + # plot landscape + print("Collecting landscape") + xx = np.linspace(5, 15, ndim) + yy = np.linspace(-10, 0, ndim) + vels = [] + for i, x in enumerate(xx): + for j, y in enumerate(yy): + vels.append((x, y, 0.0)) + vels = np.array(vels) + env.reset(start_vel=vels) + landscape = env.compute_loss().numpy() + + print("First-order optimisation") + env = Bounce(num_envs=N, num_steps=H, std=std, **params) + losses, trajectories, grad_norms = env.train(300) + + print("Zero-order optimisation") + env = Bounce(num_envs=N, num_steps=H, std=std, **params) + zero_losses, zero_trajectories, zero_norms = env.train(1000, zero_order=True) + + result = { + "landscape": landscape, + "losses": losses, + "grad_norms": grad_norms, + "trajectories": trajectories, + "zero_losses": zero_losses, + "zero_grad_norms": zero_norms, + "zero_trajectories": zero_trajectories, + "N": N, + "H": H, + "std": std, + } + results.append(result) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = f"landscapes_samples_{std:.1f}" + filename = f"{directory}/{filename}" + print("Saving to", filename) + + # TODO make this saving more space efficient + np.save(filename, results) + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_optimize_stiffness_sweep.py b/ball_env/grad_bounce_optimize_stiffness_sweep.py new file mode 100644 index 00000000..f737dc1f --- /dev/null +++ b/ball_env/grad_bounce_optimize_stiffness_sweep.py @@ -0,0 +1,118 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm +import torch +from time import time + +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + ndim = 30 + N = 1024 + H = 40 + + # iterate over different parametarisations + sweeps = [ + { + "soft_contact_ke": 1e4, + "soft_contact_kf": 1e0, + "soft_contact_kd": 1e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 3e4, + "soft_contact_kf": 3e0, + "soft_contact_kd": 3e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 5e4, + "soft_contact_kf": 5e0, + "soft_contact_kd": 5e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 1e5, + "soft_contact_kf": 1e1, + "soft_contact_kd": 1e2, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + ] + + results = [] + + print("Collecting landscape") + for i, params in tqdm(enumerate(sweeps[:1])): + print("Sweep {:}/{:}".format(i + 1, len(sweeps))) + + temp_N = ndim * ndim + env = Bounce(num_envs=temp_N, num_steps=H, std=0.0, render=True, **params) + + # plot landscape + print("Collecting landscape") + xx = np.linspace(0, 15, ndim) + yy = np.linspace(-10, 5, ndim) + vels = [] + for i, x in enumerate(xx): + for j, y in enumerate(yy): + vels.append((x, y, 0.0)) + vels = np.array(vels) + env.reset(start_vel=vels) + landscape = env.compute_loss().numpy() + landscape_traj = env.trajectory() + env.render_iter(0) + + print("First-order optimisation") + env = Bounce(num_envs=N, num_steps=H, std=std, **params) + losses, trajectories, _ = env.train(200) + + print("Zero-order optimisation") + env = Bounce(num_envs=N, num_steps=H, std=std, **params) + zero_losses, zero_trajectories, _ = env.train(500, zero_order=True) + + result = { + "landscape": landscape, + "landscape_trajectories": landscape_traj, + "losses": losses, + "trajectories": trajectories, + "zero_losses": zero_losses, + "zero_trajectories": zero_trajectories, + } + result.update(params) + results.append(result) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = f"landscapes_{std:.1f}" + filename = f"{directory}/{filename}" + print("Saving to", filename) + + # TODO make this saving more space efficient + np.save(filename, results) + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_bounce_sim_sweep.py b/ball_env/grad_bounce_sim_sweep.py new file mode 100644 index 00000000..f5fe4f75 --- /dev/null +++ b/ball_env/grad_bounce_sim_sweep.py @@ -0,0 +1,148 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import os +import numpy as np +from tqdm import tqdm + +from bounce_env import Bounce + +import warp as wp +import warp.sim +import warp.sim.render + + +def main(): + np.random.seed(0) + + std = 1e-1 + n = 2 + m = 2 + N = 128 + H = 40 + clip = 5.0 + + w = np.random.normal(0.0, std, (N, m)) + w[0] = 0.0 # for baseline + ww = np.append(w, np.zeros((N, 1)), axis=1) + + sweeps = [ + { + "soft_contact_ke": 1e4, + "soft_contact_kf": 1e0, + "soft_contact_kd": 1e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 2e4, + "soft_contact_kf": 2e0, + "soft_contact_kd": 2e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 3e4, + "soft_contact_kf": 3e0, + "soft_contact_kd": 3e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 5e4, + "soft_contact_kf": 5e0, + "soft_contact_kd": 5e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 7e4, + "soft_contact_kf": 7e0, + "soft_contact_kd": 7e1, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 1e5, + "soft_contact_kf": 1e1, + "soft_contact_kd": 1e2, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + { + "soft_contact_ke": 2e5, + "soft_contact_kf": 2e1, + "soft_contact_kd": 2e2, + "soft_contact_mu": 0.9, + "soft_contact_margin": 1e1, + }, + ] + + results = [] + + for i, params in enumerate(sweeps): + print("Sweep", i) + + fobgs = [] + zobgs = [] + losses = [] + baseline = [] + + for h in tqdm(range(1, H + 1)): + env = Bounce(N, h, profile=False, render=False, **params) + + param = env.states[0].particle_qd + + tape = wp.Tape() + with tape: + loss = env.compute_loss() + l = env.sum_loss() + tape.backward(l) + fobg = tape.gradients[param].numpy() + fobg = fobg[:, :2] + # gradient clipping by value + # print(np.sum(np.abs(fobg) > clip)) + # fobg = np.clip(fobg, -clip, clip) + + # gradient clipping by norm + # if np.any(fobg > clip): + # fobg = clip * fobg / np.linalg.norm(fobg) + tape.zero() + loss = loss.numpy() + + losses.append(loss) + baseline.append(loss[0]) + zobg = 1 / std**2 * (loss[..., None] - loss[0]) * w + zobgs.append(zobg) + fobgs.append(fobg) + + result = { + "zobgs": np.array(zobgs), + "fobgs": np.array(fobgs), + "losses": np.array(losses), + "baseline": np.array(baseline), + } + result.update(params) + results.append(result) + + # env.render_iter(0) # render last interation + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "bounce_grads_{:}_sim_sweep".format(H, clip) + filename = f"{directory}/{filename}" + print("Saving to", filename) + np.save(filename, results) + + # bounce.check_grad() + # bounce.train_graph() + + +if __name__ == "__main__": + main() diff --git a/ball_env/grad_plot.py b/ball_env/grad_plot.py new file mode 100644 index 00000000..561a0f00 --- /dev/null +++ b/ball_env/grad_plot.py @@ -0,0 +1,123 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import seaborn as sns +from numpy.linalg import norm + +sns.set() + + +def norm_variance(arr: np.ndarray): + assert len(arr.shape) == 3, arr.shape + return np.mean( + norm(arr - np.mean(arr, axis=1, keepdims=True), ord=2, axis=-1) ** 2, + axis=1, + ) + + +def max_variance(arr: np.ndarray): + assert len(arr.shape) == 3, arr.shape + return np.max( + norm(arr - np.mean(arr, axis=1, keepdims=True), ord=2, axis=-1) ** 2, + axis=1, + ) + + +filename = "outputs/bounce_grads_40.npz" +print("Loading", filename) + +data = np.load(filename) +fobgs = data["fobgs"] +zobgs = data["zobgs"] +loss = data["losses"] +baseline = data["baseline"] +zobgs = np.nan_to_num(zobgs) +zobgs_no_grad = data["zobgs_no_grad"] if "zobgs_no_grad" in data else None +if zobgs_no_grad is not None: + print("Found no grad!") + +if hasattr(data, "h"): + hh = data["h"] + H = len(hh) +else: + H = zobgs.shape[0] + hh = np.arange(H) + +N = zobgs.shape[1] +th_dim = zobgs.shape[2] +n = data["n"] +m = data["m"] +std = data["std"] +print(f"Loaded grads with H={H}, N={N} n={n} m={m} std={std}") + +grad_names = [] +for j in range(m): + grad_names.extend([f"zobgs_{j}", f"fobgs_{j}", f"zobgs_no_grad_{j}"]) +columns = ["H", "loss"] + grad_names +df = pd.DataFrame(columns=columns) + +for i in range(H): + d = {"H": i + 1, "loss": loss[i]} + for j in range(m): + d.update({f"zobgs_{j}": zobgs[i, :, j], f"fobgs_{j}": fobgs[i, :, j]}) + if zobgs_no_grad is not None: + d.update({f"zobgs_no_grad_{j}": zobgs_no_grad[i, :, j]}) + df = pd.concat((df, pd.DataFrame(d))) +df = df.explode(["loss"] + grad_names) +df = df.reset_index() + +print("Plotting") +f, ax = plt.subplots(2, 2, figsize=(12, 8)) +f.suptitle(filename.replace(".npz", "")) + +# 1. Plot bias +diff = zobgs.mean(axis=1) - fobgs.mean(axis=1) +bias_l2 = norm(diff, ord=2, axis=-1) +bias_l1 = norm(diff, ord=1, axis=-1) +ax[0, 0].plot(hh, bias_l2, label="L2 Bias") +ax[0, 0].plot(hh, bias_l1, label="L1 Bias") +ax[0, 0].set_title("FoBG bias wrt ZoBG") +ax[0, 0].legend() +ax[0, 0].set_xlabel("H") + +# 2. Plot gradient estiamtes +for j in range(m): + sns.lineplot( + df, x="H", y=f"zobgs_{j}", ax=ax[1, 0], errorbar="sd", label=f"ZoBGs {j}" + ) + sns.lineplot( + df, x="H", y=f"fobgs_{j}", ax=ax[1, 0], errorbar="sd", label=f"FoBGs {j}" + ) + if zobgs_no_grad is not None: + sns.lineplot( + df, + x="H", + y=f"zobgs_no_grad_{j}", + ax=ax[1, 0], + errorbar="sd", + label=f"True ZoBGs {j}", + ) +ax[1, 0].set_title("Gradient estimate wrt action") +ax[1, 0].set_ylabel(None) +ax[1, 0].legend() + +# 3. Plot variance +ax[0, 1].plot(hh, norm_variance(zobgs), label="ZoBGs") +ax[0, 1].plot(hh, norm_variance(fobgs), label="FoBGs") +if zobgs_no_grad is not None: + ax[0, 1].plot(hh, norm_variance(zobgs_no_grad), label="True ZoBGs") +ax[0, 1].plot(hh, hh**3 * m / (N * std**2), label="Lemma 3.10") +ax[0, 1].set_yscale("log") +ax[0, 1].set_xlabel("H") +ax[0, 1].set_title("Gradient variance") +ax[0, 1].legend() + +# 4. Plot loss +sns.lineplot(df, x="H", y="loss", ax=ax[1, 1], errorbar="sd", label="Loss") +ax[1, 1].plot(hh, baseline, label="Baseline") +ax[1, 1].legend() + +plt.tight_layout() +filename = filename.replace(".npz", ".pdf") +print("Saving to {:}".format(filename)) +plt.savefig(filename) diff --git a/dflex/dflex/adjoint.py b/dflex/dflex/adjoint.py index 8dc26bfa..62f3c20e 100755 --- a/dflex/dflex/adjoint.py +++ b/dflex/dflex/adjoint.py @@ -21,7 +21,7 @@ import copy # Todo -#----- +# ----- # # [ ] Unary ops (e.g.: -) # [ ] Inplace ops (e.g.: +=, -=) @@ -37,7 +37,7 @@ cuda_functions = {} kernels = {} -#---------------------- +# ---------------------- # built-in types @@ -87,7 +87,7 @@ def __init__(self): class spatial_transform: def __init__(self): pass - + class void: def __init__(self): @@ -101,7 +101,7 @@ def __init__(self, type): self.__name__ = "tensor<" + type.__name__ + ">" -#---------------------- +# ---------------------- # register built-in function @@ -115,7 +115,7 @@ def insert(func): return insert -#--------------------------------- +# --------------------------------- # built-in operators +,-,*,/ @@ -145,9 +145,9 @@ class MulFunc: @staticmethod def value_type(args): # todo: encode type operator type globally - if (args[0].type == mat33 and args[1].type == float3): + if args[0].type == mat33 and args[1].type == float3: return float3 - if (args[0].type == spatial_matrix and args[1].type == spatial_vector): + if args[0].type == spatial_matrix and args[1].type == spatial_vector: return spatial_vector else: return args[0].type @@ -160,7 +160,7 @@ def value_type(args): return args[0].type -#---------------------- +# ---------------------- # map operator nodes to builtin operators[ast.Add] = "add" @@ -177,11 +177,10 @@ def value_type(args): operators[ast.Eq] = "==" operators[ast.NotEq] = "!=" -#---------------------- +# ---------------------- # built-in functions - @builtin("min") class MinFunc: @staticmethod @@ -223,12 +222,14 @@ class StepFunc: def value_type(args): return float + @builtin("nonzero") class NonZeroFunc: @staticmethod def value_type(args): return float + @builtin("sign") class SignFunc: @staticmethod @@ -277,6 +278,7 @@ class CosFunc: def value_type(args): return float + @builtin("sqrt") class SqrtFunc: @staticmethod @@ -284,7 +286,6 @@ def value_type(args): return float - @builtin("dot") class DotFunc: @staticmethod @@ -298,6 +299,7 @@ class CrossFunc: def value_type(args): return float3 + @builtin("skew") class SkewFunc: @staticmethod @@ -358,9 +360,9 @@ def value_type(args): class LoadFunc: @staticmethod def value_type(args): - if (type(args[0].type) != tensor): + if type(args[0].type) != tensor: raise Exception("Load input 0 must be a tensor") - if (args[1].type != int): + if args[1].type != int: raise Exception("Load input 1 must be a int") return args[0].type.type @@ -370,11 +372,11 @@ def value_type(args): class StoreFunc: @staticmethod def value_type(args): - if (type(args[0].type) != tensor): + if type(args[0].type) != tensor: raise Exception("Store input 0 must be a tensor") - if (args[1].type != int): + if args[1].type != int: raise Exception("Store input 1 must be a int") - if (args[2].type != args[0].type.type): + if args[2].type != args[0].type.type: raise Exception("Store input 2 must be of the same type as the tensor") return None @@ -408,6 +410,7 @@ class floatFunc: def value_type(args): return float + @builtin("int") class IntFunc: @staticmethod @@ -478,6 +481,7 @@ class TransformIdentity: def value_type(args): return spatial_transform + @builtin("inverse") class Inverse: @staticmethod @@ -498,95 +502,111 @@ class TransformGetTranslation: def value_type(args): return float3 + @builtin("spatial_transform_get_rotation") class TransformGetRotation: @staticmethod def value_type(args): return quat + @builtin("spatial_transform_multiply") class TransformMulFunc: @staticmethod def value_type(args): return spatial_transform + # @builtin("spatial_transform_inertia") # class TransformInertiaFunc: # @staticmethod # def value_type(args): # return spatial_matrix + @builtin("spatial_adjoint") class SpatialAdjoint: @staticmethod def value_type(args): return spatial_matrix + @builtin("spatial_dot") class SpatialDotFunc: @staticmethod def value_type(args): return float + @builtin("spatial_cross") class SpatialDotFunc: @staticmethod def value_type(args): return spatial_vector + @builtin("spatial_cross_dual") class SpatialDotFunc: @staticmethod def value_type(args): return spatial_vector + @builtin("spatial_transform_point") class SpatialTransformPointFunc: @staticmethod def value_type(args): return float3 + @builtin("spatial_transform_vector") class SpatialTransformVectorFunc: @staticmethod def value_type(args): return float3 + @builtin("spatial_top") class SpatialTopFunc: @staticmethod def value_type(args): return float3 + @builtin("spatial_bottom") class SpatialBottomFunc: @staticmethod def value_type(args): return float3 + @builtin("spatial_jacobian") class SpatialJacobian: @staticmethod def value_type(args): return None - + + @builtin("spatial_mass") class SpatialMass: @staticmethod def value_type(args): return None + @builtin("dense_gemm") class DenseGemm: @staticmethod def value_type(args): return None + @builtin("dense_gemm_batched") class DenseGemmBatched: @staticmethod def value_type(args): - return None + return None + @builtin("dense_chol") class DenseChol: @@ -594,11 +614,13 @@ class DenseChol: def value_type(args): return None + @builtin("dense_chol_batched") class DenseCholBatched: @staticmethod def value_type(args): - return None + return None + @builtin("dense_subs") class DenseSubs: @@ -606,20 +628,24 @@ class DenseSubs: def value_type(args): return None + @builtin("dense_solve") class DenseSolve: @staticmethod def value_type(args): return None + @builtin("dense_solve_batched") class DenseSolve: @staticmethod def value_type(args): - return None + return None + # helpers + @builtin("index") class IndexFunc: @staticmethod @@ -636,7 +662,6 @@ def value_type(args): class Var: def __init__(adj, label, type, requires_grad=False, constant=None): - adj.label = label adj.type = type adj.requires_grad = requires_grad @@ -646,7 +671,7 @@ def __str__(adj): return adj.label def ctype(self): - if (isinstance(self.type, tensor)): + if isinstance(self.type, tensor): if self.type.type == float3: return str("df::" + self.type.type.__name__) + "*" @@ -657,38 +682,39 @@ def ctype(self): return str(self.type.__name__) -#-------------------- +# -------------------- # Storage class for partial AST up to a return statement. class Stmt: def __init__(self, cond, forward, forward_replay, reverse, ret_forward, ret_line): - self.cond = cond # condition, can be None - self.forward = forward # all forward code outside of conditional branch *since last return* + self.cond = cond # condition, can be None + self.forward = forward # all forward code outside of conditional branch *since last return* self.forward_replay = forward_replay - self.reverse = reverse # all reverse code including the reverse of any code in ret_forward + self.reverse = ( + reverse # all reverse code including the reverse of any code in ret_forward + ) - self.ret_forward = ret_forward # all forward commands in the return statement except the actual return statement - self.ret_line = ret_line # actual return statement + self.ret_forward = ret_forward # all forward commands in the return statement except the actual return statement + self.ret_line = ret_line # actual return statement -#------------------------------------------------------------------------ +# ------------------------------------------------------------------------ # Source code transformer, this class takes a Python function and # computes its adjoint using single-pass translation of the function's AST class Adjoint: - def __init__(adj, func, device='cpu'): - + def __init__(adj, func, device="cpu"): adj.func = func adj.device = device - adj.symbols = {} # map from symbols to adjoint variables - adj.variables = [] # list of local variables (in order) - adj.args = [] # list of function arguments (in order) + adj.symbols = {} # map from symbols to adjoint variables + adj.variables = [] # list of local variables (in order) + adj.args = [] # list of function arguments (in order) - adj.cond = None # condition variable if in branch - adj.return_var = None # return type for function or kernel + adj.cond = None # condition variable if in branch + adj.return_var = None # return type for function or kernel # build AST from function object adj.source = inspect.getsource(func) @@ -720,7 +746,6 @@ def __init__(adj, func, device='cpu'): # code generation methods def format_template(adj, template, input_vars, output_var): - # output var is always the 0th index args = [output_var] + input_vars s = template.format(*args) @@ -747,14 +772,12 @@ def add_var(adj, type=None, constant=None): return v def add_constant(adj, n): - output = adj.add_var(type=type(n), constant=n) - #adj.add_forward("var_{} = {};".format(output, n)) + # adj.add_forward("var_{} = {};".format(output, n)) return output def add_load(adj, input): - output = adj.add_var(input.type) adj.add_forward("var_{} = {};".format(output, input)) @@ -763,7 +786,6 @@ def add_load(adj, input): return output def add_operator(adj, op, inputs): - # todo: just using first input as the output type, would need some # type inference here to support things like float3 = float*float3 @@ -794,45 +816,66 @@ def add_comp(adj, op_strings, left, comps): def add_bool_op(adj, op_string, exprs): output = adj.add_var(bool) - command = "var_" + str(output) + " = " + (" " + op_string + " ").join(["var_" + str(expr) for expr in exprs]) + ";" + command = ( + "var_" + + str(output) + + " = " + + (" " + op_string + " ").join(["var_" + str(expr) for expr in exprs]) + + ";" + ) adj.add_forward(command) return output - def add_call(adj, func, inputs, prefix='df::'): + def add_call(adj, func, inputs, prefix="df::"): # expression (zero output), e.g.: tid() - if (func.value_type(inputs) == None): - - forward_call = prefix + "{}({});".format(func.key, adj.format_args("var_", inputs)) + if func.value_type(inputs) == None: + forward_call = prefix + "{}({});".format( + func.key, adj.format_args("var_", inputs) + ) adj.add_forward(forward_call) - if (len(inputs)): - reverse_call = prefix + "{}({}, {});".format("adj_" + func.key, adj.format_args("var_", inputs), adj.format_args("adj_", inputs)) + if len(inputs): + reverse_call = prefix + "{}({}, {});".format( + "adj_" + func.key, + adj.format_args("var_", inputs), + adj.format_args("adj_", inputs), + ) adj.add_reverse(reverse_call) return None # function (one output) else: - output = adj.add_var(func.value_type(inputs)) - forward_call = "var_{} = ".format(output) + prefix + "{}({});".format(func.key, adj.format_args("var_", inputs)) + forward_call = ( + "var_{} = ".format(output) + + prefix + + "{}({});".format(func.key, adj.format_args("var_", inputs)) + ) adj.add_forward(forward_call) - if (len(inputs)): + if len(inputs): reverse_call = prefix + "{}({}, {}, {});".format( - "adj_" + func.key, adj.format_args("var_", inputs), adj.format_args("adj_", inputs), adj.format_args("adj_", [output])) + "adj_" + func.key, + adj.format_args("var_", inputs), + adj.format_args("adj_", inputs), + adj.format_args("adj_", [output]), + ) adj.add_reverse(reverse_call) return output def add_return(adj, var): - - if (var == None): - adj.add_forward("return;".format(var), "goto label{};".format(adj.label_count)) + if var == None: + adj.add_forward( + "return;".format(var), "goto label{};".format(adj.label_count) + ) else: - adj.add_forward("return var_{};".format(var), "goto label{};".format(adj.label_count)) + adj.add_forward( + "return var_{};".format(var), "goto label{};".format(adj.label_count) + ) adj.add_reverse("adj_" + str(var) + " += adj_ret;") adj.add_reverse("label{}:;".format(adj.label_count)) @@ -841,14 +884,12 @@ def add_return(adj, var): # define an if statement def begin_if(adj, cond): - adj.add_forward("if (var_{}) {{".format(cond)) adj.add_reverse("}") adj.indent_count += 1 def end_if(adj, cond): - adj.indent_count -= 1 adj.add_forward("}") @@ -856,23 +897,29 @@ def end_if(adj, cond): # define a for-loop def begin_for(adj, iter, start, end): - # note that dynamic for-loops must not mutate any previous state, so we don't need to re-run them in the reverse pass - adj.add_forward("for (var_{0}=var_{1}; var_{0} < var_{2}; ++var_{0}) {{".format(iter, start, end), "if (false) {") + adj.add_forward( + "for (var_{0}=var_{1}; var_{0} < var_{2}; ++var_{0}) {{".format( + iter, start, end + ), + "if (false) {", + ) adj.add_reverse("}") adj.indent_count += 1 def end_for(adj, iter, start, end): - adj.indent_count -= 1 adj.add_forward("}") - adj.add_reverse("for (var_{0}=var_{2}-1; var_{0} >= var_{1}; --var_{0}) {{".format(iter, start, end)) + adj.add_reverse( + "for (var_{0}=var_{2}-1; var_{0} >= var_{1}; --var_{0}) {{".format( + iter, start, end + ) + ) # append a statement to the forward pass def add_forward(adj, statement, statement_replay=None): - prefix = "" for i in range(adj.indent_count): prefix += "\t" @@ -880,14 +927,13 @@ def add_forward(adj, statement, statement_replay=None): adj.body_forward.append(prefix + statement) # allow for different statement in reverse kernel replay - if (statement_replay): + if statement_replay: adj.body_forward_replay.append(prefix + statement_replay) else: adj.body_forward_replay.append(prefix + statement) # append a statement to the reverse pass def add_reverse(adj, statement): - prefix = "" for i in range(adj.indent_count): prefix += "\t" @@ -895,27 +941,37 @@ def add_reverse(adj, statement): adj.body_reverse.append(prefix + statement) def eval(adj, node): - try: - - if (isinstance(node, ast.FunctionDef)): - + if isinstance(node, ast.FunctionDef): out = None for f in node.body: out = adj.eval(f) - if 'return' in adj.symbols and adj.symbols['return'] is not None: - out = adj.symbols['return'] - stmt = Stmt(None, adj.body_forward, adj.body_forward_replay, reversed(adj.body_reverse), [], "") + if "return" in adj.symbols and adj.symbols["return"] is not None: + out = adj.symbols["return"] + stmt = Stmt( + None, + adj.body_forward, + adj.body_forward_replay, + reversed(adj.body_reverse), + [], + "", + ) adj.output.append(stmt) else: - stmt = Stmt(None, adj.body_forward, adj.body_forward_replay, reversed(adj.body_reverse), [], "") + stmt = Stmt( + None, + adj.body_forward, + adj.body_forward_replay, + reversed(adj.body_reverse), + [], + "", + ) adj.output.append(stmt) return out - elif (isinstance(node, ast.If)): # if statement - + elif isinstance(node, ast.If): # if statement if len(node.orelse) != 0: raise SyntaxError("Else statements not currently supported") @@ -938,7 +994,6 @@ def eval(adj, node): # detect symbols with conflicting definitions (assigned inside the branch) for items in symbols_prev.items(): - sym = items[0] var1 = items[1] var2 = adj.symbols[sym] @@ -951,7 +1006,7 @@ def eval(adj, node): return None - elif (isinstance(node, ast.Compare)): + elif isinstance(node, ast.Compare): # node.left, node.ops (list of ops), node.comparators (things to compare to) # e.g. (left ops[0] node.comparators[0]) ops[1] node.comparators[1] @@ -963,7 +1018,7 @@ def eval(adj, node): return out - elif (isinstance(node, ast.BoolOp)): + elif isinstance(node, ast.BoolOp): # op, expr list values (e.g. and and a list of things anded together) op = node.op @@ -981,30 +1036,29 @@ def eval(adj, node): return out - elif (isinstance(node, ast.Name)): + elif isinstance(node, ast.Name): # lookup symbol, if it has already been assigned to a variable then return the existing mapping - if (node.id in adj.symbols): + if node.id in adj.symbols: return adj.symbols[node.id] else: raise KeyError("Referencing undefined symbol: " + str(node.id)) - elif (isinstance(node, ast.Num)): - + elif isinstance(node, ast.Num): # lookup constant, if it has already been assigned then return existing var - # currently disabled, since assigning constant in a branch means it + # currently disabled, since assigning constant in a branch means it key = (node.n, type(node.n)) - if (key in adj.symbols): + if key in adj.symbols: return adj.symbols[key] else: out = adj.add_constant(node.n) adj.symbols[key] = out return out - #out = adj.add_constant(node.n) - #return out + # out = adj.add_constant(node.n) + # return out - elif (isinstance(node, ast.BinOp)): + elif isinstance(node, ast.BinOp): # evaluate binary operator arguments left = adj.eval(node.left) right = adj.eval(node.right) @@ -1015,33 +1069,32 @@ def eval(adj, node): out = adj.add_call(func, [left, right]) return out - elif (isinstance(node, ast.UnaryOp)): + elif isinstance(node, ast.UnaryOp): # evaluate unary op arguments arg = adj.eval(node.operand) out = adj.add_operator(node.op, [arg]) return out - elif (isinstance(node, ast.For)): - - if (len(node.iter.args) != 2): - raise Exception("For loop ranges must be of form range(start, end) with both start and end specified and no skip specifier.") + elif isinstance(node, ast.For): + if len(node.iter.args) != 2: + raise Exception( + "For loop ranges must be of form range(start, end) with both start and end specified and no skip specifier." + ) # check if loop range is compile time constant unroll = True for a in node.iter.args: - if (isinstance(a, ast.Num) == False): + if isinstance(a, ast.Num) == False: unroll = False break - if (unroll): - + if unroll: # constant loop, unroll start = node.iter.args[0].n end = node.iter.args[1].n for i in range(start, end): - var_iter = adj.add_constant(i) adj.symbols[node.target.id] = var_iter @@ -1049,7 +1102,6 @@ def eval(adj, node): for s in node.body: adj.eval(s) else: - # dynamic loop, body must be side-effect free, i.e.: not # overwrite memory locations used by previous operations start = adj.eval(node.iter.args[0]) @@ -1067,24 +1119,23 @@ def eval(adj, node): adj.end_for(iter, start, end) - elif (isinstance(node, ast.Expr)): + elif isinstance(node, ast.Expr): return adj.eval(node.value) - elif (isinstance(node, ast.Call)): - + elif isinstance(node, ast.Call): name = None # determine if call is to a builtin (attribute), or to a user-func (name) - if (isinstance(node.func, ast.Attribute)): + if isinstance(node.func, ast.Attribute): name = node.func.attr - elif (isinstance(node.func, ast.Name)): + elif isinstance(node.func, ast.Name): name = node.func.id # check it exists if name not in functions: raise KeyError("Could not find function {}".format(name)) - if adj.device == 'cuda' and name in cuda_functions: + if adj.device == "cuda" and name in cuda_functions: func = cuda_functions[name] else: func = functions[name] @@ -1100,7 +1151,7 @@ def eval(adj, node): out = adj.add_call(func, args, prefix=func.prefix) return out - elif (isinstance(node, ast.Subscript)): + elif isinstance(node, ast.Subscript): target = adj.eval(node.value) indices = [] @@ -1118,7 +1169,7 @@ def eval(adj, node): out = adj.add_call(functions["index"], [target, *indices]) return out - elif (isinstance(node, ast.Assign)): + elif isinstance(node, ast.Assign): # if adj.cond is not None: # raise SyntaxError("error, cannot assign variables in a conditional branch") @@ -1129,15 +1180,18 @@ def eval(adj, node): adj.symbols[node.targets[0].id] = out return out - elif (isinstance(node, ast.Return)): + elif isinstance(node, ast.Return): cond = adj.cond # None if not in branch, else branch boolean out = adj.eval(node.value) - adj.symbols['return'] = out + adj.symbols["return"] = out - if out is not None: # set return type of function + if out is not None: # set return type of function return_var = out - if adj.return_var is not None and adj.return_var.ctype() != return_var.ctype(): + if ( + adj.return_var is not None + and adj.return_var.ctype() != return_var.ctype() + ): raise TypeError("error, function returned different types") adj.return_var = return_var @@ -1150,17 +1204,25 @@ def eval(adj, node): print("[WARNING] ast node of type {} not supported".format(type(node))) except Exception as e: - # print error / line number lines = adj.source.splitlines() - print("Error: {} while transforming node {} in func: {} at line: {} col: {}: \n {}".format(e, type(node), adj.func.__name__, node.lineno, node.col_offset, lines[max(node.lineno-1, 0)])) + print( + "Error: {} while transforming node {} in func: {} at line: {} col: {}: \n {}".format( + e, + type(node), + adj.func.__name__, + node.lineno, + node.col_offset, + lines[max(node.lineno - 1, 0)], + ) + ) raise -#---------------- +# ---------------- # code generation -cpu_module_header = ''' +cpu_module_header = """ #define CPU #include "adjoint.h" @@ -1173,9 +1235,9 @@ def eval(adj, node): return (T)(t.data_ptr()); }} -''' +""" -cuda_module_header = ''' +cuda_module_header = """ #define CUDA #include "adjoint.h" @@ -1188,9 +1250,9 @@ def eval(adj, node): return (T)(t.data_ptr()); }} -''' +""" -cpu_function_template = ''' +cpu_function_template = """ {return_type} {name}_cpu_func({forward_args}) {{ {forward_body} @@ -1201,9 +1263,9 @@ def eval(adj, node): {reverse_body} }} -''' +""" -cuda_function_template = ''' +cuda_function_template = """ CUDA_CALLABLE {return_type} {name}_cuda_func({forward_args}) {{ {forward_body} @@ -1214,9 +1276,9 @@ def eval(adj, node): {reverse_body} }} -''' +""" -cuda_kernel_template = ''' +cuda_kernel_template = """ __global__ void {name}_cuda_kernel_forward(int dim, {forward_args}) {{ @@ -1228,9 +1290,9 @@ def eval(adj, node): {reverse_body} }} -''' +""" -cpu_kernel_template = ''' +cpu_kernel_template = """ void {name}_cpu_kernel_forward({forward_args}) {{ @@ -1242,9 +1304,9 @@ def eval(adj, node): {reverse_body} }} -''' +""" -cuda_module_template = ''' +cuda_module_template = """ // Python entry points void {name}_cuda_forward(int dim, {forward_args}) @@ -1263,9 +1325,9 @@ def eval(adj, node): //check_cuda(cudaDeviceSynchronize()); }} -''' +""" -cpu_module_template = ''' +cpu_module_template = """ // Python entry points void {name}_cpu_forward(int dim, {forward_args}) @@ -1288,23 +1350,23 @@ def eval(adj, node): }} }} -''' +""" -cuda_module_header_template = ''' +cuda_module_header_template = """ // Python entry points void {name}_cuda_forward(int dim, {forward_args}); void {name}_cuda_backward(int dim, {forward_args}, {reverse_args}); -''' +""" -cpu_module_header_template = ''' +cpu_module_header_template = """ // Python entry points void {name}_cpu_forward(int dim, {forward_args}); void {name}_cpu_backward(int dim, {forward_args}, {reverse_args}); -''' +""" def indent(args, stops=1): @@ -1315,7 +1377,7 @@ def indent(args, stops=1): return sep + args.replace(", ", "," + sep) -def codegen_func_forward_body(adj, device='cpu', indent=4): +def codegen_func_forward_body(adj, device="cpu", indent=4): body = [] indent_block = " " * indent @@ -1341,29 +1403,36 @@ def codegen_func_forward_body(adj, device='cpu', indent=4): return "".join([indent_block + l for l in body]) -def codegen_func_forward(adj, func_type='kernel', device='cpu'): +def codegen_func_forward(adj, func_type="kernel", device="cpu"): s = "" # primal vars s += " //---------\n" s += " // primal vars\n" - for var in adj.variables: + for var in adj.variables: if var.constant == None: s += " " + var.ctype() + " var_" + str(var.label) + ";\n" else: - s += " const " + var.ctype() + " var_" + str(var.label) + " = " + str(var.constant) + ";\n" - + s += ( + " const " + + var.ctype() + + " var_" + + str(var.label) + + " = " + + str(var.constant) + + ";\n" + ) # forward pass s += " //---------\n" s += " // forward\n" - if device == 'cpu': + if device == "cpu": s += codegen_func_forward_body(adj, device=device, indent=4) - elif device == 'cuda': - if func_type == 'kernel': + elif device == "cuda": + if func_type == "kernel": s += " int var_idx = blockDim.x * blockIdx.x + threadIdx.x;\n" s += " if (var_idx < dim) {\n" @@ -1376,7 +1445,7 @@ def codegen_func_forward(adj, func_type='kernel', device='cpu'): return s -def codegen_func_reverse_body(adj, device='cpu', indent=4): +def codegen_func_reverse_body(adj, device="cpu", indent=4): body = [] indent_block = " " * indent @@ -1419,7 +1488,7 @@ def codegen_func_reverse_body(adj, device='cpu', indent=4): return "".join([indent_block + l for l in body]) -def codegen_func_reverse(adj, func_type='kernel', device='cpu'): +def codegen_func_reverse(adj, func_type="kernel", device="cpu"): s = "" # primal vars @@ -1430,7 +1499,15 @@ def codegen_func_reverse(adj, func_type='kernel', device='cpu'): if var.constant == None: s += " " + var.ctype() + " var_" + str(var.label) + ";\n" else: - s += " const " + var.ctype() + " var_" + str(var.label) + " = " + str(var.constant) + ";\n" + s += ( + " const " + + var.ctype() + + " var_" + + str(var.label) + + " = " + + str(var.constant) + + ";\n" + ) # dual vars s += " //---------\n" @@ -1439,10 +1516,10 @@ def codegen_func_reverse(adj, func_type='kernel', device='cpu'): for var in adj.variables: s += " " + var.ctype() + " adj_" + str(var.label) + " = 0;\n" - if device == 'cpu': + if device == "cpu": s += codegen_func_reverse_body(adj, device=device, indent=4) - elif device == 'cuda': - if func_type == 'kernel': + elif device == "cuda": + if func_type == "kernel": s += " int var_idx = blockDim.x * blockIdx.x + threadIdx.x;\n" s += " if (var_idx < dim) {\n" s += codegen_func_reverse_body(adj, device=device, indent=8) @@ -1455,12 +1532,11 @@ def codegen_func_reverse(adj, func_type='kernel', device='cpu'): return s -def codegen_func(adj, device='cpu'): - +def codegen_func(adj, device="cpu"): # forward header # return_type = "void" - return_type = 'void' if adj.return_var is None else adj.return_var.ctype() + return_type = "void" if adj.return_var is None else adj.return_var.ctype() # s = "{} {}_forward(".format(return_type, adj.func.__name__) @@ -1516,28 +1592,29 @@ def codegen_func(adj, device='cpu'): # s += sep + str(adj.symbols['return'].type.__name__) + " adj_" + str(adj.symbols['return']) # codegen body - forward_body = codegen_func_forward(adj, func_type='function', device=device) - reverse_body = codegen_func_reverse(adj, func_type='function', device=device) + forward_body = codegen_func_forward(adj, func_type="function", device=device) + reverse_body = codegen_func_reverse(adj, func_type="function", device=device) - if device == 'cpu': + if device == "cpu": template = cpu_function_template - elif device == 'cuda': + elif device == "cuda": template = cuda_function_template else: raise ValueError("Device {} is not supported".format(device)) - s = template.format(name=adj.func.__name__, - return_type=return_type, - forward_args=indent(forward_args), - reverse_args=indent(reverse_args), - forward_body=forward_body, - reverse_body=reverse_body) + s = template.format( + name=adj.func.__name__, + return_type=return_type, + forward_args=indent(forward_args), + reverse_args=indent(reverse_args), + forward_body=forward_body, + reverse_body=reverse_body, + ) return s -def codegen_kernel(adj, device='cpu'): - +def codegen_kernel(adj, device="cpu"): forward_args = "" reverse_args = "" @@ -1554,30 +1631,31 @@ def codegen_kernel(adj, device='cpu'): sep = ", " # codegen body - forward_body = codegen_func_forward(adj, func_type='kernel', device=device) - reverse_body = codegen_func_reverse(adj, func_type='kernel', device=device) + forward_body = codegen_func_forward(adj, func_type="kernel", device=device) + reverse_body = codegen_func_reverse(adj, func_type="kernel", device=device) # import pdb # pdb.set_trace() - if device == 'cpu': + if device == "cpu": template = cpu_kernel_template - elif device == 'cuda': + elif device == "cuda": template = cuda_kernel_template else: raise ValueError("Device {} is not supported".format(device)) - s = template.format(name=adj.func.__name__, - forward_args=indent(forward_args), - reverse_args=indent(reverse_args), - forward_body=forward_body, - reverse_body=reverse_body) + s = template.format( + name=adj.func.__name__, + forward_args=indent(forward_args), + reverse_args=indent(reverse_args), + forward_body=forward_body, + reverse_body=reverse_body, + ) return s -def codegen_module(adj, device='cpu'): - +def codegen_module(adj, device="cpu"): forward_args = "" reverse_args = "" @@ -1586,7 +1664,7 @@ def codegen_module(adj, device='cpu'): sep = "" for arg in adj.args: - if (isinstance(arg.type, tensor)): + if isinstance(arg.type, tensor): forward_args += sep + "torch::Tensor var_" + arg.label forward_params += sep + "cast<" + arg.ctype() + ">(var_" + arg.label + ")" else: @@ -1597,7 +1675,7 @@ def codegen_module(adj, device='cpu'): sep = "" for arg in adj.args: - if (isinstance(arg.type, tensor)): + if isinstance(arg.type, tensor): reverse_args += sep + "torch::Tensor adj_" + arg.label reverse_params += sep + "cast<" + arg.ctype() + ">(adj_" + arg.label + ")" else: @@ -1606,23 +1684,24 @@ def codegen_module(adj, device='cpu'): sep = ", " - if device == 'cpu': + if device == "cpu": template = cpu_module_template - elif device == 'cuda': + elif device == "cuda": template = cuda_module_template else: raise ValueError("Device {} is not supported".format(device)) - s = template.format(name=adj.func.__name__, - forward_args=indent(forward_args), - reverse_args=indent(reverse_args), - forward_params=indent(forward_params, 3), - reverse_params=indent(reverse_params, 3)) + s = template.format( + name=adj.func.__name__, + forward_args=indent(forward_args), + reverse_args=indent(reverse_args), + forward_params=indent(forward_params, 3), + reverse_params=indent(reverse_params, 3), + ) return s -def codegen_module_decl(adj, device='cpu'): - +def codegen_module_decl(adj, device="cpu"): forward_args = "" reverse_args = "" @@ -1631,7 +1710,7 @@ def codegen_module_decl(adj, device='cpu'): sep = "" for arg in adj.args: - if (isinstance(arg.type, tensor)): + if isinstance(arg.type, tensor): forward_args += sep + "torch::Tensor var_" + arg.label forward_params += sep + "cast<" + arg.ctype() + ">(var_" + arg.label + ")" else: @@ -1642,7 +1721,7 @@ def codegen_module_decl(adj, device='cpu'): sep = "" for arg in adj.args: - if (isinstance(arg.type, tensor)): + if isinstance(arg.type, tensor): reverse_args += sep + "torch::Tensor adj_" + arg.label reverse_params += sep + "cast<" + arg.ctype() + ">(adj_" + arg.label + ")" else: @@ -1651,20 +1730,24 @@ def codegen_module_decl(adj, device='cpu'): sep = ", " - if device == 'cpu': + if device == "cpu": template = cpu_module_header_template - elif device == 'cuda': + elif device == "cuda": template = cuda_module_header_template else: raise ValueError("Device {} is not supported".format(device)) - s = template.format(name=adj.func.__name__, forward_args=indent(forward_args), reverse_args=indent(reverse_args)) + s = template.format( + name=adj.func.__name__, + forward_args=indent(forward_args), + reverse_args=indent(reverse_args), + ) return s # runs vcvars and copies back the build environment, PyTorch should really be doing this def set_build_env(): - if os.name == 'nt': + if os.name == "nt": # VS2019 (required for PyTorch headers) vcvars_path = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\Build\\vcvars64.bat" @@ -1672,14 +1755,13 @@ def set_build_env(): output = os.popen(s).read() for line in output.splitlines(): pair = line.split("=", 1) - if (len(pair) >= 2): + if len(pair) >= 2: os.environ[pair[0]] = pair[1] - else: # nothing needed for Linux or Mac + else: # nothing needed for Linux or Mac pass def import_module(module_name, path): - # https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path file, path, description = imp.find_module(module_name, [path]) @@ -1721,22 +1803,23 @@ def func(f): def kernel(f): - # stores source and compiled entry points for a kernel (will be populated after module loads) class Kernel: def __init__(self, f): - self.func = f def register(self, module): - # lookup entry points based on name self.forward_cpu = eval("module." + self.func.__name__ + "_cpu_forward") self.backward_cpu = eval("module." + self.func.__name__ + "_cpu_backward") - if (torch.cuda.is_available()): - self.forward_cuda = eval("module." + self.func.__name__ + "_cuda_forward") - self.backward_cuda = eval("module." + self.func.__name__ + "_cuda_backward") + if torch.cuda.is_available(): + self.forward_cuda = eval( + "module." + self.func.__name__ + "_cuda_forward" + ) + self.backward_cuda = eval( + "module." + self.func.__name__ + "_cuda_backward" + ) k = Kernel(f) @@ -1762,11 +1845,11 @@ def compile(): # functions for name, func in user_funcs.items(): - adj = Adjoint(func, device='cpu') - cpp_source += codegen_func(adj, device='cpu') + adj = Adjoint(func, device="cpu") + cpp_source += codegen_func(adj, device="cpu") - adj = Adjoint(func, device='cuda') - cuda_source += codegen_func(adj, device='cuda') + adj = Adjoint(func, device="cuda") + cuda_source += codegen_func(adj, device="cuda") # import pdb # pdb.set_trace() @@ -1800,32 +1883,31 @@ def value_type(cls, *args): entry_points.append(name + "_cpu_backward") if use_cuda: - adj = Adjoint(kernel.func, device='cuda') - cuda_source += codegen_kernel(adj, device='cuda') - cuda_source += codegen_module(adj, device='cuda') - cpp_source += codegen_module_decl(adj, device='cuda') + adj = Adjoint(kernel.func, device="cuda") + cuda_source += codegen_kernel(adj, device="cuda") + cuda_source += codegen_module(adj, device="cuda") + cpp_source += codegen_module_decl(adj, device="cuda") - adj = Adjoint(kernel.func, device='cpu') - cpp_source += codegen_kernel(adj, device='cpu') - cpp_source += codegen_module(adj, device='cpu') - cpp_source += codegen_module_decl(adj, device='cpu') + adj = Adjoint(kernel.func, device="cpu") + cpp_source += codegen_kernel(adj, device="cpu") + cpp_source += codegen_module(adj, device="cpu") + cpp_source += codegen_module_decl(adj, device="cpu") include_path = os.path.dirname(os.path.realpath(__file__)) build_path = os.path.dirname(os.path.realpath(__file__)) + "/kernels" cache_file = build_path + "/adjoint.gen" - if (os.path.exists(build_path) == False): + if os.path.exists(build_path) == False: os.mkdir(build_path) # test cache - if (os.path.exists(cache_file)): - - f = open(cache_file, 'r') + if os.path.exists(cache_file): + f = open(cache_file, "r") cache_string = f.read() f.close() - if (cache_string == cpp_source): + if cache_string == cpp_source: print("Using cached kernels") module = import_module("kernels", build_path) @@ -1845,49 +1927,53 @@ def value_type(cls, *args): set_build_env() # debug config - #module = torch.utils.cpp_extension.load_inline('kernels', [cpp_source], None, entry_points, extra_cflags=["/Zi", "/Od"], extra_ldflags=["/DEBUG"], build_directory=build_path, extra_include_paths=[include_path], verbose=True) + # module = torch.utils.cpp_extension.load_inline('kernels', [cpp_source], None, entry_points, extra_cflags=["/Zi", "/Od"], extra_ldflags=["/DEBUG"], build_directory=build_path, extra_include_paths=[include_path], verbose=True) - if os.name == 'nt': + if os.name == "nt": cpp_flags = ["/Ox", "-DNDEBUG", "/fp:fast"] ld_flags = ["-DNDEBUG"] -# cpp_flags = ["/Zi", "/Od", "/DEBUG"] -# ld_flags = ["/DEBUG"] + # cpp_flags = ["/Zi", "/Od", "/DEBUG"] + # ld_flags = ["/DEBUG"] else: cpp_flags = ["-Z", "-O2", "-DNDEBUG"] ld_flags = ["-DNDEBUG"] # just use minimum to ensure compatability - cuda_flags = ['-gencode=arch=compute_35,code=compute_35'] + cuda_flags = ["-gencode=arch=compute_35,code=compute_35"] # release config if use_cuda: - module = torch.utils.cpp_extension.load_inline('kernels', - cpp_sources=[cpp_source], - cuda_sources=[cuda_source], - functions=entry_points, - extra_cflags=cpp_flags, - extra_ldflags=ld_flags, - extra_cuda_cflags=cuda_flags, - build_directory=build_path, - extra_include_paths=[include_path], - verbose=True, - with_pytorch_error_handling=False) + module = torch.utils.cpp_extension.load_inline( + "kernels", + cpp_sources=[cpp_source], + cuda_sources=[cuda_source], + functions=entry_points, + extra_cflags=cpp_flags, + extra_ldflags=ld_flags, + extra_cuda_cflags=cuda_flags, + build_directory=build_path, + extra_include_paths=[include_path], + verbose=True, + with_pytorch_error_handling=False, + ) else: - module = torch.utils.cpp_extension.load_inline('kernels', - cpp_sources=[cpp_source], - cuda_sources=[], - functions=entry_points, - extra_cflags=cpp_flags, - extra_ldflags=ld_flags, - extra_cuda_cflags=cuda_flags, - build_directory=build_path, - extra_include_paths=[include_path], - verbose=True, - with_pytorch_error_handling=False) + module = torch.utils.cpp_extension.load_inline( + "kernels", + cpp_sources=[cpp_source], + cuda_sources=[], + functions=entry_points, + extra_cflags=cpp_flags, + extra_ldflags=ld_flags, + extra_cuda_cflags=cuda_flags, + build_directory=build_path, + extra_include_paths=[include_path], + verbose=True, + with_pytorch_error_handling=False, + ) # update cache - f = open(cache_file, 'w') + f = open(cache_file, "w") f.write(cpp_source) f.close() @@ -1898,36 +1984,31 @@ def value_type(cls, *args): return module - - - - - - -#--------------------------------------------- +# --------------------------------------------- # Helper functions for launching kernels as Torch ops -def check_adapter(l, a): +def check_adapter(l, a): for t in l: if torch.is_tensor(t): - assert(t.device.type == a) + assert t.device.type == a + def check_finite(l): for t in l: if torch.is_tensor(t): - assert(t.is_contiguous()) + assert t.is_contiguous() - if (torch.isnan(t).any() == True): + if torch.isnan(t).any() == True: print(t) - assert(torch.isnan(t).any() == False) + assert torch.isnan(t).any() == False else: - assert(math.isnan(t) == False) + assert math.isnan(t) == False def filter_grads(grads): """helper that takes a list of gradient tensors and makes non-outputs None - as required by PyTorch when returning from a custom op + as required by PyTorch when returning from a custom op """ outputs = [] @@ -1941,7 +2022,6 @@ def filter_grads(grads): def make_empty(outputs, device): - empty = [] for o in outputs: @@ -1951,14 +2031,13 @@ def make_empty(outputs, device): def make_contiguous(grads): - ret = [] for g in grads: ret.append(g.contiguous()) return ret - + def copy_params(params): out = [] for p in params: @@ -1983,17 +2062,32 @@ def assert_device(device, inputs): for arg in inputs: if isinstance(arg, torch.Tensor): if (arg.dtype == torch.float64) or (arg.dtype == torch.float16): - raise TypeError("Tensor {arg} has invalid dtype {dtype}".format(arg=arg, dtype=arg.dtype)) - - if device == 'cpu': - if arg.is_cuda: # make sure all tensors are on the right device. Can fail silently in the CUDA kernel. - raise TypeError("Tensor {arg} is using CUDA but was expected to be on the CPU.".format(arg=arg)) - elif torch.device(device).type == 'cuda': #elif device.startswith('cuda'): + raise TypeError( + "Tensor {arg} has invalid dtype {dtype}".format( + arg=arg, dtype=arg.dtype + ) + ) + + if device == "cpu": + if ( + arg.is_cuda + ): # make sure all tensors are on the right device. Can fail silently in the CUDA kernel. + raise TypeError( + "Tensor {arg} is using CUDA but was expected to be on the CPU.".format( + arg=arg + ) + ) + elif torch.device(device).type == "cuda": # elif device.startswith('cuda'): if not arg.is_cuda: - raise TypeError("Tensor {arg} is not on a CUDA device but was expected to be using CUDA.".format(arg=arg)) + raise TypeError( + "Tensor {arg} is not on a CUDA device but was expected to be using CUDA.".format( + arg=arg + ) + ) else: raise ValueError("Device {} is not supported".format(device)) + def to_weak_list(s): w = [] for o in s: @@ -2001,30 +2095,38 @@ def to_weak_list(s): return w + def to_strong_list(w): s = [] for o in w: s.append(o()) - + return s # standalone method to launch a kernel using PyTorch graph (skip custom tape) -def launch_torch(func, dim, inputs, outputs, adapter, preserve_output=False, check_grad=False, no_grad=False): - +def launch_torch( + func, + dim, + inputs, + outputs, + adapter, + preserve_output=False, + check_grad=False, + no_grad=False, +): num_inputs = len(inputs) num_outputs = len(outputs) - + # define autograd type class TorchFunc(torch.autograd.Function): @staticmethod def forward(ctx, *args): - - #local_inputs = args[0:num_inputs] - #local_outputs = args[num_inputs:len(args)] + # local_inputs = args[0:num_inputs] + # local_outputs = args[num_inputs:len(args)] # save for backward - #ctx.inputs = list(local_inputs) + # ctx.inputs = list(local_inputs) ctx.inputs = args local_outputs = [] @@ -2037,9 +2139,11 @@ def forward(ctx, *args): assert_device(adapter, args) # launch - if adapter == 'cpu': + if adapter == "cpu": func.forward_cpu(*[dim, *args, *ctx.outputs]) - elif torch.device(adapter).type == 'cuda': #elif adapter.startswith('cuda'): + elif ( + torch.device(adapter).type == "cuda" + ): # elif adapter.startswith('cuda'): func.forward_cuda(*[dim, *args, *ctx.outputs]) ret = tuple(ctx.outputs) @@ -2047,7 +2151,6 @@ def forward(ctx, *args): @staticmethod def backward(ctx, *grads): - # ensure grads are contiguous in memory adj_outputs = make_contiguous(grads) @@ -2069,7 +2172,7 @@ def backward(ctx, *grads): # print (" inputs") # for i in ctx.inputs: # print(i) - + # print (" outputs") # for o in ctx.outputs: # print(o) @@ -2083,10 +2186,16 @@ def backward(ctx, *grads): # print(adj_o) # launch - if adapter == 'cpu': - func.backward_cpu(*[dim, *ctx.inputs, *local_outputs, *adj_inputs, *adj_outputs]) - elif torch.device(adapter).type == 'cuda': #elif adapter.startswith('cuda'): - func.backward_cuda(*[dim, *ctx.inputs, *local_outputs, *adj_inputs, *adj_outputs]) + if adapter == "cpu": + func.backward_cpu( + *[dim, *ctx.inputs, *local_outputs, *adj_inputs, *adj_outputs] + ) + elif ( + torch.device(adapter).type == "cuda" + ): # elif adapter.startswith('cuda'): + func.backward_cuda( + *[dim, *ctx.inputs, *local_outputs, *adj_inputs, *adj_outputs] + ) # filter grads replaces empty tensors / constant params with None ret = list(filter_grads(adj_inputs)) @@ -2101,9 +2210,16 @@ def backward(ctx, *grads): torch.set_printoptions(edgeitems=3) - if (check_grad == True and no_grad == False): + if check_grad == True and no_grad == False: try: - torch.autograd.gradcheck(TorchFunc.apply, params, eps=1e-2, atol=1e-3, rtol=1.e-3, raise_exception=True) + torch.autograd.gradcheck( + TorchFunc.apply, + params, + eps=1e-2, + atol=1e-3, + rtol=1.0e-3, + raise_exception=True, + ) except Exception as e: print(str(func.func.__name__) + " failed: " + str(e)) @@ -2113,21 +2229,26 @@ def backward(ctx, *grads): class Tape: def __init__(self): - self.launches = [] # dictionary mapping Tensor inputs to their adjoint self.adjoints = {} - - def launch(self, func, dim, inputs, outputs, adapter, preserve_output=False, skip_check_grad=False): - - if (dim > 0): - + def launch( + self, + func, + dim, + inputs, + outputs, + adapter, + preserve_output=False, + skip_check_grad=False, + ): + if dim > 0: # run kernel - if adapter == 'cpu': + if adapter == "cpu": func.forward_cpu(*[dim, *inputs, *outputs]) - elif torch.device(adapter).type == 'cuda': #adapter.startswith('cuda'): + elif torch.device(adapter).type == "cuda": # adapter.startswith('cuda'): func.forward_cuda(*[dim, *inputs, *outputs]) if dflex.config.verify_fp: @@ -2138,26 +2259,32 @@ def launch(self, func, dim, inputs, outputs, adapter, preserve_output=False, ski # record launch if dflex.config.no_grad == False: - self.launches.append([func, dim, inputs, outputs, adapter, preserve_output]) + self.launches.append( + [func, dim, inputs, outputs, adapter, preserve_output] + ) # optionally run grad check if dflex.config.check_grad == True and skip_check_grad == False: - # copy inputs and outputs to avoid disturbing the computational graph inputs_copy = copy_params(inputs) outputs_copy = copy_params(outputs) - launch_torch(func, dim, inputs_copy, outputs_copy, adapter, preserve_output, check_grad=True) - + launch_torch( + func, + dim, + inputs_copy, + outputs_copy, + adapter, + preserve_output, + check_grad=True, + ) def replay(self): - for kernel in reversed(self.launches): - func = kernel[0] dim = kernel[1] inputs = kernel[2] - #outputs = to_strong_list(kernel[3]) + # outputs = to_strong_list(kernel[3]) outputs = kernel[3] adapter = kernel[4] @@ -2167,7 +2294,6 @@ def replay(self): # build input adjoints for i in inputs: - if i in self.adjoints: adj_inputs.append(self.adjoints[i]) else: @@ -2185,28 +2311,31 @@ def replay(self): # allocate a zero tensor (they will still be read by the kernels) adj_outputs.append(self.alloc_grad(o)) - # launch reverse - if adapter == 'cpu': + # launch reverse + if adapter == "cpu": func.backward_cpu(*[dim, *inputs, *outputs, *adj_inputs, *adj_outputs]) - elif torch.device(adapter).type == 'cuda': #elif adapter.startswith('cuda'): + elif ( + torch.device(adapter).type == "cuda" + ): # elif adapter.startswith('cuda'): func.backward_cuda(*[dim, *inputs, *outputs, *adj_inputs, *adj_outputs]) - if dflex.config.verify_fp: check_finite(inputs) check_finite(outputs) check_finite(adj_inputs) check_finite(adj_outputs) - def reset(self): - self.adjoints = {} self.launches = [] - - def alloc_grad(self, t): + def zero(self): + # print("Adjoint len", len(self.adjoints)) + + for k, v in self.adjoints.items(): + self.adjoints[k] = torch.zeros_like(v) + def alloc_grad(self, t): if t.dtype == torch.float32 and t.requires_grad: # zero tensor self.adjoints[t] = torch.zeros_like(t) @@ -2230,10 +2359,10 @@ def alloc_grads(inputs, adapter): grads = [] for arg in inputs: - if (torch.is_tensor(arg)): - if (arg.requires_grad and arg.dtype == torch.float): + if torch.is_tensor(arg): + if arg.requires_grad and arg.dtype == torch.float: grads.append(torch.zeros_like(arg, device=adapter)) - #grads.append(lookup_grad(arg)) + # grads.append(lookup_grad(arg)) else: grads.append(torch.FloatTensor().to(adapter)) else: @@ -2242,13 +2371,11 @@ def alloc_grads(inputs, adapter): return grads - def matmul(tape, m, n, k, t1, t2, A, B, C, adapter): - - if (adapter == 'cpu'): + if adapter == "cpu": threads = 1 else: - threads = 256 # should match the threadblock size + threads = 256 # should match the threadblock size tape.launch( func=dflex.eval_dense_gemm, @@ -2262,19 +2389,21 @@ def matmul(tape, m, n, k, t1, t2, A, B, C, adapter): A, B, ], - outputs=[ - C - ], + outputs=[C], adapter=adapter, - preserve_output=False) + preserve_output=False, + ) -def matmul_batched(tape, batch_count, m, n, k, t1, t2, A_start, B_start, C_start, A, B, C, adapter): - - if (adapter == 'cpu'): +def matmul_batched( + tape, batch_count, m, n, k, t1, t2, A_start, B_start, C_start, A, B, C, adapter +): + if adapter == "cpu": threads = batch_count else: - threads = 256*batch_count # must match the threadblock size used in adjoint.py + threads = ( + 256 * batch_count + ) # must match the threadblock size used in adjoint.py tape.launch( func=dflex.eval_dense_gemm_batched, @@ -2291,8 +2420,7 @@ def matmul_batched(tape, batch_count, m, n, k, t1, t2, A_start, B_start, C_start A, B, ], - outputs=[ - C - ], + outputs=[C], adapter=adapter, - preserve_output=False) \ No newline at end of file + preserve_output=False, + ) diff --git a/dflex/dflex/sim.py b/dflex/dflex/sim.py index 37da557f..0b63a249 100755 --- a/dflex/dflex/sim.py +++ b/dflex/dflex/sim.py @@ -20,9 +20,10 @@ from dflex.model import * import time +from copy import deepcopy # Todo -#----- +# ----- # # [x] Spring model # [x] 2D FEM model @@ -46,9 +47,9 @@ # externally compiled kernels module (C++/CUDA code with PyBind entry points) kernels = None + @df.func def test(c: float): - x = 1.0 y = float(2) z = int(3.0) @@ -56,10 +57,10 @@ def test(c: float): print(y) print(z) - if (c < 3.0): + if c < 3.0: x = 2.0 - return x*6.0 + return x * 6.0 def kernel_init(): @@ -68,15 +69,16 @@ def kernel_init(): @df.kernel -def integrate_particles(x: df.tensor(df.float3), - v: df.tensor(df.float3), - f: df.tensor(df.float3), - w: df.tensor(float), - gravity: df.tensor(df.float3), - dt: float, - x_new: df.tensor(df.float3), - v_new: df.tensor(df.float3)): - +def integrate_particles( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + f: df.tensor(df.float3), + w: df.tensor(float), + gravity: df.tensor(df.float3), + dt: float, + x_new: df.tensor(df.float3), + v_new: df.tensor(df.float3), +): tid = df.tid() x0 = df.load(x, tid) @@ -96,21 +98,22 @@ def integrate_particles(x: df.tensor(df.float3), # semi-implicit Euler integration @df.kernel -def integrate_rigids(rigid_x: df.tensor(df.float3), - rigid_r: df.tensor(df.quat), - rigid_v: df.tensor(df.float3), - rigid_w: df.tensor(df.float3), - rigid_f: df.tensor(df.float3), - rigid_t: df.tensor(df.float3), - inv_m: df.tensor(float), - inv_I: df.tensor(df.mat33), - gravity: df.tensor(df.float3), - dt: float, - rigid_x_new: df.tensor(df.float3), - rigid_r_new: df.tensor(df.quat), - rigid_v_new: df.tensor(df.float3), - rigid_w_new: df.tensor(df.float3)): - +def integrate_rigids( + rigid_x: df.tensor(df.float3), + rigid_r: df.tensor(df.quat), + rigid_v: df.tensor(df.float3), + rigid_w: df.tensor(df.float3), + rigid_f: df.tensor(df.float3), + rigid_t: df.tensor(df.float3), + inv_m: df.tensor(float), + inv_I: df.tensor(df.mat33), + gravity: df.tensor(df.float3), + dt: float, + rigid_x_new: df.tensor(df.float3), + rigid_r_new: df.tensor(df.quat), + rigid_v_new: df.tensor(df.float3), + rigid_w_new: df.tensor(df.float3), +): tid = df.tid() # positions @@ -119,33 +122,39 @@ def integrate_rigids(rigid_x: df.tensor(df.float3), # velocities v0 = df.load(rigid_v, tid) - w0 = df.load(rigid_w, tid) # angular velocity + w0 = df.load(rigid_w, tid) # angular velocity # forces f0 = df.load(rigid_f, tid) t0 = df.load(rigid_t, tid) # masses - inv_mass = df.load(inv_m, tid) # 1 / mass + inv_mass = df.load(inv_m, tid) # 1 / mass inv_inertia = df.load(inv_I, tid) # inverse of 3x3 inertia matrix g = df.load(gravity, 0) # linear part - v1 = v0 + (f0 * inv_mass + g * df.nonzero(inv_mass)) * dt # linear integral (linear position/velocity) + v1 = ( + v0 + (f0 * inv_mass + g * df.nonzero(inv_mass)) * dt + ) # linear integral (linear position/velocity) x1 = x0 + v1 * dt # angular part # so reverse multiplication by r0 takes you from global coordinates into local coordinates # because it's covector and thus gets pulled back rather than pushed forward - wb = df.rotate_inv(r0, w0) # angular integral (angular velocity and rotation), rotate into object reference frame - tb = df.rotate_inv(r0, t0) # also rotate torques into local coordinates + wb = df.rotate_inv( + r0, w0 + ) # angular integral (angular velocity and rotation), rotate into object reference frame + tb = df.rotate_inv(r0, t0) # also rotate torques into local coordinates # I^{-1} torque = angular acceleration and inv_inertia is always going to be in the object frame. # So we need to rotate into that frame, and then back into global. - w1 = df.rotate(r0, wb + inv_inertia * tb * dt) # I^-1 * torque * dt., then go back into global coordinates - r1 = df.normalize(r0 + df.quat(w1, 0.0) * r0 * 0.5 * dt) # rotate around w1 by dt + w1 = df.rotate( + r0, wb + inv_inertia * tb * dt + ) # I^-1 * torque * dt., then go back into global coordinates + r1 = df.normalize(r0 + df.quat(w1, 0.0) * r0 * 0.5 * dt) # rotate around w1 by dt df.store(rigid_x_new, tid, x1) df.store(rigid_r_new, tid, r1) @@ -154,14 +163,15 @@ def integrate_rigids(rigid_x: df.tensor(df.float3), @df.kernel -def eval_springs(x: df.tensor(df.float3), - v: df.tensor(df.float3), - spring_indices: df.tensor(int), - spring_rest_lengths: df.tensor(float), - spring_stiffness: df.tensor(float), - spring_damping: df.tensor(float), - f: df.tensor(df.float3)): - +def eval_springs( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + spring_indices: df.tensor(int), + spring_rest_lengths: df.tensor(float), + spring_stiffness: df.tensor(float), + spring_damping: df.tensor(float), + f: df.tensor(df.float3), +): tid = df.tid() i = df.load(spring_indices, tid * 2 + 0) @@ -197,37 +207,39 @@ def eval_springs(x: df.tensor(df.float3), @df.kernel -def eval_triangles(x: df.tensor(df.float3), - v: df.tensor(df.float3), - indices: df.tensor(int), - pose: df.tensor(df.mat22), - activation: df.tensor(float), - k_mu: float, - k_lambda: float, - k_damp: float, - k_drag: float, - k_lift: float, - f: df.tensor(df.float3)): +def eval_triangles( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + indices: df.tensor(int), + pose: df.tensor(df.mat22), + activation: df.tensor(float), + k_mu: float, + k_lambda: float, + k_damp: float, + k_drag: float, + k_lift: float, + f: df.tensor(df.float3), +): tid = df.tid() i = df.load(indices, tid * 3 + 0) j = df.load(indices, tid * 3 + 1) k = df.load(indices, tid * 3 + 2) - p = df.load(x, i) # point zero - q = df.load(x, j) # point one - r = df.load(x, k) # point two + p = df.load(x, i) # point zero + q = df.load(x, j) # point one + r = df.load(x, k) # point two - vp = df.load(v, i) # vel zero - vq = df.load(v, j) # vel one - vr = df.load(v, k) # vel two + vp = df.load(v, i) # vel zero + vq = df.load(v, j) # vel one + vr = df.load(v, k) # vel two - qp = q - p # barycentric coordinates (centered at p) + qp = q - p # barycentric coordinates (centered at p) rp = r - p Dm = df.load(pose, tid) - inv_rest_area = df.determinant(Dm) * 2.0 # 1 / det(A) = det(A^-1) + inv_rest_area = df.determinant(Dm) * 2.0 # 1 / det(A) = det(A^-1) rest_area = 1.0 / inv_rest_area # scale stiffness coefficients to account for area @@ -239,7 +251,7 @@ def eval_triangles(x: df.tensor(df.float3), f1 = qp * Dm[0, 0] + rp * Dm[1, 0] f2 = qp * Dm[0, 1] + rp * Dm[1, 1] - #----------------------------- + # ----------------------------- # St. Venant-Kirchoff # # Green strain, F'*F-I @@ -259,7 +271,7 @@ def eval_triangles(x: df.tensor(df.float3), # fr = (f1*T[0,1] + f2*T[1,1])*k_mu*2.0 # alpha = 1.0 - #----------------------------- + # ----------------------------- # Baraff & Witkin, note this model is not isotropic # c1 = length(f1) - 1.0 @@ -270,7 +282,7 @@ def eval_triangles(x: df.tensor(df.float3), # fq = f1*Dm[0,0] + f2*Dm[0,1] # fr = f1*Dm[1,0] + f2*Dm[1,1] - #----------------------------- + # ----------------------------- # Neo-Hookean (with rest stability) # force = mu*F*Dm' @@ -278,7 +290,7 @@ def eval_triangles(x: df.tensor(df.float3), fr = (f1 * Dm[1, 0] + f2 * Dm[1, 1]) * k_mu alpha = 1.0 + k_mu / k_lambda - #----------------------------- + # ----------------------------- # Area Preservation n = df.cross(qp, rp) @@ -297,7 +309,7 @@ def eval_triangles(x: df.tensor(df.float3), f_area = k_lambda * c - #----------------------------- + # ----------------------------- # Area Damping dcdt = dot(dcdq, vq) + dot(dcdr, vr) - dot(dcdq + dcdr, vp) @@ -307,14 +319,16 @@ def eval_triangles(x: df.tensor(df.float3), fr = fr + dcdr * (f_area + f_damp) fp = fq + fr - #----------------------------- + # ----------------------------- # Lift + Drag vmid = (vp + vr + vq) * 0.3333 vdir = df.normalize(vmid) f_drag = vmid * (k_drag * area * df.abs(df.dot(n, vmid))) - f_lift = n * (k_lift * area * (1.57079 - df.acos(df.dot(n, vdir)))) * dot(vmid, vmid) + f_lift = ( + n * (k_lift * area * (1.57079 - df.acos(df.dot(n, vdir)))) * dot(vmid, vmid) + ) # note reversed sign due to atomic_add below.. need to write the unary op - fp = fp - f_drag - f_lift @@ -326,8 +340,11 @@ def eval_triangles(x: df.tensor(df.float3), df.atomic_sub(f, j, fq) df.atomic_sub(f, k, fr) + @df.func -def triangle_closest_point_barycentric(a: df.float3, b: df.float3, c: df.float3, p: df.float3): +def triangle_closest_point_barycentric( + a: df.float3, b: df.float3, c: df.float3, p: df.float3 +): ab = b - a ac = c - a ap = p - a @@ -335,36 +352,36 @@ def triangle_closest_point_barycentric(a: df.float3, b: df.float3, c: df.float3, d1 = df.dot(ab, ap) d2 = df.dot(ac, ap) - if (d1 <= 0.0 and d2 <= 0.0): + if d1 <= 0.0 and d2 <= 0.0: return float3(1.0, 0.0, 0.0) bp = p - b d3 = df.dot(ab, bp) d4 = df.dot(ac, bp) - if (d3 >= 0.0 and d4 <= d3): + if d3 >= 0.0 and d4 <= d3: return float3(0.0, 1.0, 0.0) vc = d1 * d4 - d3 * d2 v = d1 / (d1 - d3) - if (vc <= 0.0 and d1 >= 0.0 and d3 <= 0.0): + if vc <= 0.0 and d1 >= 0.0 and d3 <= 0.0: return float3(1.0 - v, v, 0.0) cp = p - c d5 = dot(ab, cp) d6 = dot(ac, cp) - if (d6 >= 0.0 and d5 <= d6): + if d6 >= 0.0 and d5 <= d6: return float3(0.0, 0.0, 1.0) vb = d5 * d2 - d1 * d6 w = d2 / (d2 - d6) - if (vb <= 0.0 and d2 >= 0.0 and d6 <= 0.0): + if vb <= 0.0 and d2 >= 0.0 and d6 <= 0.0: return float3(1.0 - w, 0.0, w) va = d3 * d6 - d5 * d4 w = (d4 - d3) / ((d4 - d3) + (d5 - d6)) - if (va <= 0.0 and (d4 - d3) >= 0.0 and (d5 - d6) >= 0.0): + if va <= 0.0 and (d4 - d3) >= 0.0 and (d5 - d6) >= 0.0: return float3(0.0, w, 1.0 - w) denom = 1.0 / (va + vb + vc) @@ -373,10 +390,11 @@ def triangle_closest_point_barycentric(a: df.float3, b: df.float3, c: df.float3, return float3(1.0 - v - w, v, w) + @df.kernel def eval_triangles_contact( - # idx : df.tensor(int), # list of indices for colliding particles - num_particles: int, # size of particles + # idx : df.tensor(int), # list of indices for colliding particles + num_particles: int, # size of particles x: df.tensor(df.float3), v: df.tensor(df.float3), indices: df.tensor(int), @@ -387,26 +405,26 @@ def eval_triangles_contact( k_damp: float, k_drag: float, k_lift: float, - f: df.tensor(df.float3)): - + f: df.tensor(df.float3), +): tid = df.tid() - face_no = tid // num_particles # which face + face_no = tid // num_particles # which face particle_no = tid % num_particles # which particle # index = df.load(idx, tid) - pos = df.load(x, particle_no) # at the moment, just one particle - # vel0 = df.load(v, 0) + pos = df.load(x, particle_no) # at the moment, just one particle + # vel0 = df.load(v, 0) i = df.load(indices, face_no * 3 + 0) j = df.load(indices, face_no * 3 + 1) k = df.load(indices, face_no * 3 + 2) - if (i == particle_no or j == particle_no or k == particle_no): + if i == particle_no or j == particle_no or k == particle_no: return - p = df.load(x, i) # point zero - q = df.load(x, j) # point one - r = df.load(x, k) # point two + p = df.load(x, i) # point zero + q = df.load(x, j) # point one + r = df.load(x, k) # point two # vp = df.load(v, i) # vel zero # vq = df.load(v, j) # vel one @@ -421,8 +439,8 @@ def eval_triangles_contact( diff = pos - closest dist = df.dot(diff, diff) n = df.normalize(diff) - c = df.min(dist - 0.01, 0.0) # 0 unless within 0.01 of surface - #c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) + c = df.min(dist - 0.01, 0.0) # 0 unless within 0.01 of surface + # c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) fn = n * c * 1e5 df.atomic_sub(f, particle_no, fn) @@ -435,26 +453,26 @@ def eval_triangles_contact( @df.kernel def eval_triangles_rigid_contacts( - num_particles: int, # number of particles (size of contact_point) - x: df.tensor(df.float3), # position of particles + num_particles: int, # number of particles (size of contact_point) + x: df.tensor(df.float3), # position of particles v: df.tensor(df.float3), - indices: df.tensor(int), # triangle indices - rigid_x: df.tensor(df.float3), # rigid body positions + indices: df.tensor(int), # triangle indices + rigid_x: df.tensor(df.float3), # rigid body positions rigid_r: df.tensor(df.quat), rigid_v: df.tensor(df.float3), rigid_w: df.tensor(df.float3), contact_body: df.tensor(int), - contact_point: df.tensor(df.float3), # position of contact points relative to body + contact_point: df.tensor(df.float3), # position of contact points relative to body contact_dist: df.tensor(float), contact_mat: df.tensor(int), materials: df.tensor(float), - # rigid_f : df.tensor(df.float3), - # rigid_t : df.tensor(df.float3), - tri_f: df.tensor(df.float3)): - + # rigid_f : df.tensor(df.float3), + # rigid_t : df.tensor(df.float3), + tri_f: df.tensor(df.float3), +): tid = df.tid() - face_no = tid // num_particles # which face + face_no = tid // num_particles # which face particle_no = tid % num_particles # which particle # ----------------------- @@ -465,13 +483,13 @@ def eval_triangles_rigid_contacts( c_mat = df.load(contact_mat, particle_no) # hard coded surface parameter tensor layout (ke, kd, kf, mu) - ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient - kd = df.load(materials, c_mat * 4 + 1) # damping coefficient - kf = df.load(materials, c_mat * 4 + 2) # friction coefficient - mu = df.load(materials, c_mat * 4 + 3) # coulomb friction + ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient + kd = df.load(materials, c_mat * 4 + 1) # damping coefficient + kf = df.load(materials, c_mat * 4 + 2) # friction coefficient + mu = df.load(materials, c_mat * 4 + 3) # coulomb friction - x0 = df.load(rigid_x, c_body) # position of colliding body - r0 = df.load(rigid_r, c_body) # orientation of colliding body + x0 = df.load(rigid_x, c_body) # position of colliding body + r0 = df.load(rigid_r, c_body) # orientation of colliding body v0 = df.load(rigid_v, c_body) w0 = df.load(rigid_w, c_body) @@ -481,12 +499,16 @@ def eval_triangles_rigid_contacts( # use x0 as center, everything is offset from center of mass # moment arm - r = pos - x0 # basically just c_point in the new coordinates + r = pos - x0 # basically just c_point in the new coordinates rhat = df.normalize(r) - pos = pos + rhat * c_dist # add on 'thickness' of shape, e.g.: radius of sphere/capsule + pos = ( + pos + rhat * c_dist + ) # add on 'thickness' of shape, e.g.: radius of sphere/capsule # contact point velocity - dpdt = v0 + df.cross(w0, r) # this is rigid velocity cross offset, so it's the velocity of the contact point. + dpdt = v0 + df.cross( + w0, r + ) # this is rigid velocity cross offset, so it's the velocity of the contact point. # ----------------------- # load triangle @@ -494,51 +516,55 @@ def eval_triangles_rigid_contacts( j = df.load(indices, face_no * 3 + 1) k = df.load(indices, face_no * 3 + 2) - p = df.load(x, i) # point zero - q = df.load(x, j) # point one - r = df.load(x, k) # point two + p = df.load(x, i) # point zero + q = df.load(x, j) # point one + r = df.load(x, k) # point two - vp = df.load(v, i) # vel zero - vq = df.load(v, j) # vel one - vr = df.load(v, k) # vel two + vp = df.load(v, i) # vel zero + vq = df.load(v, j) # vel one + vr = df.load(v, k) # vel two bary = triangle_closest_point_barycentric(p, q, r, pos) closest = p * bary[0] + q * bary[1] + r * bary[2] - diff = pos - closest # vector from tri to point - dist = df.dot(diff, diff) # squared distance - n = df.normalize(diff) # points into the object - c = df.min(dist - 0.05, 0.0) # 0 unless within 0.05 of surface - #c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) - # fn = n * c * 1e6 # points towards cloth (both n and c are negative) + diff = pos - closest # vector from tri to point + dist = df.dot(diff, diff) # squared distance + n = df.normalize(diff) # points into the object + c = df.min(dist - 0.05, 0.0) # 0 unless within 0.05 of surface + # c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) + # fn = n * c * 1e6 # points towards cloth (both n and c are negative) # df.atomic_sub(tri_f, particle_no, fn) - fn = c * ke # normal force (restitution coefficient * how far inside for ground) (negative) + fn = ( + c * ke + ) # normal force (restitution coefficient * how far inside for ground) (negative) - vtri = vp * bary[0] + vq * bary[1] + vr * bary[2] # bad approximation for centroid velocity + vtri = ( + vp * bary[0] + vq * bary[1] + vr * bary[2] + ) # bad approximation for centroid velocity vrel = vtri - dpdt - vn = dot(n, vrel) # velocity component of rigid in negative normal direction - vt = vrel - n * vn # velocity component not in normal direction + vn = dot(n, vrel) # velocity component of rigid in negative normal direction + vt = vrel - n * vn # velocity component not in normal direction # contact damping - fd = 0.0 - df.max(vn, 0.0) * kd * df.step(c) # again, negative, into the ground + fd = 0.0 - df.max(vn, 0.0) * kd * df.step(c) # again, negative, into the ground # # viscous friction # ft = vt*kf # Coulomb friction (box) lower = mu * (fn + fd) - upper = 0.0 - lower # workaround because no unary ops yet + upper = 0.0 - lower # workaround because no unary ops yet - nx = cross(n, float3(0.0, 0.0, 1.0)) # basis vectors for tangent + nx = cross(n, float3(0.0, 0.0, 1.0)) # basis vectors for tangent nz = cross(n, float3(1.0, 0.0, 0.0)) vx = df.clamp(dot(nx * kf, vt), lower, upper) vz = df.clamp(dot(nz * kf, vt), lower, upper) - ft = (nx * vx + nz * vz) * (0.0 - df.step(c)) # df.float3(vx, 0.0, vz)*df.step(c) + ft = (nx * vx + nz * vz) * (0.0 - df.step(c)) # df.float3(vx, 0.0, vz)*df.step(c) # # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0) # #ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) @@ -552,8 +578,14 @@ def eval_triangles_rigid_contacts( @df.kernel def eval_bending( - x: df.tensor(df.float3), v: df.tensor(df.float3), indices: df.tensor(int), rest: df.tensor(float), ke: float, kd: float, f: df.tensor(df.float3)): - + x: df.tensor(df.float3), + v: df.tensor(df.float3), + indices: df.tensor(int), + rest: df.tensor(float), + ke: float, + kd: float, + f: df.tensor(df.float3), +): tid = df.tid() i = df.load(indices, tid * 4 + 0) @@ -573,8 +605,8 @@ def eval_bending( v3 = df.load(v, k) v4 = df.load(v, l) - n1 = df.cross(x3 - x1, x4 - x1) # normal to face 1 - n2 = df.cross(x4 - x2, x3 - x2) # normal to face 2 + n1 = df.cross(x3 - x1, x4 - x1) # normal to face 1 + n2 = df.cross(x4 - x2, x3 - x2) # normal to face 2 n1_length = df.length(n1) n2_length = df.length(n2) @@ -615,14 +647,15 @@ def eval_bending( @df.kernel -def eval_tetrahedra(x: df.tensor(df.float3), - v: df.tensor(df.float3), - indices: df.tensor(int), - pose: df.tensor(df.mat33), - activation: df.tensor(float), - materials: df.tensor(float), - f: df.tensor(df.float3)): - +def eval_tetrahedra( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + indices: df.tensor(int), + pose: df.tensor(df.mat33), + activation: df.tensor(float), + materials: df.tensor(float), + f: df.tensor(df.float3), +): tid = df.tid() i = df.load(indices, tid * 4 + 0) @@ -675,9 +708,9 @@ def eval_tetrahedra(x: df.tensor(df.float3), col2 = df.float3(F[0, 1], F[1, 1], F[2, 1]) col3 = df.float3(F[0, 2], F[1, 2], F[2, 2]) - #----------------------------- + # ----------------------------- # Neo-Hookean (with rest stability [Smith et al 2018]) - + Ic = dot(col1, col1) + dot(col2, col2) + dot(col3, col3) # deviatoric part @@ -688,25 +721,24 @@ def eval_tetrahedra(x: df.tensor(df.float3), f2 = df.float3(H[0, 1], H[1, 1], H[2, 1]) f3 = df.float3(H[0, 2], H[1, 2], H[2, 2]) - #----------------------------- + # ----------------------------- # C_spherical - + # r_s = df.sqrt(dot(col1, col1) + dot(col2, col2) + dot(col3, col3)) # r_s_inv = 1.0/r_s - # C = r_s - df.sqrt(3.0) + # C = r_s - df.sqrt(3.0) # dCdx = F*df.transpose(Dm)*r_s_inv # grad1 = float3(dCdx[0,0], dCdx[1,0], dCdx[2,0]) # grad2 = float3(dCdx[0,1], dCdx[1,1], dCdx[2,1]) # grad3 = float3(dCdx[0,2], dCdx[1,2], dCdx[2,2]) - # f1 = grad1*C*k_mu # f2 = grad2*C*k_mu # f3 = grad3*C*k_mu - #---------------------------- + # ---------------------------- # C_D # r_s = df.sqrt(dot(col1, col1) + dot(col2, col2) + dot(col3, col3)) @@ -717,16 +749,15 @@ def eval_tetrahedra(x: df.tensor(df.float3), # grad1 = float3(dCdx[0,0], dCdx[1,0], dCdx[2,0]) # grad2 = float3(dCdx[0,1], dCdx[1,1], dCdx[2,1]) # grad3 = float3(dCdx[0,2], dCdx[1,2], dCdx[2,2]) - + # f1 = grad1*C*k_mu # f2 = grad2*C*k_mu # f3 = grad3*C*k_mu - # hydrostatic part J = df.determinant(F) - #print(J) + # print(J) s = inv_rest_volume / 6.0 dJdx1 = df.cross(x20, x30) * s dJdx2 = df.cross(x30, x10) * s @@ -750,17 +781,28 @@ def eval_tetrahedra(x: df.tensor(df.float3), @df.kernel -def eval_contacts(x: df.tensor(df.float3), v: df.tensor(df.float3), ke: float, kd: float, kf: float, mu: float, f: df.tensor(df.float3)): - - tid = df.tid() # this just handles contact of particles with the ground plane, nothing else. +def eval_contacts( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + ke: float, + kd: float, + kf: float, + mu: float, + f: df.tensor(df.float3), +): + tid = ( + df.tid() + ) # this just handles contact of particles with the ground plane, nothing else. x0 = df.load(x, tid) v0 = df.load(v, tid) - n = float3(0.0, 1.0, 0.0) # why is the normal always y? Ground is always (0, 1, 0) normal + n = float3( + 0.0, 1.0, 0.0 + ) # why is the normal always y? Ground is always (0, 1, 0) normal - c = df.min(dot(n, x0) - 0.01, 0.0) # 0 unless within 0.01 of surface - #c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) + c = df.min(dot(n, x0) - 0.01, 0.0) # 0 unless within 0.01 of surface + # c = df.leaky_min(dot(n, x0)-0.01, 0.0, 0.0) vn = dot(n, v0) vt = v0 - n * vn @@ -771,7 +813,7 @@ def eval_contacts(x: df.tensor(df.float3), v: df.tensor(df.float3), ke: float, k fd = n * df.min(vn, 0.0) * kd # viscous friction - #ft = vt*kf + # ft = vt*kf # Coulomb friction (box) lower = mu * c * ke @@ -783,7 +825,7 @@ def eval_contacts(x: df.tensor(df.float3), v: df.tensor(df.float3), ke: float, k ft = df.float3(vx, 0.0, vz) # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0) - #ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) + # ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) ftotal = fn + (fd + ft) * df.step(c) @@ -792,40 +834,37 @@ def eval_contacts(x: df.tensor(df.float3), v: df.tensor(df.float3), ke: float, k @df.func def sphere_sdf(center: df.float3, radius: float, p: df.float3): + return df.length(p - center) - radius - return df.length(p-center) - radius @df.func def sphere_sdf_grad(center: df.float3, radius: float, p: df.float3): + return df.normalize(p - center) - return df.normalize(p-center) @df.func def box_sdf(upper: df.float3, p: df.float3): - # adapted from https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm - qx = abs(p[0])-upper[0] - qy = abs(p[1])-upper[1] - qz = abs(p[2])-upper[2] + qx = abs(p[0]) - upper[0] + qy = abs(p[1]) - upper[1] + qz = abs(p[2]) - upper[2] e = df.float3(df.max(qx, 0.0), df.max(qy, 0.0), df.max(qz, 0.0)) - + return df.length(e) + df.min(df.max(qx, df.max(qy, qz)), 0.0) @df.func def box_sdf_grad(upper: df.float3, p: df.float3): - - qx = abs(p[0])-upper[0] - qy = abs(p[1])-upper[1] - qz = abs(p[2])-upper[2] + qx = abs(p[0]) - upper[0] + qy = abs(p[1]) - upper[1] + qz = abs(p[2]) - upper[2] # exterior case - if (qx > 0.0 or qy > 0.0 or qz > 0.0): - - x = df.clamp(p[0], 0.0-upper[0], upper[0]) - y = df.clamp(p[1], 0.0-upper[1], upper[1]) - z = df.clamp(p[2], 0.0-upper[2], upper[2]) + if qx > 0.0 or qy > 0.0 or qz > 0.0: + x = df.clamp(p[0], 0.0 - upper[0], upper[0]) + y = df.clamp(p[1], 0.0 - upper[1], upper[1]) + z = df.clamp(p[2], 0.0 - upper[2], upper[2]) return df.normalize(p - df.float3(x, y, z)) @@ -834,91 +873,91 @@ def box_sdf_grad(upper: df.float3, p: df.float3): sz = df.sign(p[2]) # x projection - if (qx > qy and qx > qz): + if qx > qy and qx > qz: return df.float3(sx, 0.0, 0.0) - + # y projection - if (qy > qx and qy > qz): + if qy > qx and qy > qz: return df.float3(0.0, sy, 0.0) # z projection - if (qz > qx and qz > qy): + if qz > qx and qz > qy: return df.float3(0.0, 0.0, sz) + @df.func def capsule_sdf(radius: float, half_width: float, p: df.float3): - - if (p[0] > half_width): + if p[0] > half_width: return length(df.float3(p[0] - half_width, p[1], p[2])) - radius - if (p[0] < 0.0 - half_width): + if p[0] < 0.0 - half_width: return length(df.float3(p[0] + half_width, p[1], p[2])) - radius return df.length(df.float3(0.0, p[1], p[2])) - radius + @df.func def capsule_sdf_grad(radius: float, half_width: float, p: df.float3): - - if (p[0] > half_width): + if p[0] > half_width: return normalize(df.float3(p[0] - half_width, p[1], p[2])) - if (p[0] < 0.0 - half_width): + if p[0] < 0.0 - half_width: return normalize(df.float3(p[0] + half_width, p[1], p[2])) - + return normalize(df.float3(0.0, p[1], p[2])) @df.kernel def eval_soft_contacts( num_particles: int, - particle_x: df.tensor(df.float3), - particle_v: df.tensor(df.float3), + particle_x: df.tensor(df.float3), + particle_v: df.tensor(df.float3), body_X_sc: df.tensor(df.spatial_transform), body_v_sc: df.tensor(df.spatial_vector), shape_X_co: df.tensor(df.spatial_transform), shape_body: df.tensor(int), - shape_geo_type: df.tensor(int), + shape_geo_type: df.tensor(int), shape_geo_src: df.tensor(int), shape_geo_scale: df.tensor(df.float3), shape_materials: df.tensor(float), ke: float, - kd: float, - kf: float, - mu: float, + kd: float, + kf: float, + mu: float, # outputs particle_f: df.tensor(df.float3), - body_f: df.tensor(df.spatial_vector)): - - tid = df.tid() + body_f: df.tensor(df.spatial_vector), +): + tid = df.tid() - shape_index = tid // num_particles # which shape - particle_index = tid % num_particles # which particle + shape_index = tid // num_particles # which shape + particle_index = tid % num_particles # which particle rigid_index = df.load(shape_body, shape_index) px = df.load(particle_x, particle_index) pv = df.load(particle_v, particle_index) - #center = float3(0.0, 0.5, 0.0) - #radius = 0.25 - #margin = 0.01 + # center = float3(0.0, 0.5, 0.0) + # radius = 0.25 + # margin = 0.01 # sphere collider # c = df.min(sphere_sdf(center, radius, x0)-margin, 0.0) # n = sphere_sdf_grad(center, radius, x0) # box collider - #c = df.min(box_sdf(df.float3(radius, radius, radius), x0-center)-margin, 0.0) - #n = box_sdf_grad(df.float3(radius, radius, radius), x0-center) + # c = df.min(box_sdf(df.float3(radius, radius, radius), x0-center)-margin, 0.0) + # n = box_sdf_grad(df.float3(radius, radius, radius), x0-center) X_sc = df.spatial_transform_identity() - if (rigid_index >= 0): + if rigid_index >= 0: X_sc = df.load(body_X_sc, rigid_index) - + X_co = df.load(shape_X_co, shape_index) X_so = df.spatial_transform_multiply(X_sc, X_co) X_os = df.spatial_transform_inverse(X_so) - + # transform particle position to shape local space x_local = df.spatial_transform_point(X_os, px) @@ -933,25 +972,31 @@ def eval_soft_contacts( n = df.float3(0.0, 0.0, 0.0) # GEO_SPHERE (0) - if (geo_type == 0): - c = df.min(sphere_sdf(df.float3(0.0, 0.0, 0.0), geo_scale[0], x_local)-margin, 0.0) - n = df.spatial_transform_vector(X_so, sphere_sdf_grad(df.float3(0.0, 0.0, 0.0), geo_scale[0], x_local)) + if geo_type == 0: + c = df.min( + sphere_sdf(df.float3(0.0, 0.0, 0.0), geo_scale[0], x_local) - margin, 0.0 + ) + n = df.spatial_transform_vector( + X_so, sphere_sdf_grad(df.float3(0.0, 0.0, 0.0), geo_scale[0], x_local) + ) # GEO_BOX (1) - if (geo_type == 1): - c = df.min(box_sdf(geo_scale, x_local)-margin, 0.0) + if geo_type == 1: + c = df.min(box_sdf(geo_scale, x_local) - margin, 0.0) n = df.spatial_transform_vector(X_so, box_sdf_grad(geo_scale, x_local)) - + # GEO_CAPSULE (2) - if (geo_type == 2): - c = df.min(capsule_sdf(geo_scale[0], geo_scale[1], x_local)-margin, 0.0) - n = df.spatial_transform_vector(X_so, capsule_sdf_grad(geo_scale[0], geo_scale[1], x_local)) - + if geo_type == 2: + c = df.min(capsule_sdf(geo_scale[0], geo_scale[1], x_local) - margin, 0.0) + n = df.spatial_transform_vector( + X_so, capsule_sdf_grad(geo_scale[0], geo_scale[1], x_local) + ) + # rigid velocity - rigid_v_s = df.spatial_vector() - if (rigid_index >= 0): + rigid_v_s = df.spatial_vector() + if rigid_index >= 0: rigid_v_s = df.load(body_v_sc, rigid_index) - + rigid_w = df.spatial_top(rigid_v_s) rigid_v = df.spatial_bottom(rigid_v_s) @@ -964,7 +1009,7 @@ def eval_soft_contacts( # decompose relative velocity vn = dot(n, v) vt = v - n * vn - + # contact elastic fn = n * c * ke @@ -972,7 +1017,7 @@ def eval_soft_contacts( fd = n * df.min(vn, 0.0) * kd # viscous friction - #ft = vt*kf + # ft = vt*kf # Coulomb friction (box) lower = mu * c * ke @@ -984,31 +1029,31 @@ def eval_soft_contacts( ft = df.float3(vx, 0.0, vz) # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0) - #ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) + # ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) f_total = fn + (fd + ft) * df.step(c) t_total = df.cross(px, f_total) df.atomic_sub(particle_f, particle_index, f_total) - if (rigid_index >= 0): + if rigid_index >= 0: df.atomic_sub(body_f, rigid_index, df.spatial_vector(t_total, f_total)) - @df.kernel -def eval_rigid_contacts(rigid_x: df.tensor(df.float3), - rigid_r: df.tensor(df.quat), - rigid_v: df.tensor(df.float3), - rigid_w: df.tensor(df.float3), - contact_body: df.tensor(int), - contact_point: df.tensor(df.float3), - contact_dist: df.tensor(float), - contact_mat: df.tensor(int), - materials: df.tensor(float), - rigid_f: df.tensor(df.float3), - rigid_t: df.tensor(df.float3)): - +def eval_rigid_contacts( + rigid_x: df.tensor(df.float3), + rigid_r: df.tensor(df.quat), + rigid_v: df.tensor(df.float3), + rigid_w: df.tensor(df.float3), + contact_body: df.tensor(int), + contact_point: df.tensor(df.float3), + contact_dist: df.tensor(float), + contact_mat: df.tensor(int), + materials: df.tensor(float), + rigid_f: df.tensor(df.float3), + rigid_t: df.tensor(df.float3), +): tid = df.tid() c_body = df.load(contact_body, tid) @@ -1017,13 +1062,13 @@ def eval_rigid_contacts(rigid_x: df.tensor(df.float3), c_mat = df.load(contact_mat, tid) # hard coded surface parameter tensor layout (ke, kd, kf, mu) - ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient - kd = df.load(materials, c_mat * 4 + 1) # damping coefficient - kf = df.load(materials, c_mat * 4 + 2) # friction coefficient - mu = df.load(materials, c_mat * 4 + 3) # coulomb friction + ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient + kd = df.load(materials, c_mat * 4 + 1) # damping coefficient + kf = df.load(materials, c_mat * 4 + 2) # friction coefficient + mu = df.load(materials, c_mat * 4 + 3) # coulomb friction - x0 = df.load(rigid_x, c_body) # position of colliding body - r0 = df.load(rigid_r, c_body) # orientation of colliding body + x0 = df.load(rigid_x, c_body) # position of colliding body + r0 = df.load(rigid_r, c_body) # orientation of colliding body v0 = df.load(rigid_v, c_body) w0 = df.load(rigid_w, c_body) @@ -1031,32 +1076,36 @@ def eval_rigid_contacts(rigid_x: df.tensor(df.float3), n = float3(0.0, 1.0, 0.0) # transform point to world space - p = x0 + df.rotate(r0, c_point) - n * c_dist # add on 'thickness' of shape, e.g.: radius of sphere/capsule - # use x0 as center, everything is offset from center of mass + p = ( + x0 + df.rotate(r0, c_point) - n * c_dist + ) # add on 'thickness' of shape, e.g.: radius of sphere/capsule + # use x0 as center, everything is offset from center of mass # moment arm - r = p - x0 # basically just c_point in the new coordinates + r = p - x0 # basically just c_point in the new coordinates # contact point velocity - dpdt = v0 + df.cross(w0, r) # this is rigid velocity cross offset, so it's the velocity of the contact point. + dpdt = v0 + df.cross( + w0, r + ) # this is rigid velocity cross offset, so it's the velocity of the contact point. # check ground contact - c = df.min(dot(n, p), 0.0) # check if we're inside the ground + c = df.min(dot(n, p), 0.0) # check if we're inside the ground - vn = dot(n, dpdt) # velocity component out of the ground - vt = dpdt - n * vn # velocity component not into the ground + vn = dot(n, dpdt) # velocity component out of the ground + vt = dpdt - n * vn # velocity component not into the ground - fn = c * ke # normal force (restitution coefficient * how far inside for ground) + fn = c * ke # normal force (restitution coefficient * how far inside for ground) # contact damping - fd = df.min(vn, 0.0) * kd * df.step(c) # again, velocity into the ground, negative + fd = df.min(vn, 0.0) * kd * df.step(c) # again, velocity into the ground, negative # viscous friction - #ft = vt*kf + # ft = vt*kf # Coulomb friction (box) - lower = mu * (fn + fd) # negative - upper = 0.0 - lower # positive, workaround for no unary ops + lower = mu * (fn + fd) # negative + upper = 0.0 - lower # positive, workaround for no unary ops vx = df.clamp(dot(float3(kf, 0.0, 0.0), vt), lower, upper) vz = df.clamp(dot(float3(0.0, 0.0, kf), vt), lower, upper) @@ -1064,7 +1113,7 @@ def eval_rigid_contacts(rigid_x: df.tensor(df.float3), ft = df.float3(vx, 0.0, vz) * df.step(c) # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0) - #ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) + # ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) f_total = n * (fn + fd) + ft t_total = df.cross(r, f_total) @@ -1072,10 +1121,10 @@ def eval_rigid_contacts(rigid_x: df.tensor(df.float3), df.atomic_sub(rigid_f, c_body, f_total) df.atomic_sub(rigid_t, c_body, t_total) + # # Frank & Park definition 3.20, pg 100 @df.func def spatial_transform_twist(t: df.spatial_transform, x: df.spatial_vector): - q = spatial_transform_get_rotation(t) p = spatial_transform_get_translation(t) @@ -1090,7 +1139,6 @@ def spatial_transform_twist(t: df.spatial_transform, x: df.spatial_vector): @df.func def spatial_transform_wrench(t: df.spatial_transform, x: df.spatial_vector): - q = spatial_transform_get_rotation(t) p = spatial_transform_get_translation(t) @@ -1102,21 +1150,19 @@ def spatial_transform_wrench(t: df.spatial_transform, x: df.spatial_vector): return spatial_vector(w, v) + @df.func def spatial_transform_inverse(t: df.spatial_transform): - p = spatial_transform_get_translation(t) q = spatial_transform_get_rotation(t) q_inv = inverse(q) - return spatial_transform(rotate(q_inv, p)*(0.0 - 1.0), q_inv); - + return spatial_transform(rotate(q_inv, p) * (0.0 - 1.0), q_inv) # computes adj_t^-T*I*adj_t^-1 (tensor change of coordinates), Frank & Park, section 8.2.3, pg 290 @df.func def spatial_transform_inertia(t: df.spatial_transform, I: df.spatial_matrix): - t_inv = spatial_transform_inverse(t) q = spatial_transform_get_rotation(t_inv) @@ -1130,7 +1176,7 @@ def spatial_transform_inertia(t: df.spatial_transform, I: df.spatial_matrix): S = mul(skew(p), R) T = spatial_adjoint(R, S) - + return mul(mul(transpose(T), I), T) @@ -1143,8 +1189,8 @@ def eval_rigid_contacts_art( contact_dist: df.tensor(float), contact_mat: df.tensor(int), materials: df.tensor(float), - body_f_s: df.tensor(df.spatial_vector)): - + body_f_s: df.tensor(df.spatial_vector), +): tid = df.tid() c_body = df.load(contact_body, tid) @@ -1153,18 +1199,20 @@ def eval_rigid_contacts_art( c_mat = df.load(contact_mat, tid) # hard coded surface parameter tensor layout (ke, kd, kf, mu) - ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient - kd = df.load(materials, c_mat * 4 + 1) # damping coefficient - kf = df.load(materials, c_mat * 4 + 2) # friction coefficient - mu = df.load(materials, c_mat * 4 + 3) # coulomb friction + ke = df.load(materials, c_mat * 4 + 0) # restitution coefficient + kd = df.load(materials, c_mat * 4 + 1) # damping coefficient + kf = df.load(materials, c_mat * 4 + 2) # friction coefficient + mu = df.load(materials, c_mat * 4 + 3) # coulomb friction - X_s = df.load(body_X_s, c_body) # position of colliding body - v_s = df.load(body_v_s, c_body) # orientation of colliding body + X_s = df.load(body_X_s, c_body) # position of colliding body + v_s = df.load(body_v_s, c_body) # orientation of colliding body n = float3(0.0, 1.0, 0.0) # transform point to world space - p = df.spatial_transform_point(X_s, c_point) - n * c_dist # add on 'thickness' of shape, e.g.: radius of sphere/capsule + p = ( + df.spatial_transform_point(X_s, c_point) - n * c_dist + ) # add on 'thickness' of shape, e.g.: radius of sphere/capsule w = df.spatial_top(v_s) v = df.spatial_bottom(v_s) @@ -1172,33 +1220,32 @@ def eval_rigid_contacts_art( # contact point velocity dpdt = v + df.cross(w, p) - # check ground contact - c = df.dot(n, p) # check if we're inside the ground + c = df.dot(n, p) # check if we're inside the ground - if (c >= 0.0): + if c >= 0.0: return - vn = dot(n, dpdt) # velocity component out of the ground - vt = dpdt - n * vn # velocity component not into the ground + vn = dot(n, dpdt) # velocity component out of the ground + vt = dpdt - n * vn # velocity component not into the ground - fn = c * ke # normal force (restitution coefficient * how far inside for ground) + fn = c * ke # normal force (restitution coefficient * how far inside for ground) # contact damping fd = df.min(vn, 0.0) * kd * df.step(c) * (0.0 - c) # viscous friction - #ft = vt*kf + # ft = vt*kf # Coulomb friction (box) - lower = mu * (fn + fd) # negative - upper = 0.0 - lower # positive, workaround for no unary ops + lower = mu * (fn + fd) # negative + upper = 0.0 - lower # positive, workaround for no unary ops vx = df.clamp(dot(float3(kf, 0.0, 0.0), vt), lower, upper) vz = df.clamp(dot(float3(0.0, 0.0, kf), vt), lower, upper) # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0) - ft = df.normalize(vt)*df.min(kf*df.length(vt), 0.0 - mu*c*ke) * df.step(c) + ft = df.normalize(vt) * df.min(kf * df.length(vt), 0.0 - mu * c * ke) * df.step(c) f_total = n * (fn + fd) + ft t_total = df.cross(p, f_total) @@ -1210,20 +1257,20 @@ def eval_rigid_contacts_art( def compute_muscle_force( i: int, body_X_s: df.tensor(df.spatial_transform), - body_v_s: df.tensor(df.spatial_vector), + body_v_s: df.tensor(df.spatial_vector), muscle_links: df.tensor(int), muscle_points: df.tensor(df.float3), muscle_activation: float, - body_f_s: df.tensor(df.spatial_vector)): - + body_f_s: df.tensor(df.spatial_vector), +): link_0 = df.load(muscle_links, i) - link_1 = df.load(muscle_links, i+1) + link_1 = df.load(muscle_links, i + 1) - if (link_0 == link_1): + if link_0 == link_1: return 0 r_0 = df.load(muscle_points, i) - r_1 = df.load(muscle_points, i+1) + r_1 = df.load(muscle_points, i + 1) xform_0 = df.load(body_X_s, link_0) xform_1 = df.load(body_X_s, link_1) @@ -1252,40 +1299,38 @@ def eval_muscles( muscle_points: df.tensor(df.float3), muscle_activation: df.tensor(float), # output - body_f_s: df.tensor(df.spatial_vector)): - + body_f_s: df.tensor(df.spatial_vector), +): tid = df.tid() m_start = df.load(muscle_start, tid) - m_end = df.load(muscle_start, tid+1) - 1 + m_end = df.load(muscle_start, tid + 1) - 1 activation = df.load(muscle_activation, tid) for i in range(m_start, m_end): - compute_muscle_force(i, body_X_s, body_v_s, muscle_links, muscle_points, activation, body_f_s) - + compute_muscle_force( + i, body_X_s, body_v_s, muscle_links, muscle_points, activation, body_f_s + ) + # compute transform across a joint @df.func def jcalc_transform(type: int, axis: df.float3, joint_q: df.tensor(float), start: int): - # prismatic - if (type == 0): - + if type == 0: q = df.load(joint_q, start) X_jc = spatial_transform(axis * q, quat_identity()) return X_jc # revolute - if (type == 1): - + if type == 1: q = df.load(joint_q, start) X_jc = spatial_transform(float3(0.0, 0.0, 0.0), quat_from_axis_angle(axis, q)) return X_jc # ball - if (type == 2): - + if type == 2: qx = df.load(joint_q, start + 0) qy = df.load(joint_q, start + 1) qz = df.load(joint_q, start + 2) @@ -1295,14 +1340,12 @@ def jcalc_transform(type: int, axis: df.float3, joint_q: df.tensor(float), start return X_jc # fixed - if (type == 3): - + if type == 3: X_jc = spatial_transform_identity() return X_jc # free - if (type == 4): - + if type == 4: px = df.load(joint_q, start + 0) py = df.load(joint_q, start + 1) pz = df.load(joint_q, start + 2) @@ -1321,65 +1364,93 @@ def jcalc_transform(type: int, axis: df.float3, joint_q: df.tensor(float), start # compute motion subspace and velocity for a joint @df.func -def jcalc_motion(type: int, axis: df.float3, X_sc: df.spatial_transform, joint_S_s: df.tensor(df.spatial_vector), joint_qd: df.tensor(float), joint_start: int): - +def jcalc_motion( + type: int, + axis: df.float3, + X_sc: df.spatial_transform, + joint_S_s: df.tensor(df.spatial_vector), + joint_qd: df.tensor(float), + joint_start: int, +): # prismatic - if (type == 0): - - S_s = df.spatial_transform_twist(X_sc, spatial_vector(float3(0.0, 0.0, 0.0), axis)) + if type == 0: + S_s = df.spatial_transform_twist( + X_sc, spatial_vector(float3(0.0, 0.0, 0.0), axis) + ) v_j_s = S_s * df.load(joint_qd, joint_start) df.store(joint_S_s, joint_start, S_s) return v_j_s # revolute - if (type == 1): - - S_s = df.spatial_transform_twist(X_sc, spatial_vector(axis, float3(0.0, 0.0, 0.0))) + if type == 1: + S_s = df.spatial_transform_twist( + X_sc, spatial_vector(axis, float3(0.0, 0.0, 0.0)) + ) v_j_s = S_s * df.load(joint_qd, joint_start) df.store(joint_S_s, joint_start, S_s) return v_j_s # ball - if (type == 2): - - w = float3(df.load(joint_qd, joint_start + 0), - df.load(joint_qd, joint_start + 1), - df.load(joint_qd, joint_start + 2)) - - S_0 = df.spatial_transform_twist(X_sc, spatial_vector(1.0, 0.0, 0.0, 0.0, 0.0, 0.0)) - S_1 = df.spatial_transform_twist(X_sc, spatial_vector(0.0, 1.0, 0.0, 0.0, 0.0, 0.0)) - S_2 = df.spatial_transform_twist(X_sc, spatial_vector(0.0, 0.0, 1.0, 0.0, 0.0, 0.0)) + if type == 2: + w = float3( + df.load(joint_qd, joint_start + 0), + df.load(joint_qd, joint_start + 1), + df.load(joint_qd, joint_start + 2), + ) + + S_0 = df.spatial_transform_twist( + X_sc, spatial_vector(1.0, 0.0, 0.0, 0.0, 0.0, 0.0) + ) + S_1 = df.spatial_transform_twist( + X_sc, spatial_vector(0.0, 1.0, 0.0, 0.0, 0.0, 0.0) + ) + S_2 = df.spatial_transform_twist( + X_sc, spatial_vector(0.0, 0.0, 1.0, 0.0, 0.0, 0.0) + ) # write motion subspace df.store(joint_S_s, joint_start + 0, S_0) df.store(joint_S_s, joint_start + 1, S_1) df.store(joint_S_s, joint_start + 2, S_2) - return S_0*w[0] + S_1*w[1] + S_2*w[2] + return S_0 * w[0] + S_1 * w[1] + S_2 * w[2] # fixed - if (type == 3): + if type == 3: return spatial_vector() # free - if (type == 4): - - v_j_s = spatial_vector(df.load(joint_qd, joint_start + 0), - df.load(joint_qd, joint_start + 1), - df.load(joint_qd, joint_start + 2), - df.load(joint_qd, joint_start + 3), - df.load(joint_qd, joint_start + 4), - df.load(joint_qd, joint_start + 5)) + if type == 4: + v_j_s = spatial_vector( + df.load(joint_qd, joint_start + 0), + df.load(joint_qd, joint_start + 1), + df.load(joint_qd, joint_start + 2), + df.load(joint_qd, joint_start + 3), + df.load(joint_qd, joint_start + 4), + df.load(joint_qd, joint_start + 5), + ) # write motion subspace - df.store(joint_S_s, joint_start + 0, spatial_vector(1.0, 0.0, 0.0, 0.0, 0.0, 0.0)) - df.store(joint_S_s, joint_start + 1, spatial_vector(0.0, 1.0, 0.0, 0.0, 0.0, 0.0)) - df.store(joint_S_s, joint_start + 2, spatial_vector(0.0, 0.0, 1.0, 0.0, 0.0, 0.0)) - df.store(joint_S_s, joint_start + 3, spatial_vector(0.0, 0.0, 0.0, 1.0, 0.0, 0.0)) - df.store(joint_S_s, joint_start + 4, spatial_vector(0.0, 0.0, 0.0, 0.0, 1.0, 0.0)) - df.store(joint_S_s, joint_start + 5, spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 1.0)) + df.store( + joint_S_s, joint_start + 0, spatial_vector(1.0, 0.0, 0.0, 0.0, 0.0, 0.0) + ) + df.store( + joint_S_s, joint_start + 1, spatial_vector(0.0, 1.0, 0.0, 0.0, 0.0, 0.0) + ) + df.store( + joint_S_s, joint_start + 2, spatial_vector(0.0, 0.0, 1.0, 0.0, 0.0, 0.0) + ) + df.store( + joint_S_s, joint_start + 3, spatial_vector(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + ) + df.store( + joint_S_s, joint_start + 4, spatial_vector(0.0, 0.0, 0.0, 0.0, 1.0, 0.0) + ) + df.store( + joint_S_s, joint_start + 5, spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 1.0) + ) return v_j_s @@ -1420,12 +1491,12 @@ def jcalc_motion(type: int, axis: df.float3, X_sc: df.spatial_transform, joint_S # computes joint space forces/torques in tau @df.func def jcalc_tau( - type: int, + type: int, target_k_e: float, target_k_d: float, limit_k_e: float, limit_k_d: float, - joint_S_s: df.tensor(spatial_vector), + joint_S_s: df.tensor(spatial_vector), joint_q: df.tensor(float), joint_qd: df.tensor(float), joint_act: df.tensor(float), @@ -1433,12 +1504,12 @@ def jcalc_tau( joint_limit_lower: df.tensor(float), joint_limit_upper: df.tensor(float), coord_start: int, - dof_start: int, - body_f_s: spatial_vector, - tau: df.tensor(float)): - + dof_start: int, + body_f_s: spatial_vector, + tau: df.tensor(float), +): # prismatic / revolute - if (type == 0 or type == 1): + if type == 0 or type == 1: S_s = df.load(joint_S_s, dof_start) q = df.load(joint_q, coord_start) @@ -1452,52 +1523,65 @@ def jcalc_tau( limit_f = 0.0 # compute limit forces, damping only active when limit is violated - if (q < lower): - limit_f = limit_k_e*(lower-q) + if q < lower: + limit_f = limit_k_e * (lower - q) - if (q > upper): - limit_f = limit_k_e*(upper-q) + if q > upper: + limit_f = limit_k_e * (upper - q) damping_f = (0.0 - limit_k_d) * qd # total torque / force on the joint - t = 0.0 - spatial_dot(S_s, body_f_s) - target_k_e*(q - target) - target_k_d*qd + act + limit_f + damping_f - + t = ( + 0.0 + - spatial_dot(S_s, body_f_s) + - target_k_e * (q - target) + - target_k_d * qd + + act + + limit_f + + damping_f + ) df.store(tau, dof_start, t) # ball - if (type == 2): - - # elastic term.. this is proportional to the + if type == 2: + # elastic term.. this is proportional to the # imaginary part of the relative quaternion - r_j = float3(df.load(joint_q, coord_start + 0), - df.load(joint_q, coord_start + 1), - df.load(joint_q, coord_start + 2)) + r_j = float3( + df.load(joint_q, coord_start + 0), + df.load(joint_q, coord_start + 1), + df.load(joint_q, coord_start + 2), + ) # angular velocity for damping - w_j = float3(df.load(joint_qd, dof_start + 0), - df.load(joint_qd, dof_start + 1), - df.load(joint_qd, dof_start + 2)) + w_j = float3( + df.load(joint_qd, dof_start + 0), + df.load(joint_qd, dof_start + 1), + df.load(joint_qd, dof_start + 2), + ) for i in range(0, 3): - S_s = df.load(joint_S_s, dof_start+i) + S_s = df.load(joint_S_s, dof_start + i) w = w_j[i] r = r_j[i] - df.store(tau, dof_start+i, 0.0 - spatial_dot(S_s, body_f_s) - w*target_k_d - r*target_k_e) + df.store( + tau, + dof_start + i, + 0.0 - spatial_dot(S_s, body_f_s) - w * target_k_d - r * target_k_e, + ) # fixed # if (type == 3) # pass # free - if (type == 4): - + if type == 4: for i in range(0, 6): - S_s = df.load(joint_S_s, dof_start+i) - df.store(tau, dof_start+i, 0.0 - spatial_dot(S_s, body_f_s)) + S_s = df.load(joint_S_s, dof_start + i) + df.store(tau, dof_start + i, 0.0 - spatial_dot(S_s, body_f_s)) return 0 @@ -1512,39 +1596,43 @@ def jcalc_integrate( dof_start: int, dt: float, joint_q_new: df.tensor(float), - joint_qd_new: df.tensor(float)): - + joint_qd_new: df.tensor(float), +): # prismatic / revolute - if (type == 0 or type == 1): - + if type == 0 or type == 1: qdd = df.load(joint_qdd, dof_start) qd = df.load(joint_qd, dof_start) q = df.load(joint_q, coord_start) - qd_new = qd + qdd*dt - q_new = q + qd_new*dt + qd_new = qd + qdd * dt + q_new = q + qd_new * dt df.store(joint_qd_new, dof_start, qd_new) df.store(joint_q_new, coord_start, q_new) # ball - if (type == 2): - - m_j = float3(df.load(joint_qdd, dof_start + 0), - df.load(joint_qdd, dof_start + 1), - df.load(joint_qdd, dof_start + 2)) - - w_j = float3(df.load(joint_qd, dof_start + 0), - df.load(joint_qd, dof_start + 1), - df.load(joint_qd, dof_start + 2)) - - r_j = quat(df.load(joint_q, coord_start + 0), - df.load(joint_q, coord_start + 1), - df.load(joint_q, coord_start + 2), - df.load(joint_q, coord_start + 3)) + if type == 2: + m_j = float3( + df.load(joint_qdd, dof_start + 0), + df.load(joint_qdd, dof_start + 1), + df.load(joint_qdd, dof_start + 2), + ) + + w_j = float3( + df.load(joint_qd, dof_start + 0), + df.load(joint_qd, dof_start + 1), + df.load(joint_qd, dof_start + 2), + ) + + r_j = quat( + df.load(joint_q, coord_start + 0), + df.load(joint_q, coord_start + 1), + df.load(joint_q, coord_start + 2), + df.load(joint_q, coord_start + 3), + ) # symplectic Euler - w_j_new = w_j + m_j*dt + w_j_new = w_j + m_j * dt drdt_j = mul(quat(w_j_new, 0.0), r_j) * 0.5 @@ -1563,51 +1651,62 @@ def jcalc_integrate( df.store(joint_qd_new, dof_start + 2, w_j_new[2]) # fixed joint - #if (type == 3) + # if (type == 3) # pass # free joint - if (type == 4): - + if type == 4: # dofs: qd = (omega_x, omega_y, omega_z, vel_x, vel_y, vel_z) # coords: q = (trans_x, trans_y, trans_z, quat_x, quat_y, quat_z, quat_w) # angular and linear acceleration - m_s = float3(df.load(joint_qdd, dof_start + 0), - df.load(joint_qdd, dof_start + 1), - df.load(joint_qdd, dof_start + 2)) - - a_s = float3(df.load(joint_qdd, dof_start + 3), - df.load(joint_qdd, dof_start + 4), - df.load(joint_qdd, dof_start + 5)) + m_s = float3( + df.load(joint_qdd, dof_start + 0), + df.load(joint_qdd, dof_start + 1), + df.load(joint_qdd, dof_start + 2), + ) + + a_s = float3( + df.load(joint_qdd, dof_start + 3), + df.load(joint_qdd, dof_start + 4), + df.load(joint_qdd, dof_start + 5), + ) # angular and linear velocity - w_s = float3(df.load(joint_qd, dof_start + 0), - df.load(joint_qd, dof_start + 1), - df.load(joint_qd, dof_start + 2)) - - v_s = float3(df.load(joint_qd, dof_start + 3), - df.load(joint_qd, dof_start + 4), - df.load(joint_qd, dof_start + 5)) + w_s = float3( + df.load(joint_qd, dof_start + 0), + df.load(joint_qd, dof_start + 1), + df.load(joint_qd, dof_start + 2), + ) + + v_s = float3( + df.load(joint_qd, dof_start + 3), + df.load(joint_qd, dof_start + 4), + df.load(joint_qd, dof_start + 5), + ) # symplectic Euler - w_s = w_s + m_s*dt - v_s = v_s + a_s*dt - + w_s = w_s + m_s * dt + v_s = v_s + a_s * dt + # translation of origin - p_s = float3(df.load(joint_q, coord_start + 0), - df.load(joint_q, coord_start + 1), - df.load(joint_q, coord_start + 2)) + p_s = float3( + df.load(joint_q, coord_start + 0), + df.load(joint_q, coord_start + 1), + df.load(joint_q, coord_start + 2), + ) - # linear vel of origin (note q/qd switch order of linear angular elements) + # linear vel of origin (note q/qd switch order of linear angular elements) # note we are converting the body twist in the space frame (w_s, v_s) to compute center of mass velcity dpdt_s = v_s + cross(w_s, p_s) - + # quat and quat derivative - r_s = quat(df.load(joint_q, coord_start + 3), - df.load(joint_q, coord_start + 4), - df.load(joint_q, coord_start + 5), - df.load(joint_q, coord_start + 6)) + r_s = quat( + df.load(joint_q, coord_start + 3), + df.load(joint_q, coord_start + 4), + df.load(joint_q, coord_start + 5), + df.load(joint_q, coord_start + 6), + ) drdt_s = mul(quat(w_s, 0.0), r_s) * 0.5 @@ -1635,25 +1734,27 @@ def jcalc_integrate( return 0 -@df.func -def compute_link_transform(i: int, - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_q_start: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_q: df.tensor(float), - joint_X_pj: df.tensor(df.spatial_transform), - joint_X_cm: df.tensor(df.spatial_transform), - joint_axis: df.tensor(df.float3), - body_X_sc: df.tensor(df.spatial_transform), - body_X_sm: df.tensor(df.spatial_transform)): +@df.func +def compute_link_transform( + i: int, + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_q_start: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_q: df.tensor(float), + joint_X_pj: df.tensor(df.spatial_transform), + joint_X_cm: df.tensor(df.spatial_transform), + joint_axis: df.tensor(df.float3), + body_X_sc: df.tensor(df.spatial_transform), + body_X_sm: df.tensor(df.spatial_transform), +): # parent transform parent = load(joint_parent, i) # parent transform in spatial coordinates X_sp = spatial_transform_identity() - if (parent >= 0): + if parent >= 0: X_sp = load(body_X_sc, parent) type = load(joint_type, i) @@ -1679,69 +1780,71 @@ def compute_link_transform(i: int, @df.kernel -def eval_rigid_fk(articulation_start: df.tensor(int), - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_q_start: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_q: df.tensor(float), - joint_X_pj: df.tensor(df.spatial_transform), - joint_X_cm: df.tensor(df.spatial_transform), - joint_axis: df.tensor(df.float3), - body_X_sc: df.tensor(df.spatial_transform), - body_X_sm: df.tensor(df.spatial_transform)): - +def eval_rigid_fk( + articulation_start: df.tensor(int), + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_q_start: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_q: df.tensor(float), + joint_X_pj: df.tensor(df.spatial_transform), + joint_X_cm: df.tensor(df.spatial_transform), + joint_axis: df.tensor(df.float3), + body_X_sc: df.tensor(df.spatial_transform), + body_X_sm: df.tensor(df.spatial_transform), +): # one thread per-articulation index = tid() start = df.load(articulation_start, index) - end = df.load(articulation_start, index+1) + end = df.load(articulation_start, index + 1) for i in range(start, end): - compute_link_transform(i, - joint_type, - joint_parent, - joint_q_start, - joint_qd_start, - joint_q, - joint_X_pj, - joint_X_cm, - joint_axis, - body_X_sc, - body_X_sm) - - + compute_link_transform( + i, + joint_type, + joint_parent, + joint_q_start, + joint_qd_start, + joint_q, + joint_X_pj, + joint_X_cm, + joint_axis, + body_X_sc, + body_X_sm, + ) @df.func -def compute_link_velocity(i: int, - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_qd: df.tensor(float), - joint_axis: df.tensor(df.float3), - body_I_m: df.tensor(df.spatial_matrix), - body_X_sc: df.tensor(df.spatial_transform), - body_X_sm: df.tensor(df.spatial_transform), - joint_X_pj: df.tensor(df.spatial_transform), - gravity: df.tensor(df.float3), - # outputs - joint_S_s: df.tensor(df.spatial_vector), - body_I_s: df.tensor(df.spatial_matrix), - body_v_s: df.tensor(df.spatial_vector), - body_f_s: df.tensor(df.spatial_vector), - body_a_s: df.tensor(df.spatial_vector)): - +def compute_link_velocity( + i: int, + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_qd: df.tensor(float), + joint_axis: df.tensor(df.float3), + body_I_m: df.tensor(df.spatial_matrix), + body_X_sc: df.tensor(df.spatial_transform), + body_X_sm: df.tensor(df.spatial_transform), + joint_X_pj: df.tensor(df.spatial_transform), + gravity: df.tensor(df.float3), + # outputs + joint_S_s: df.tensor(df.spatial_vector), + body_I_s: df.tensor(df.spatial_matrix), + body_v_s: df.tensor(df.spatial_vector), + body_f_s: df.tensor(df.spatial_vector), + body_a_s: df.tensor(df.spatial_vector), +): type = df.load(joint_type, i) axis = df.load(joint_axis, i) parent = df.load(joint_parent, i) dof_start = df.load(joint_qd_start, i) - + X_sc = df.load(body_X_sc, i) # parent transform in spatial coordinates X_sp = spatial_transform_identity() - if (parent >= 0): + if parent >= 0: X_sp = load(body_X_sc, parent) X_pj = load(joint_X_pj, i) @@ -1754,13 +1857,15 @@ def compute_link_velocity(i: int, v_parent_s = spatial_vector() a_parent_s = spatial_vector() - if (parent >= 0): + if parent >= 0: v_parent_s = df.load(body_v_s, parent) a_parent_s = df.load(body_a_s, parent) # body velocity, acceleration v_s = v_parent_s + v_j_s - a_s = a_parent_s + spatial_cross(v_s, v_j_s) # + self.joint_S_s[i]*self.joint_qdd[i] + a_s = a_parent_s + spatial_cross( + v_s, v_j_s + ) # + self.joint_S_s[i]*self.joint_qdd[i] # compute body forces X_sm = df.load(body_X_sm, i) @@ -1772,9 +1877,12 @@ def compute_link_velocity(i: int, m = I_m[3, 3] f_g_m = spatial_vector(float3(), g) * m - f_g_s = spatial_transform_wrench(spatial_transform(spatial_transform_get_translation(X_sm), quat_identity()), f_g_m) + f_g_s = spatial_transform_wrench( + spatial_transform(spatial_transform_get_translation(X_sm), quat_identity()), + f_g_m, + ) - #f_ext_s = df.load(body_f_s, i) + f_g_s + # f_ext_s = df.load(body_f_s, i) + f_g_s # body forces I_s = spatial_transform_inertia(X_sm, I_m) @@ -1790,30 +1898,31 @@ def compute_link_velocity(i: int, @df.func -def compute_link_tau(offset: int, - joint_end: int, - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_q_start: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_q: df.tensor(float), - joint_qd: df.tensor(float), - joint_act: df.tensor(float), - joint_target: df.tensor(float), - joint_target_ke: df.tensor(float), - joint_target_kd: df.tensor(float), - joint_limit_lower: df.tensor(float), - joint_limit_upper: df.tensor(float), - joint_limit_ke: df.tensor(float), - joint_limit_kd: df.tensor(float), - joint_S_s: df.tensor(df.spatial_vector), - body_fb_s: df.tensor(df.spatial_vector), - # outputs - body_ft_s: df.tensor(df.spatial_vector), - tau: df.tensor(float)): - +def compute_link_tau( + offset: int, + joint_end: int, + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_q_start: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_q: df.tensor(float), + joint_qd: df.tensor(float), + joint_act: df.tensor(float), + joint_target: df.tensor(float), + joint_target_ke: df.tensor(float), + joint_target_kd: df.tensor(float), + joint_limit_lower: df.tensor(float), + joint_limit_upper: df.tensor(float), + joint_limit_ke: df.tensor(float), + joint_limit_kd: df.tensor(float), + joint_S_s: df.tensor(df.spatial_vector), + body_fb_s: df.tensor(df.spatial_vector), + # outputs + body_ft_s: df.tensor(df.spatial_vector), + tau: df.tensor(float), +): # for backwards traversal - i = joint_end-offset-1 + i = joint_end - offset - 1 type = df.load(joint_type, i) parent = df.load(joint_parent, i) @@ -1833,44 +1942,62 @@ def compute_link_tau(offset: int, f_s = f_b_s + f_t_s # compute joint-space forces, writes out tau - jcalc_tau(type, target_k_e, target_k_d, limit_k_e, limit_k_d, joint_S_s, joint_q, joint_qd, joint_act, joint_target, joint_limit_lower, joint_limit_upper, coord_start, dof_start, f_s, tau) + jcalc_tau( + type, + target_k_e, + target_k_d, + limit_k_e, + limit_k_d, + joint_S_s, + joint_q, + joint_qd, + joint_act, + joint_target, + joint_limit_lower, + joint_limit_upper, + coord_start, + dof_start, + f_s, + tau, + ) # update parent forces, todo: check that this is valid for the backwards pass - if (parent >= 0): + if parent >= 0: df.atomic_add(body_ft_s, parent, f_s) return 0 @df.kernel -def eval_rigid_id(articulation_start: df.tensor(int), - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_q_start: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_q: df.tensor(float), - joint_qd: df.tensor(float), - joint_axis: df.tensor(df.float3), - joint_target_ke: df.tensor(float), - joint_target_kd: df.tensor(float), - body_I_m: df.tensor(df.spatial_matrix), - body_X_sc: df.tensor(df.spatial_transform), - body_X_sm: df.tensor(df.spatial_transform), - joint_X_pj: df.tensor(df.spatial_transform), - gravity: df.tensor(df.float3), - # outputs - joint_S_s: df.tensor(df.spatial_vector), - body_I_s: df.tensor(df.spatial_matrix), - body_v_s: df.tensor(df.spatial_vector), - body_f_s: df.tensor(df.spatial_vector), - body_a_s: df.tensor(df.spatial_vector)): - +def eval_rigid_id( + articulation_start: df.tensor(int), + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_q_start: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_q: df.tensor(float), + joint_qd: df.tensor(float), + joint_axis: df.tensor(df.float3), + joint_target_ke: df.tensor(float), + joint_target_kd: df.tensor(float), + body_I_m: df.tensor(df.spatial_matrix), + body_X_sc: df.tensor(df.spatial_transform), + body_X_sm: df.tensor(df.spatial_transform), + joint_X_pj: df.tensor(df.spatial_transform), + gravity: df.tensor(df.float3), + # outputs + joint_S_s: df.tensor(df.spatial_vector), + body_I_s: df.tensor(df.spatial_matrix), + body_v_s: df.tensor(df.spatial_vector), + body_f_s: df.tensor(df.spatial_vector), + body_a_s: df.tensor(df.spatial_vector), +): # one thread per-articulation index = tid() start = df.load(articulation_start, index) - end = df.load(articulation_start, index+1) - count = end-start + end = df.load(articulation_start, index + 1) + count = end - start # compute link velocities and coriolis forces for i in range(start, end): @@ -1890,38 +2017,40 @@ def eval_rigid_id(articulation_start: df.tensor(int), body_I_s, body_v_s, body_f_s, - body_a_s) + body_a_s, + ) @df.kernel -def eval_rigid_tau(articulation_start: df.tensor(int), - joint_type: df.tensor(int), - joint_parent: df.tensor(int), - joint_q_start: df.tensor(int), - joint_qd_start: df.tensor(int), - joint_q: df.tensor(float), - joint_qd: df.tensor(float), - joint_act: df.tensor(float), - joint_target: df.tensor(float), - joint_target_ke: df.tensor(float), - joint_target_kd: df.tensor(float), - joint_limit_lower: df.tensor(float), - joint_limit_upper: df.tensor(float), - joint_limit_ke: df.tensor(float), - joint_limit_kd: df.tensor(float), - joint_axis: df.tensor(df.float3), - joint_S_s: df.tensor(df.spatial_vector), - body_fb_s: df.tensor(df.spatial_vector), - # outputs - body_ft_s: df.tensor(df.spatial_vector), - tau: df.tensor(float)): - +def eval_rigid_tau( + articulation_start: df.tensor(int), + joint_type: df.tensor(int), + joint_parent: df.tensor(int), + joint_q_start: df.tensor(int), + joint_qd_start: df.tensor(int), + joint_q: df.tensor(float), + joint_qd: df.tensor(float), + joint_act: df.tensor(float), + joint_target: df.tensor(float), + joint_target_ke: df.tensor(float), + joint_target_kd: df.tensor(float), + joint_limit_lower: df.tensor(float), + joint_limit_upper: df.tensor(float), + joint_limit_ke: df.tensor(float), + joint_limit_kd: df.tensor(float), + joint_axis: df.tensor(df.float3), + joint_S_s: df.tensor(df.spatial_vector), + body_fb_s: df.tensor(df.spatial_vector), + # outputs + body_ft_s: df.tensor(df.spatial_vector), + tau: df.tensor(float), +): # one thread per-articulation index = tid() start = df.load(articulation_start, index) - end = df.load(articulation_start, index+1) - count = end-start + end = df.load(articulation_start, index + 1) + count = end - start # compute joint forces for i in range(0, count): @@ -1945,7 +2074,9 @@ def eval_rigid_tau(articulation_start: df.tensor(int), joint_S_s, body_fb_s, body_ft_s, - tau) + tau, + ) + @df.kernel def eval_rigid_jacobian( @@ -1955,25 +2086,27 @@ def eval_rigid_jacobian( joint_qd_start: df.tensor(int), joint_S_s: df.tensor(spatial_vector), # outputs - J: df.tensor(float)): - + J: df.tensor(float), +): # one thread per-articulation index = tid() joint_start = df.load(articulation_start, index) - joint_end = df.load(articulation_start, index+1) - joint_count = joint_end-joint_start + joint_end = df.load(articulation_start, index + 1) + joint_count = joint_end - joint_start J_offset = df.load(articulation_J_start, index) # in spatial.h - spatial_jacobian(joint_S_s, joint_parent, joint_qd_start, joint_start, joint_count, J_offset, J) + spatial_jacobian( + joint_S_s, joint_parent, joint_qd_start, joint_start, joint_count, J_offset, J + ) # @df.kernel # def eval_rigid_jacobian( # articulation_start: df.tensor(int), -# articulation_J_start: df.tensor(int), +# articulation_J_start: df.tensor(int), # joint_parent: df.tensor(int), # joint_qd_start: df.tensor(int), # joint_S_s: df.tensor(spatial_vector), @@ -1995,57 +2128,110 @@ def eval_rigid_jacobian( # spatial_jacobian(joint_S_s, joint_parent, joint_qd_start, joint_count, dof_count, J) - @df.kernel def eval_rigid_mass( articulation_start: df.tensor(int), - articulation_M_start: df.tensor(int), + articulation_M_start: df.tensor(int), body_I_s: df.tensor(spatial_matrix), # outputs - M: df.tensor(float)): - + M: df.tensor(float), +): # one thread per-articulation index = tid() joint_start = df.load(articulation_start, index) - joint_end = df.load(articulation_start, index+1) - joint_count = joint_end-joint_start + joint_end = df.load(articulation_start, index + 1) + joint_count = joint_end - joint_start M_offset = df.load(articulation_M_start, index) # in spatial.h spatial_mass(body_I_s, joint_start, joint_count, M_offset, M) + @df.kernel -def eval_dense_gemm(m: int, n: int, p: int, t1: int, t2: int, A: df.tensor(float), B: df.tensor(float), C: df.tensor(float)): +def eval_dense_gemm( + m: int, + n: int, + p: int, + t1: int, + t2: int, + A: df.tensor(float), + B: df.tensor(float), + C: df.tensor(float), +): dense_gemm(m, n, p, t1, t2, A, B, C) + @df.kernel -def eval_dense_gemm_batched(m: df.tensor(int), n: df.tensor(int), p: df.tensor(int), t1: int, t2: int, A_start: df.tensor(int), B_start: df.tensor(int), C_start: df.tensor(int), A: df.tensor(float), B: df.tensor(float), C: df.tensor(float)): +def eval_dense_gemm_batched( + m: df.tensor(int), + n: df.tensor(int), + p: df.tensor(int), + t1: int, + t2: int, + A_start: df.tensor(int), + B_start: df.tensor(int), + C_start: df.tensor(int), + A: df.tensor(float), + B: df.tensor(float), + C: df.tensor(float), +): dense_gemm_batched(m, n, p, t1, t2, A_start, B_start, C_start, A, B, C) + @df.kernel -def eval_dense_cholesky(n: int, A: df.tensor(float), regularization: df.tensor(float), L: df.tensor(float)): +def eval_dense_cholesky( + n: int, A: df.tensor(float), regularization: df.tensor(float), L: df.tensor(float) +): dense_chol(n, A, regularization, L) + @df.kernel -def eval_dense_cholesky_batched(A_start: df.tensor(int), A_dim: df.tensor(int), A: df.tensor(float), regularization: df.tensor(float), L: df.tensor(float)): +def eval_dense_cholesky_batched( + A_start: df.tensor(int), + A_dim: df.tensor(int), + A: df.tensor(float), + regularization: df.tensor(float), + L: df.tensor(float), +): dense_chol_batched(A_start, A_dim, A, regularization, L) + @df.kernel -def eval_dense_subs(n: int, L: df.tensor(float), b: df.tensor(float), x: df.tensor(float)): +def eval_dense_subs( + n: int, L: df.tensor(float), b: df.tensor(float), x: df.tensor(float) +): dense_subs(n, L, b, x) + # helper that propagates gradients back to A, treating L as a constant / temporary variable # allows us to reuse the Cholesky decomposition from the forward pass @df.kernel -def eval_dense_solve(n: int, A: df.tensor(float), L: df.tensor(float), b: df.tensor(float), tmp: df.tensor(float), x: df.tensor(float)): +def eval_dense_solve( + n: int, + A: df.tensor(float), + L: df.tensor(float), + b: df.tensor(float), + tmp: df.tensor(float), + x: df.tensor(float), +): dense_solve(n, A, L, b, tmp, x) + # helper that propagates gradients back to A, treating L as a constant / temporary variable # allows us to reuse the Cholesky decomposition from the forward pass @df.kernel -def eval_dense_solve_batched(b_start: df.tensor(int), A_start: df.tensor(int), A_dim: df.tensor(int), A: df.tensor(float), L: df.tensor(float), b: df.tensor(float), tmp: df.tensor(float), x: df.tensor(float)): +def eval_dense_solve_batched( + b_start: df.tensor(int), + A_start: df.tensor(int), + A_dim: df.tensor(int), + A: df.tensor(float), + L: df.tensor(float), + b: df.tensor(float), + tmp: df.tensor(float), + x: df.tensor(float), +): dense_solve_batched(b_start, A_start, A_dim, A, L, b, tmp, x) @@ -2060,8 +2246,8 @@ def eval_rigid_integrate( dt: float, # outputs joint_q_new: df.tensor(float), - joint_qd_new: df.tensor(float)): - + joint_qd_new: df.tensor(float), +): # one thread per-articulation index = tid() @@ -2078,39 +2264,65 @@ def eval_rigid_integrate( dof_start, dt, joint_q_new, - joint_qd_new) + joint_qd_new, + ) + g_state_out = None + # define PyTorch autograd op to wrap simulate func class SimulateFunc(torch.autograd.Function): """PyTorch autograd function representing a simulation stpe - + Note: - + This node will be inserted into the computation graph whenever `forward()` is called on an integrator object. It should not be called - directly by the user. + directly by the user. """ @staticmethod - def forward(ctx, integrator, model, state_in, dt, substeps, mass_matrix_freq, *tensors): + def forward( + ctx, + integrator, + model, + state_in, + dt, + substeps, + mass_matrix_freq, + reset_tape, + *tensors + ): + """ + ctx: context object that can be used to stash information for backward computation + tensors: TODO? + """ # record launches ctx.tape = df.Tape() + + # ctx.inputs is the input to the model but what are they? ctx.inputs = tensors - #ctx.outputs = df.to_weak_list(state_out.flatten()) + ctx.reset_tape = reset_tape + ctx.num_backward = 0 # TODO very much a hack actuation = state_in.joint_act # simulate for i in range(substeps): - # ensure actuation is set on all substeps state_in.joint_act = actuation state_out = model.state() - integrator._simulate(ctx.tape, model, state_in, state_out, dt/float(substeps), update_mass_matrix=((i%mass_matrix_freq)==0)) + integrator._simulate( + ctx.tape, + model, + state_in, + state_out, + dt / float(substeps), + update_mass_matrix=((i % mass_matrix_freq) == 0), + ) # swap states state_in = state_out @@ -2119,39 +2331,67 @@ def forward(ctx, integrator, model, state_in, dt, substeps, mass_matrix_freq, *t global g_state_out g_state_out = state_out + # ctx.outputs is simple the output of a single step simulation ctx.outputs = df.to_weak_list(state_out.flatten()) return tuple(state_out.flatten()) - @staticmethod - def backward(ctx, *grads): + def backward(ctx, *grad_output): + ctx.num_backward += 1 + # NOTE: debugging code below + # print("calling backwards!") + # tot_norm = 0 + # for i in ctx.inputs: + # tot_norm += torch.norm(i.float()) + # print("Input norm", tot_norm) # ensure grads are contiguous in memory - adj_outputs = df.make_contiguous(grads) + # TODO why call gradients adjoints? + adj_outputs = df.make_contiguous(grad_output) # register outputs with tape - outputs = df.to_strong_list(ctx.outputs) + outputs = df.to_strong_list(ctx.outputs) for o in range(len(outputs)): - ctx.tape.adjoints[outputs[o]] = adj_outputs[o] + # # NOTE: debugging ############## + # tot_norm = 0 + # for i in outputs: + # if i is not None: + # tot_norm += torch.norm(i.float()) + # print("Output norm", tot_norm) + # ##################################### + # replay launches backwards ctx.tape.replay() + # TODO: replay somehow changes the outputs of the function! + + # NOTE: debugging ############## + # tot_norm = 0 + # for i in outputs: + # if i is not None: + # tot_norm += torch.norm(i.float()) + # print("Output norm", tot_norm) + ##################################### # find adjoint of inputs adj_inputs = [] for i in ctx.inputs: - if i in ctx.tape.adjoints: adj_inputs.append(ctx.tape.adjoints[i]) else: adj_inputs.append(None) - # free the tape - ctx.tape.reset() + # Free the tape if we don't think it would be useful again; + # otherwise just zero it so that we don't accumulate gradients + if ctx.reset_tape or ctx.num_backward == 11: # this should be output dim! + ctx.tape.reset() + else: + ctx.tape.zero() # filter grads to replace empty tensors / no grad / constant params with None - return (None, None, None, None, None, None, *df.filter_grads(adj_inputs)) + # NOTE: Each none below is for each input parameter of forward! + return (None, None, None, None, None, None, None, *df.filter_grads(adj_inputs)) class SemiImplicitIntegrator: @@ -2160,7 +2400,7 @@ class SemiImplicitIntegrator: After constructing `Model` and `State` objects this time-integrator may be used to advance the simulation state forward in time. - Semi-implicit time integration is a variational integrator that + Semi-implicit time integration is a variational integrator that preserves energy, however it not unconditionally stable, and requires a time-step small enough to support the required stiffness and damping forces. @@ -2179,9 +2419,17 @@ class SemiImplicitIntegrator: def __init__(self): pass - def forward(self, model: Model, state_in: State, dt: float, substeps: int, mass_matrix_freq: int) -> State: + def forward( + self, + model: Model, + state_in: State, + dt: float, + substeps: int, + mass_matrix_freq: int, + reset_tape: bool = True, + ) -> State: """Performs a single integration step forward in time - + This method inserts a node into the PyTorch computational graph with references to all model and state tensors such that gradients can be propagrated back through the simulation step. @@ -2199,122 +2447,176 @@ def forward(self, model: Model, state_in: State, dt: float, substeps: int, mass_ """ if dflex.config.no_grad: - # if no gradient required then do inplace update for i in range(substeps): - self._simulate(df.Tape(), model, state_in, state_in, dt/float(substeps), update_mass_matrix=(i%mass_matrix_freq)==0) - + self._simulate( + df.Tape(), + model, + state_in, + state_in, + dt / float(substeps), + update_mass_matrix=(i % mass_matrix_freq) == 0, + ) + return state_in else: - - # get list of inputs and outputs for PyTorch tensor tracking + # get list of inputs and outputs for PyTorch tensor tracking inputs = [*state_in.flatten(), *model.flatten()] # run sim as a PyTorch op - tensors = SimulateFunc.apply(self, model, state_in, dt, substeps, mass_matrix_freq, *inputs) + tensors = SimulateFunc.apply( + self, + model, + state_in, + dt, + substeps, + mass_matrix_freq, + reset_tape, + *inputs + ) global g_state_out state_out = g_state_out - g_state_out = None # null reference + g_state_out = None # null reference return state_out - - def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=True): - - with dflex.util.ScopedTimer("simulate", False): + with dflex.util.ScopedTimer("simulate", False): # alloc particle force buffer - if (model.particle_count): + if model.particle_count: state_out.particle_f.zero_() - if (model.link_count): - state_out.body_ft_s = torch.zeros((model.link_count, 6), dtype=torch.float32, device=model.adapter, requires_grad=True) - state_out.body_f_ext_s = torch.zeros((model.link_count, 6), dtype=torch.float32, device=model.adapter, requires_grad=True) + if model.link_count: + state_out.body_ft_s = torch.zeros( + (model.link_count, 6), + dtype=torch.float32, + device=model.adapter, + requires_grad=True, + ) + state_out.body_f_ext_s = torch.zeros( + (model.link_count, 6), + dtype=torch.float32, + device=model.adapter, + requires_grad=True, + ) # damped springs - if (model.spring_count): - - tape.launch(func=eval_springs, - dim=model.spring_count, - inputs=[state_in.particle_q, state_in.particle_qd, model.spring_indices, model.spring_rest_length, model.spring_stiffness, model.spring_damping], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.spring_count: + tape.launch( + func=eval_springs, + dim=model.spring_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + model.spring_indices, + model.spring_rest_length, + model.spring_stiffness, + model.spring_damping, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # triangle elastic and lift/drag forces - if (model.tri_count and model.tri_ke > 0.0): - - tape.launch(func=eval_triangles, - dim=model.tri_count, - inputs=[ - state_in.particle_q, - state_in.particle_qd, - model.tri_indices, - model.tri_poses, - model.tri_activations, - model.tri_ke, - model.tri_ka, - model.tri_kd, - model.tri_drag, - model.tri_lift - ], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.tri_count and model.tri_ke > 0.0: + tape.launch( + func=eval_triangles, + dim=model.tri_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + model.tri_indices, + model.tri_poses, + model.tri_activations, + model.tri_ke, + model.tri_ka, + model.tri_kd, + model.tri_drag, + model.tri_lift, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # triangle/triangle contacts - if (model.enable_tri_collisions and model.tri_count and model.tri_ke > 0.0): - tape.launch(func=eval_triangles_contact, - dim=model.tri_count * model.particle_count, - inputs=[ - model.particle_count, - state_in.particle_q, - state_in.particle_qd, - model.tri_indices, - model.tri_poses, - model.tri_activations, - model.tri_ke, - model.tri_ka, - model.tri_kd, - model.tri_drag, - model.tri_lift - ], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.enable_tri_collisions and model.tri_count and model.tri_ke > 0.0: + tape.launch( + func=eval_triangles_contact, + dim=model.tri_count * model.particle_count, + inputs=[ + model.particle_count, + state_in.particle_q, + state_in.particle_qd, + model.tri_indices, + model.tri_poses, + model.tri_activations, + model.tri_ke, + model.tri_ka, + model.tri_kd, + model.tri_drag, + model.tri_lift, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # triangle bending - if (model.edge_count): - - tape.launch(func=eval_bending, - dim=model.edge_count, - inputs=[state_in.particle_q, state_in.particle_qd, model.edge_indices, model.edge_rest_angle, model.edge_ke, model.edge_kd], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.edge_count: + tape.launch( + func=eval_bending, + dim=model.edge_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + model.edge_indices, + model.edge_rest_angle, + model.edge_ke, + model.edge_kd, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # particle ground contact - if (model.ground and model.particle_count): - - tape.launch(func=eval_contacts, - dim=model.particle_count, - inputs=[state_in.particle_q, state_in.particle_qd, model.contact_ke, model.contact_kd, model.contact_kf, model.contact_mu], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.ground and model.particle_count: + tape.launch( + func=eval_contacts, + dim=model.particle_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + model.contact_ke, + model.contact_kd, + model.contact_kf, + model.contact_mu, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # tetrahedral FEM - if (model.tet_count): - - tape.launch(func=eval_tetrahedra, - dim=model.tet_count, - inputs=[state_in.particle_q, state_in.particle_qd, model.tet_indices, model.tet_poses, model.tet_activations, model.tet_materials], - outputs=[state_out.particle_f], - adapter=model.adapter) - + if model.tet_count: + tape.launch( + func=eval_tetrahedra, + dim=model.tet_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + model.tet_indices, + model.tet_poses, + model.tet_activations, + model.tet_materials, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) - #---------------------------- + # ---------------------------- # articulations - if (model.link_count): - + if model.link_count: # evaluate body transforms tape.launch( func=eval_rigid_fk, @@ -2328,19 +2630,17 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru state_in.joint_q, model.joint_X_pj, model.joint_X_cm, - model.joint_axis - ], - outputs=[ - state_out.body_X_sc, - state_out.body_X_sm + model.joint_axis, ], + outputs=[state_out.body_X_sc, state_out.body_X_sm], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) # evaluate joint inertias, motion vectors, and forces tape.launch( func=eval_rigid_id, - dim=model.articulation_count, + dim=model.articulation_count, inputs=[ model.articulation_joint_start, model.joint_type, @@ -2356,7 +2656,7 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru state_out.body_X_sc, state_out.body_X_sm, model.joint_X_pj, - model.gravity + model.gravity, ], outputs=[ state_out.joint_S_s, @@ -2366,9 +2666,10 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru state_out.body_a_s, ], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) - if (model.ground and model.contact_count > 0): + if model.ground and model.contact_count > 0: # evaluate contact forces tape.launch( func=eval_rigid_contacts_art, @@ -2380,46 +2681,45 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.contact_point0, model.contact_dist, model.contact_material, - model.shape_materials - ], - outputs=[ - state_out.body_f_s + model.shape_materials, ], + outputs=[state_out.body_f_s], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) # particle shape contact - if (model.particle_count): - + if model.particle_count: # tape.launch(func=eval_soft_contacts, # dim=model.particle_count*model.shape_count, # inputs=[state_in.particle_q, state_in.particle_qd, model.contact_ke, model.contact_kd, model.contact_kf, model.contact_mu], # outputs=[state_out.particle_f], # adapter=model.adapter) - tape.launch(func=eval_soft_contacts, - dim=model.particle_count*model.shape_count, - inputs=[ - model.particle_count, - state_in.particle_q, - state_in.particle_qd, - state_in.body_X_sc, - state_in.body_v_s, - model.shape_transform, - model.shape_body, - model.shape_geo_type, - torch.Tensor(), - model.shape_geo_scale, - model.shape_materials, - model.contact_ke, - model.contact_kd, - model.contact_kf, - model.contact_mu], - # outputs - outputs=[ - state_out.particle_f, - state_out.body_f_s], - adapter=model.adapter) + tape.launch( + func=eval_soft_contacts, + dim=model.particle_count * model.shape_count, + inputs=[ + model.particle_count, + state_in.particle_q, + state_in.particle_qd, + state_in.body_X_sc, + state_in.body_v_s, + model.shape_transform, + model.shape_body, + model.shape_geo_type, + torch.Tensor(), + model.shape_geo_scale, + model.shape_materials, + model.contact_ke, + model.contact_kd, + model.contact_kf, + model.contact_mu, + ], + # outputs + outputs=[state_out.particle_f, state_out.body_f_s], + adapter=model.adapter, + ) # evaluate muscle actuation tape.launch( @@ -2432,13 +2732,12 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.muscle_params, model.muscle_links, model.muscle_points, - model.muscle_activation - ], - outputs=[ - state_out.body_f_s + model.muscle_activation, ], + outputs=[state_out.body_f_s], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) # evaluate joint torques tape.launch( @@ -2462,18 +2761,14 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.joint_limit_kd, model.joint_axis, state_out.joint_S_s, - state_out.body_f_s - ], - outputs=[ - state_out.body_ft_s, - state_out.joint_tau + state_out.body_f_s, ], + outputs=[state_out.body_ft_s, state_out.joint_tau], adapter=model.adapter, - preserve_output=True) - - - if (update_mass_matrix): + preserve_output=True, + ) + if update_mass_matrix: model.alloc_mass_matrix() # build J @@ -2486,29 +2781,27 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.articulation_J_start, model.joint_parent, model.joint_qd_start, - state_out.joint_S_s - ], - outputs=[ - model.J + state_out.joint_S_s, ], + outputs=[model.J], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) # build M tape.launch( func=eval_rigid_mass, - dim=model.articulation_count, + dim=model.articulation_count, inputs=[ # inputs model.articulation_joint_start, model.articulation_M_start, - state_out.body_I_s - ], - outputs=[ - model.M + state_out.body_I_s, ], + outputs=[model.M], adapter=model.adapter, - preserve_output=True) + preserve_output=True, + ) # form P = M*J df.matmul_batched( @@ -2521,11 +2814,12 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru 0, model.articulation_M_start, model.articulation_J_start, - model.articulation_J_start, # P start is the same as J start since it has the same dims as J + model.articulation_J_start, # P start is the same as J start since it has the same dims as J model.M, model.J, model.P, - adapter=model.adapter) + adapter=model.adapter, + ) # form H = J^T*P df.matmul_batched( @@ -2533,16 +2827,17 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.articulation_count, model.articulation_J_cols, model.articulation_J_cols, - model.articulation_J_rows, # P rows is the same as J rows + model.articulation_J_rows, # P rows is the same as J rows 1, 0, model.articulation_J_start, - model.articulation_J_start, # P start is the same as J start since it has the same dims as J + model.articulation_J_start, # P start is the same as J start since it has the same dims as J model.articulation_H_start, model.J, model.P, model.H, - adapter=model.adapter) + adapter=model.adapter, + ) # compute decomposition tape.launch( @@ -2552,13 +2847,12 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru model.articulation_H_start, model.articulation_H_rows, model.H, - model.joint_armature - ], - outputs=[ - model.L + model.joint_armature, ], + outputs=[model.L], adapter=model.adapter, - skip_check_grad=True) + skip_check_grad=True, + ) tmp = torch.zeros_like(state_out.joint_tau) @@ -2567,19 +2861,18 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru func=eval_dense_solve_batched, dim=model.articulation_count, inputs=[ - model.articulation_dof_start, + model.articulation_dof_start, model.articulation_H_start, - model.articulation_H_rows, + model.articulation_H_rows, model.H, model.L, state_out.joint_tau, - tmp - ], - outputs=[ - state_out.joint_qdd + tmp, ], + outputs=[state_out.joint_qdd], adapter=model.adapter, - skip_check_grad=True) + skip_check_grad=True, + ) # integrate joint dofs -> joint coords tape.launch( @@ -2592,38 +2885,46 @@ def _simulate(self, tape, model, state_in, state_out, dt, update_mass_matrix=Tru state_in.joint_q, state_in.joint_qd, state_out.joint_qdd, - dt - ], - outputs=[ - state_out.joint_q, - state_out.joint_qd + dt, ], - adapter=model.adapter) + outputs=[state_out.joint_q, state_out.joint_qd], + adapter=model.adapter, + ) - #---------------------------- + # ---------------------------- # integrate particles - if (model.particle_count): - tape.launch(func=integrate_particles, - dim=model.particle_count, - inputs=[state_in.particle_q, state_in.particle_qd, state_out.particle_f, model.particle_inv_mass, model.gravity, dt], - outputs=[state_out.particle_q, state_out.particle_qd], - adapter=model.adapter) + if model.particle_count: + tape.launch( + func=integrate_particles, + dim=model.particle_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + state_out.particle_f, + model.particle_inv_mass, + model.gravity, + dt, + ], + outputs=[state_out.particle_q, state_out.particle_qd], + adapter=model.adapter, + ) return state_out @df.kernel -def solve_springs(x: df.tensor(df.float3), - v: df.tensor(df.float3), - invmass: df.tensor(float), - spring_indices: df.tensor(int), - spring_rest_lengths: df.tensor(float), - spring_stiffness: df.tensor(float), - spring_damping: df.tensor(float), - dt: float, - delta: df.tensor(df.float3)): - +def solve_springs( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + invmass: df.tensor(float), + spring_indices: df.tensor(int), + spring_rest_lengths: df.tensor(float), + spring_stiffness: df.tensor(float), + spring_damping: df.tensor(float), + dt: float, + delta: df.tensor(df.float3), +): tid = df.tid() i = df.load(spring_indices, tid * 2 + 0) @@ -2652,35 +2953,35 @@ def solve_springs(x: df.tensor(df.float3), dcdt = dot(dir, vij) # damping based on relative velocity. - #fs = dir * (ke * c + kd * dcdt) + # fs = dir * (ke * c + kd * dcdt) wi = df.load(invmass, i) wj = df.load(invmass, j) denom = wi + wj - alpha = 1.0/(ke*dt*dt) - - multiplier = c / (denom)# + alpha) + alpha = 1.0 / (ke * dt * dt) - xd = dir*multiplier + multiplier = c / (denom) # + alpha) - df.atomic_sub(delta, i, xd*wi) - df.atomic_add(delta, j, xd*wj) + xd = dir * multiplier + df.atomic_sub(delta, i, xd * wi) + df.atomic_add(delta, j, xd * wj) @df.kernel -def solve_tetrahedra(x: df.tensor(df.float3), - v: df.tensor(df.float3), - inv_mass: df.tensor(float), - indices: df.tensor(int), - pose: df.tensor(df.mat33), - activation: df.tensor(float), - materials: df.tensor(float), - dt: float, - relaxation: float, - delta: df.tensor(df.float3)): - +def solve_tetrahedra( + x: df.tensor(df.float3), + v: df.tensor(df.float3), + inv_mass: df.tensor(float), + indices: df.tensor(int), + pose: df.tensor(df.mat33), + activation: df.tensor(float), + materials: df.tensor(float), + dt: float, + relaxation: float, + delta: df.tensor(df.float3), +): tid = df.tid() i = df.load(indices, tid * 4 + 0) @@ -2735,13 +3036,13 @@ def solve_tetrahedra(x: df.tensor(df.float3), r_s = df.sqrt(abs(tr - 3.0)) C = r_s - if (r_s == 0.0): + if r_s == 0.0: return - - if (tr < 3.0): + + if tr < 3.0: r_s = 0.0 - r_s - dCdx = F*df.transpose(Dm)*(1.0/r_s) + dCdx = F * df.transpose(Dm) * (1.0 / r_s) alpha = 1.0 + k_mu / k_lambda # C_Neo @@ -2754,35 +3055,37 @@ def solve_tetrahedra(x: df.tensor(df.float3), # C_Spherical # r_s = df.sqrt(dot(f1, f1) + dot(f2, f2) + dot(f3, f3)) # r_s_inv = 1.0/r_s - # C = r_s - df.sqrt(3.0) + # C = r_s - df.sqrt(3.0) # dCdx = F*df.transpose(Dm)*r_s_inv # alpha = 1.0 # C_D - #r_s = df.sqrt(dot(f1, f1) + dot(f2, f2) + dot(f3, f3)) - #C = r_s*r_s - 3.0 - #dCdx = F*df.transpose(Dm)*2.0 - #alpha = 1.0 - - grad1 = float3(dCdx[0,0], dCdx[1,0], dCdx[2,0]) - grad2 = float3(dCdx[0,1], dCdx[1,1], dCdx[2,1]) - grad3 = float3(dCdx[0,2], dCdx[1,2], dCdx[2,2]) - grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0) - - denom = dot(grad0,grad0)*w0 + dot(grad1,grad1)*w1 + dot(grad2,grad2)*w2 + dot(grad3,grad3)*w3 - multiplier = C/(denom + 1.0/(k_mu*dt*dt*rest_volume)) + # r_s = df.sqrt(dot(f1, f1) + dot(f2, f2) + dot(f3, f3)) + # C = r_s*r_s - 3.0 + # dCdx = F*df.transpose(Dm)*2.0 + # alpha = 1.0 - delta0 = grad0*multiplier - delta1 = grad1*multiplier - delta2 = grad2*multiplier - delta3 = grad3*multiplier + grad1 = float3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0]) + grad2 = float3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1]) + grad3 = float3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2]) + grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0) + denom = ( + dot(grad0, grad0) * w0 + + dot(grad1, grad1) * w1 + + dot(grad2, grad2) * w2 + + dot(grad3, grad3) * w3 + ) + multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume)) + delta0 = grad0 * multiplier + delta1 = grad1 * multiplier + delta2 = grad2 * multiplier + delta3 = grad3 * multiplier # hydrostatic part J = df.determinant(F) - C_vol = J - alpha # dCdx = df.mat33(cross(f2, f3), cross(f3, f1), cross(f1, f2))*df.transpose(Dm) @@ -2795,10 +3098,15 @@ def solve_tetrahedra(x: df.tensor(df.float3), grad1 = df.cross(x20, x30) * s grad2 = df.cross(x30, x10) * s grad3 = df.cross(x10, x20) * s - grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0) + grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0) - denom = dot(grad0, grad0)*w0 + dot(grad1, grad1)*w1 + dot(grad2, grad2)*w2 + dot(grad3, grad3)*w3 - multiplier = C_vol/(denom + 1.0/(k_lambda*dt*dt*rest_volume)) + denom = ( + dot(grad0, grad0) * w0 + + dot(grad1, grad1) * w1 + + dot(grad2, grad2) * w2 + + dot(grad3, grad3) * w3 + ) + multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume)) delta0 = delta0 + grad0 * multiplier delta1 = delta1 + grad1 * multiplier @@ -2806,22 +3114,22 @@ def solve_tetrahedra(x: df.tensor(df.float3), delta3 = delta3 + grad3 * multiplier # apply forces - df.atomic_sub(delta, i, delta0*w0*relaxation) - df.atomic_sub(delta, j, delta1*w1*relaxation) - df.atomic_sub(delta, k, delta2*w2*relaxation) - df.atomic_sub(delta, l, delta3*w3*relaxation) + df.atomic_sub(delta, i, delta0 * w0 * relaxation) + df.atomic_sub(delta, j, delta1 * w1 * relaxation) + df.atomic_sub(delta, k, delta2 * w2 * relaxation) + df.atomic_sub(delta, l, delta3 * w3 * relaxation) @df.kernel def solve_contacts( - x: df.tensor(df.float3), - v: df.tensor(df.float3), + x: df.tensor(df.float3), + v: df.tensor(df.float3), inv_mass: df.tensor(float), mu: float, dt: float, - delta: df.tensor(df.float3)): - - tid = df.tid() + delta: df.tensor(df.float3), +): + tid = df.tid() x0 = df.load(x, tid) v0 = df.load(v, tid) @@ -2831,32 +3139,33 @@ def solve_contacts( n = df.float3(0.0, 1.0, 0.0) c = df.dot(n, x0) - 0.01 - if (c > 0.0): + if c > 0.0: return - # normal + # normal lambda_n = c - delta_n = n*lambda_n + delta_n = n * lambda_n # friction vn = df.dot(n, v0) vt = v0 - n * vn - lambda_f = df.max(mu*lambda_n, 0.0 - df.length(vt)*dt) - delta_f = df.normalize(vt)*lambda_f + lambda_f = df.max(mu * lambda_n, 0.0 - df.length(vt) * dt) + delta_f = df.normalize(vt) * lambda_f - df.atomic_add(delta, tid, delta_f - delta_n) + df.atomic_add(delta, tid, delta_f - delta_n) @df.kernel -def apply_deltas(x_orig: df.tensor(df.float3), - v_orig: df.tensor(df.float3), - x_pred: df.tensor(df.float3), - delta: df.tensor(df.float3), - dt: float, - x_out: df.tensor(df.float3), - v_out: df.tensor(df.float3)): - +def apply_deltas( + x_orig: df.tensor(df.float3), + v_orig: df.tensor(df.float3), + x_pred: df.tensor(df.float3), + delta: df.tensor(df.float3), + dt: float, + x_out: df.tensor(df.float3), + v_out: df.tensor(df.float3), +): tid = df.tid() x0 = df.load(x_orig, tid) @@ -2866,7 +3175,7 @@ def apply_deltas(x_orig: df.tensor(df.float3), d = df.load(delta, tid) x_new = xp + d - v_new = (x_new - x0)/dt + v_new = (x_new - x0) / dt df.store(x_out, tid, x_new) df.store(v_out, tid, v_new) @@ -2878,7 +3187,7 @@ class XPBDIntegrator: After constructing `Model` and `State` objects this time-integrator may be used to advance the simulation state forward in time. - Semi-implicit time integration is a variational integrator that + Semi-implicit time integration is a variational integrator that preserves energy, however it not unconditionally stable, and requires a time-step small enough to support the required stiffness and damping forces. @@ -2897,10 +3206,11 @@ class XPBDIntegrator: def __init__(self): pass - - def forward(self, model: Model, state_in: State, dt: float) -> State: + def forward( + self, model: Model, state_in: State, dt: float, reset_tape: bool = True + ) -> State: """Performs a single integration step forward in time - + This method inserts a node into the PyTorch computational graph with references to all model and state tensors such that gradients can be propagrated back through the simulation step. @@ -2917,87 +3227,121 @@ def forward(self, model: Model, state_in: State, dt: float) -> State: """ - if dflex.config.no_grad: - # if no gradient required then do inplace update self._simulate(df.Tape(), model, state_in, state_in, dt) return state_in else: - - # get list of inputs and outputs for PyTorch tensor tracking + # get list of inputs and outputs for PyTorch tensor tracking inputs = [*state_in.flatten(), *model.flatten()] # allocate new output state_out = model.state() # run sim as a PyTorch op - tensors = SimulateFunc.apply(self, model, state_in, state_out, dt, *inputs) + tensors = SimulateFunc.apply( + self, model, state_in, state_out, dt, reset_tape, *inputs + ) return state_out - - def _simulate(self, tape, model, state_in, state_out, dt): - with dflex.util.ScopedTimer("simulate", False): - # alloc particle force buffer - if (model.particle_count): + if model.particle_count: state_out.particle_f.zero_() q_pred = torch.zeros_like(state_in.particle_q) qd_pred = torch.zeros_like(state_in.particle_qd) - #---------------------------- + # ---------------------------- # integrate particles - if (model.particle_count): - tape.launch(func=integrate_particles, - dim=model.particle_count, - inputs=[state_in.particle_q, state_in.particle_qd, state_out.particle_f, model.particle_inv_mass, model.gravity, dt], - outputs=[q_pred, qd_pred], - adapter=model.adapter) + if model.particle_count: + tape.launch( + func=integrate_particles, + dim=model.particle_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + state_out.particle_f, + model.particle_inv_mass, + model.gravity, + dt, + ], + outputs=[q_pred, qd_pred], + adapter=model.adapter, + ) # contacts - if (model.particle_count and model.ground): - - tape.launch(func=solve_contacts, - dim=model.particle_count, - inputs=[q_pred, qd_pred, model.particle_inv_mass, model.contact_mu, dt], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.particle_count and model.ground: + tape.launch( + func=solve_contacts, + dim=model.particle_count, + inputs=[ + q_pred, + qd_pred, + model.particle_inv_mass, + model.contact_mu, + dt, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # damped springs - if (model.spring_count): - - tape.launch(func=solve_springs, - dim=model.spring_count, - inputs=[q_pred, qd_pred, model.particle_inv_mass, model.spring_indices, model.spring_rest_length, model.spring_stiffness, model.spring_damping, dt], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.spring_count: + tape.launch( + func=solve_springs, + dim=model.spring_count, + inputs=[ + q_pred, + qd_pred, + model.particle_inv_mass, + model.spring_indices, + model.spring_rest_length, + model.spring_stiffness, + model.spring_damping, + dt, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # tetrahedral FEM - if (model.tet_count): - - tape.launch(func=solve_tetrahedra, - dim=model.tet_count, - inputs=[q_pred, qd_pred, model.particle_inv_mass, model.tet_indices, model.tet_poses, model.tet_activations, model.tet_materials, dt, model.relaxation], - outputs=[state_out.particle_f], - adapter=model.adapter) + if model.tet_count: + tape.launch( + func=solve_tetrahedra, + dim=model.tet_count, + inputs=[ + q_pred, + qd_pred, + model.particle_inv_mass, + model.tet_indices, + model.tet_poses, + model.tet_activations, + model.tet_materials, + dt, + model.relaxation, + ], + outputs=[state_out.particle_f], + adapter=model.adapter, + ) # apply updates - tape.launch(func=apply_deltas, - dim=model.particle_count, - inputs=[state_in.particle_q, - state_in.particle_qd, - q_pred, - state_out.particle_f, - dt], - outputs=[state_out.particle_q, - state_out.particle_qd], - adapter=model.adapter) - + tape.launch( + func=apply_deltas, + dim=model.particle_count, + inputs=[ + state_in.particle_q, + state_in.particle_qd, + q_pred, + state_out.particle_f, + dt, + ], + outputs=[state_out.particle_q, state_out.particle_qd], + adapter=model.adapter, + ) return state_out diff --git a/diffrl_conda.yml b/diffrl_conda.yml index 4e952de8..cf32eb45 100644 --- a/diffrl_conda.yml +++ b/diffrl_conda.yml @@ -2,13 +2,22 @@ name: shac channels: - pytorch - defaults + - nvidia/label/cuda-11.8.0 dependencies: - - python=3.8.13=h12debd9_0 - - pytorch=1.11.0=py3.8_cuda11.3_cudnn8.2.0_0 - - torchvision=0.12.0=py38_cu113 + - python=3.8 + - pytorch-cuda=11.8 + - torchvision + - cuda=11.8 + - cuda-toolkit=11.8 + - pandas + - matplotlib + - seaborn + - pip - pip: - - pyyaml==6.0 - - tensorboard==2.8.0 - - tensorboardx==2.5 - - urdfpy==0.0.22 - - usd-core==22.3 + - urdfpy==0.0.22 + - usd-core + - hydra-core + - tqdm + - pytinydiffsim + - gym + - wandb diff --git a/examples/test_jacobian.py b/examples/test_jacobian.py new file mode 100644 index 00000000..dfd9f8c3 --- /dev/null +++ b/examples/test_jacobian.py @@ -0,0 +1,260 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import sys, os + +project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(project_dir) + +import argparse +import numpy as np +from time import time +import torch + +from shac.utils import torch_utils as tu +from shac import envs +from shac.utils.common import seeding +from shac.utils.torch_utils import jacobian, jacobian2 + + +EPS = 0.1 +ATOL = 1e-4 + + +def example_jac(args): + seeding() + + # Create environment + env_fn = getattr(envs, args.env) + + env_fn_kwargs = dict( + num_envs=args.num_envs, + device="cuda", + render=False, + seed=0, + no_grad=False, + stochastic_init=False, # True + ) + + env = env_fn(**env_fn_kwargs) + env.reset() + + def f(inputs): + """Wrapper function for a single simulation step function""" + # first set state + states = inputs[:, : env.num_obs] + env.state.joint_q.view(env.num_envs, -1)[:, 0] = 0.0 + env.state.joint_q.view(env.num_envs, -1)[:, 1:] = states[:, :5] + env.state.joint_qd.view(env.num_envs, -1)[:] = states[:, 5:] + + # compute and set action + actions = inputs[:, env.num_obs :] + # actions = torch.clip(actions, -1.0, 1.0) + # unscaled_actions = actions * env.action_strength + env.state.joint_act.view(env.num_envs, -1)[:, 3:] = actions + + next_state = env.integrator.forward( + env.model, + env.state, + env.sim_dt, + env.sim_substeps, + env.MM_caching_frequency, + False, + ) + next_state = torch.cat( + [ + next_state.joint_q.view(env.num_envs, -1)[:, 1:], + next_state.joint_qd.view(env.num_envs, -1), + ], + dim=-1, + ) + return next_state + + print("obs space:", env.num_obs) + print("act space:", env.num_acts) + states = np.random.randn(env.num_envs, env.num_obs) + actions = ( + np.random.uniform(-1, 1, (env.num_envs, env.num_acts)) * env.action_strength + ) + inputs = tu.to_torch(np.concatenate((states, actions), axis=-1)) + inputs.requires_grad_(True) + + # Now compute jacobian + now = time() + jac = jacobian(f, inputs) + total_time = time() - now + print("took {:.2f}".format(total_time)) + print("jacobian shape", jac.shape) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "jacs_{:}".format(args.env) + filename = f"{directory}/{filename}" + print("Saving to", filename) + np.save(filename, jac.detach().cpu().numpy()) + + for b in range(len(jac)): + for i in range(jac.shape[1]): + print(b, i, torch.norm(jac[b, i])) + print(b, torch.norm(jac[b])) + + +def example_jac2(args): + seeding() + + # Create environment + env_fn = getattr(envs, args.env) + + env_fn_kwargs = dict( + num_envs=args.num_envs, + device="cuda", + render=False, + seed=0, + no_grad=False, + stochastic_init=False, # True + ) + + env = env_fn(**env_fn_kwargs) + env.reset() + + print("obs space:", env.num_obs) + print("act space:", env.num_acts) + states = np.random.randn(env.num_envs, env.num_obs) + actions = ( + np.random.uniform(-1, 1, (env.num_envs, env.num_acts)) * env.action_strength + ) + inputs = tu.to_torch(np.concatenate((states, actions), axis=-1)) + inputs.requires_grad_(True) + + # Set state + states = inputs[:, : env.num_obs] + env.state.joint_q.view(env.num_envs, -1)[:, 0] = 0.0 + env.state.joint_q.view(env.num_envs, -1)[:, 1:] = states[:, :5] + env.state.joint_qd.view(env.num_envs, -1)[:] = states[:, 5:] + + # compute and set action + actions = inputs[:, env.num_obs :] + env.state.joint_act.view(env.num_envs, -1)[:, 3:] = actions + + next_state = env.integrator.forward( + env.model, + env.state, + env.sim_dt, + env.sim_substeps, + env.MM_caching_frequency, + False, + ) + outputs = torch.cat( + [ + next_state.joint_q.view(env.num_envs, -1)[:, 1:], + next_state.joint_qd.view(env.num_envs, -1), + ], + dim=-1, + ) + + # Now compute jacobian + now = time() + jac = jacobian2(outputs, inputs) + total_time = time() - now + print("took {:.2f}".format(total_time)) + print("jacobian shape", jac.shape) + + directory = "outputs" + if not os.path.exists(directory): + os.makedirs(directory) + + filename = "jacs2_{:}".format(args.env) + filename = f"{directory}/{filename}" + print("Saving to", filename) + np.save(filename, jac.detach().cpu().numpy()) + + for b in range(len(jac)): + for i in range(jac.shape[1]): + print(b, i, torch.norm(jac[b, i])) + print(b, torch.norm(jac[b])) + + +def test_jac(args): + seeding() + + # Create environment + env_fn = getattr(envs, args.env) + + env_fn_kwargs = dict( + num_envs=1, + device="cuda", + render=False, + seed=0, + no_grad=False, + stochastic_init=False, # True + ) + + env = env_fn(**env_fn_kwargs) + env.reset() + + def f(inputs): + """Wrapper function for a single simulation step function""" + # first set state + states = inputs[:, : env.num_obs] + env.state.joint_q.view(env.num_envs, -1)[:, 0] = 0.0 + env.state.joint_q.view(env.num_envs, -1)[:, 1:] = states[:, :5] + env.state.joint_qd.view(env.num_envs, -1)[:] = states[:, 5:] + + # compute and set action + actions = inputs[:, env.num_obs :] + actions = torch.clip(actions, -1.0, 1.0) + unscaled_actions = actions * env.action_strength + env.state.joint_act.view(env.num_envs, -1)[:, 3:] = unscaled_actions + + next_state = env.integrator.forward( + env.model, + env.state, + env.sim_dt, + env.sim_substeps, + env.MM_caching_frequency, + False, + ) + next_state = torch.cat( + [ + next_state.joint_q.view(env.num_envs, -1)[:, 1:], + next_state.joint_qd.view(env.num_envs, -1), + ], + dim=-1, + ) + return next_state.flatten() + + print("obs space:", env.num_obs) + print("act space:", env.num_acts) + states = tu.to_torch(np.random.randn(env.num_envs, env.num_obs)) + actions = tu.to_torch(np.random.uniform(-1, 1, (env.num_envs, env.num_acts))) + inputs = torch.cat((states, actions), dim=1) + inputs.requires_grad_(True) + + assert torch.autograd.gradcheck(f, (inputs,), eps=EPS, atol=ATOL) + + +def main(args): + print("\nJacobian method 1") + example_jac(args) + + print("\nJacobian method 2") + example_jac2(args) + + if args.test: + args.num_envs = 1 + test_jac(args) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--env", type=str, default="HopperEnv") + parser.add_argument("--num-envs", type=int, default=1) + parser.add_argument("--test", default=False, action="store_true") + + args = parser.parse_args() + main(args) diff --git a/examples/test_jacobian_warp.py b/examples/test_jacobian_warp.py new file mode 100644 index 00000000..61767f21 --- /dev/null +++ b/examples/test_jacobian_warp.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + + +## BROKEN + +import sys, os + +project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(project_dir) + +from time import time + +import torch +import functorch +import random + +from shac import envs +from shac.utils.common import seeding + +import argparse + +import matplotlib.pyplot as plt +import numpy as np + +from shac.utils import torch_utils as tu +from torch.autograd.functional import jacobian + +from warp.envs import HopperEnv + + +def test_jac(args): + seeding() + + # env_fn = getattr(envs, args.env) + + # env_fn_kwargs = dict( + # num_envs=args.num_envs, + # device="cuda", + # render=args.render, + # seed=0, + # no_grad=False, + # stochastic_init=False, # True + # ) + # if issubclass(env_fn, envs.DFlexEnv): + # env_fn_kwargs["MM_caching_frequency"] = 1 + + # env = env_fn(**env_fn_kwargs) + env = HopperEnv( + num_envs=args.num_envs, seed=0, no_grad=False, stochastic_init=False + ) + ob_vec = [] + obs = env.reset() + + def f(inputs): + # print("inputs", inputs.shape) + # first set state + # states = np.tile(states, (env.num_envs, len(states))) + states = inputs[:, : env.num_obs] + env.joint_q.view(env.num_envs, -1)[:, 0] = 0.0 + env.joint_q.view(env.num_envs, -1)[:, 1:] = states[:, :5] + env.joint_qd.view(env.num_envs, -1)[:] = states[:, 5:] + + # compute and set action + # actions = tu.to_torch(actions).view((env.num_envs, env.num_actions)) + # actions = inputs[:, env.num_obs :] + # actions = torch.clip(actions, -1.0, 1.0) + # unscaled_actions = actions * env.action_strength + # env.state.joint_act.view(env.num_envs, -1)[:, 3:] = unscaled_actions + env.assign_actions(tu.to_torch(actions)) + + env.update() + next_state = torch.cat( + [ + env.joint_q.view(env.num_envs, -1)[:, 1:], + env.joint_qd.view(env.num_envs, -1), + ], + dim=-1, + ) + return next_state + + print("obs space:", env.num_obs) + print("act space:", env.num_acts) + states = tu.to_torch(np.random.randn(env.num_envs, env.num_obs)) + actions = tu.to_torch(np.random.uniform(-1, 1, (env.num_envs, env.num_acts))) + print(states.shape) + print(actions.shape) + inputs = torch.cat((states, actions), dim=1) + now = time() + jac = jacobian(f, inputs) + # this below is a faster function which sadly doesn't work + # jac = functorch.vmap(functorch.jacrev(f))(inputs) + print("took {:.2f}".format(time() - now)) + print("jacobian", jac.shape) + + # print(jac) + + # discard cross-batched data + jac = torch.stack([jac[i, :, i] for i in range(env.num_envs)]) + print("jacobian", jac.shape) + + J = jac.detach().cpu().numpy() + np.save("jac", J) + + for b in range(len(jac)): + for i in range(jac.shape[1]): + print(b, i, torch.norm(jac[b, i])) + print(b, torch.norm(jac[b])) + + print("overall", torch.norm(jac)) + # print(jac) + + # print(env.model.J.shape) + + +def check_grad(fn, inputs, eps=1e-6, atol=1e-4, rtol=1e-6): + if inputs.grad is not None: + inputs.grad.zero_() + out = fn(inputs) + out.backward() + analytical = inputs.grad.clone() + x2, x1 = inputs + eps, inputs - eps + numerical = (fn(x2) - fn(x1)) / (2 * eps) + assert torch.allclose( + numerical, analytical, rtol, atol + ), "numerical gradient was: {}, analytical was: {}".format(numerical, analytical) + return (numerical, analytical) + + +def main(args): + test_jac(args) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--env", type=str, default="CartPoleSwingUpEnv") + parser.add_argument("--num-envs", type=int, default=1) + parser.add_argument("--render", default=False, action="store_true") + + args = parser.parse_args() + main(args) diff --git a/examples/train_rl.py b/examples/train_rl.py index dbfba76e..88fd9e6e 100644 --- a/examples/train_rl.py +++ b/examples/train_rl.py @@ -15,47 +15,50 @@ from gym import wrappers from shac import envs from shac.utils.common import * -from dmanip.envs.claw_env import GoalType, ActionType, ObjectType - - -# def create_dflex_env(**kwargs): -# env_fn = getattr(envs, cfg_train["params"]["diff_env"]["name"]) -# -# env = env_fn( -# num_envs=cfg_train["params"]["config"]["num_actors"], -# render=args.render, -# seed=args.seed, -# episode_length=cfg_train["params"]["diff_env"].get("episode_length", 1000), -# no_grad=True, -# stochastic_init=cfg_train["params"]["diff_env"]["stochastic_env"], -# MM_caching_frequency=cfg_train["params"]["diff_env"].get( -# "MM_caching_frequency", 1 -# ), -# ) -# -# print("num_envs = ", env.num_envs) -# print("num_actions = ", env.num_actions) -# print("num_obs = ", env.num_obs) -# -# frames = kwargs.pop("frames", 1) -# if frames > 1: -# env = wrappers.FrameStack(env, frames, False) -# -# return env -# + + +def create_dflex_env(**kwargs): + env_fn = getattr(envs, cfg_train["params"]["diff_env"]["name"]) + + env = env_fn( + num_envs=cfg_train["params"]["config"]["num_actors"], + render=args.render, + seed=args.seed, + episode_length=cfg_train["params"]["diff_env"].get("episode_length", 1000), + no_grad=True, + stochastic_init=cfg_train["params"]["diff_env"]["stochastic_env"], + MM_caching_frequency=cfg_train["params"]["diff_env"].get( + "MM_caching_frequency", 1 + ), + ) + + print("num_envs = ", env.num_envs) + print("num_actions = ", env.num_actions) + print("num_obs = ", env.num_obs) + + frames = kwargs.pop("frames", 1) + if frames > 1: + env = wrappers.FrameStack(env, frames, False) + + return env def parse_diff_env_kwargs(diff_env): env_kwargs = {} for key, value in diff_env.items(): - if key in ["name", "episode_length", "stochastic_env"]: + if key in [ + "name", + "episode_length", + "stochastic_env", + "num_envs", + "MM_caching_frequency", + "no_grad", + "render", + "seed", + "stochastic_init", + ]: continue - if key == "goal_type": - env_kwargs["goal_type"] = GoalType(value) - if key == "action_type": - env_kwargs["action_type"] = ActionType(value) - if key == "object_type": - env_kwargs["object_type"] = ObjectType(value) + env_kwargs[key] = value print("parsed kwargs:", env_kwargs) return env_kwargs @@ -183,19 +186,19 @@ def after_print_stats(self, frame, epoch_num, total_time): ) -# vecenv.register( -# "DFLEX", -# lambda config_name, num_actors, **kwargs: RLGPUEnv( -# config_name, num_actors, **kwargs -# ), -# ) -# env_configurations.register( -# "dflex", -# { -# "env_creator": lambda **kwargs: create_dflex_env(**kwargs), -# "vecenv_type": "DFLEX", -# }, -# ) +vecenv.register( + "DFLEX", + lambda config_name, num_actors, **kwargs: RLGPUEnv( + config_name, num_actors, **kwargs + ), +) +env_configurations.register( + "dflex", + { + "env_creator": lambda **kwargs: create_dflex_env(**kwargs), + "vecenv_type": "DFLEX", + }, +) vecenv.register( "WARP", @@ -320,7 +323,6 @@ def get_args(): # TODO: delve into the arguments if __name__ == "__main__": - args = get_args() with open(args.cfg, "r") as f: diff --git a/scripts/cfg/alg/mpc.yaml b/scripts/cfg/alg/mpc.yaml new file mode 100644 index 00000000..d52b85fc --- /dev/null +++ b/scripts/cfg/alg/mpc.yaml @@ -0,0 +1,12 @@ +name: mpc +config: + planner: + _target_: shac.algorithms.mpc.Planner + noise: 0.1 + policy: + _target_: shac.algorithms.mpc.Policy + num_actions: ${env.num_actions} + horizon: 0.25 + dt: 0.01666667 + max_steps: ${env.config.episode_length} + policy_type: "zero" diff --git a/scripts/cfg/alg/ppo.yaml b/scripts/cfg/alg/ppo.yaml new file mode 100644 index 00000000..d04ed542 --- /dev/null +++ b/scripts/cfg/alg/ppo.yaml @@ -0,0 +1,72 @@ +algo: + name: a2c_continuous + +model: + name: continuous_a2c_logstd + +network: + name: actor_critic + separate: False + space: + continuous: + mu_activation: None + sigma_activation: None + + mu_init: + name: default + sigma_init: + name: const_initializer + val: 0 + fixed_sigma: True + mlp: + units: ${resolve_default:[64, 64],${..env.actor_mlp.units}} + activation: elu + d2rl: False + + initializer: + name: default + regularizer: + name: None + +load_checkpoint: False +load_path: nn/${..env.name}_ppo.pth + +config: + name: ${..env.name}_ppo + env_name: ${..env.name} + multi_gpu: False + ppo: True + mixed_precision: False + normalize_input: True + normalize_value: True + reward_shaper: + scale_value: 0.01 + normalize_advantage: True + gamma: 0.99 + tau: 0.95 + learning_rate: ${resolve_default:3e-4${...env.ppo.lr}} + lr_schedule: adaptive + lr_threshold: 0.008 + kl_threshold: 0.008 + score_to_win: 20000 + max_epochs: ${resolve_default:5000,${...env.ppo.max_epochs}} + save_best_after: ${resolve_Default:100${...env.ppo.save_best_after}} + save_frequency: ${resolve_default:400,${...env.ppo.save_interval}} + grad_norm: 1.0 + entropy_coef: 0.0 + truncate_grads: True + e_clip: 0.2 + num_actors: ${resolve_default:2048,${..env.ppo.num_actors}} + steps_num: ${resolve_default:32,${...env.ppo.max_epochs}} + minibatch_size: ${resolve_default:16384,${...env.ppo.minibatch_size} + mini_epochs: 5 + critic_coef: 4 + clip_value: True + seq_len: 4 + bounds_loss_coef: 0.0001 + + player: + games_num: ${resolve_default:24,${....env.player.games_num}} + num_actors: ${resolve_default:3,${....env.player.num_actors}} + determenistic: True + print_stats: True diff --git a/scripts/cfg/alg/shac.yaml b/scripts/cfg/alg/shac.yaml new file mode 100644 index 00000000..46671547 --- /dev/null +++ b/scripts/cfg/alg/shac.yaml @@ -0,0 +1,43 @@ +name: shac +params: + network: + actor: ActorStochasticMLP # ActorDeterministicMLP + actor_mlp: + units: ${env.shac.actor_mlp.units} + activation: elu + + critic: CriticMLP + critic_mlp: + units: ${env.shac.critic_mlp.units} + activation: elu + + config: + name: ${env.name}_shac + actor_learning_rate: 2e-3 # ${resolve_default:2e-3,${..env.actor_lr}} # adam + critic_learning_rate: 2e-3 # ${resolve_default:2e-3,${..env.critic_lr}} # adam + lr_schedule: linear # ('constant', 'linear') + target_critic_alpha: 0.2 # ${resolve_default:0.2,${..env.target_critic_alpha}} + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda # ('td-lambda', 'one-step') + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: + - 0.7 + - 0.95 # adam + max_epochs: ${env.shac.max_epochs} + steps_min: 8 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: ${env.config.num_envs} # ${resolve_default:64,${..env.config.num_envs}} + save_interval: 400 # ${resolve_default:400,${..env.save_interval}} + contact_theshold: 150 + + player: + determenistic: True + games_num: ${env.player.games_num} + num_actors: ${env.player.num_actors} + print_stats: True diff --git a/scripts/cfg/alg/shac2.yaml b/scripts/cfg/alg/shac2.yaml new file mode 100644 index 00000000..d5040a9c --- /dev/null +++ b/scripts/cfg/alg/shac2.yaml @@ -0,0 +1,66 @@ +name: shac2 +params: + network: + actor: + _target_: shac.models.actor.ActorStochasticMLP # ActorDeterministicMLP + device: ${general.device} + cfg_network: + actor_mlp: + units: ${env.shac2.actor_mlp.units} + activation: elu + + critic: + _target_: shac.models.critic.QCriticMLP + cfg_network: + critic_mlp: + units: ${env.shac2.critic_mlp.units} + activation: elu + + config: + name: ${env.name}_${...name} + actor_optimizer: ${..default_actor_opt} + critic_optimizer: ${..default_critic_opt} + lr_schedule: linear # ['constant', 'linear', 'adaptive'] + target_critic_alpha: ${resolve_default:0.4,${env.shac2.target_critic_alpha}} + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda # ['td-lambda', 'one-step'] + lam: ${env.shac2.lambda} + num_batch: 4 + gamma: 0.99 + max_epochs: ${resolve_default:2000,${env.shac2.max_epochs}} + steps_num: ${resolve_default:32,${env.shac2.steps_num}} + grad_norm: 1.0 + truncate_grads: True + save_interval: ${resolve_default:400,${env.shac2.save_interval}} + early_stopping_patience: ${env.shac2.max_epochs} + rew_scale: 1.0 + score_keys: [] + + player: + determenistic: True + games_num: ${resolve_default:1,${env.games_num}} + num_actors: ${resolve_default:1,${env.player.num_actors}} + print_stats: True + + default_actor_opt: + _target_: torch.optim.Adam + lr: ${env.shac2.actor_lr} # adam + betas: ${env.shac2.betas} # adam + + default_critic_opt: + _target_: torch.optim.Adam + lr: ${env.shac2.critic_lr} # adam + betas: ${env.shac2.betas} # adam + + default_adaptive_scheduler: + _target_: rl_games.common.schedulers.AdaptiveScheduler + kl_threshold : 0.01 + + default_linear_scheduler: + _target_: rl_games.common.schedulers.LinearScheduler + start_lr: ${..default_actor_opt.lr} + min_lr: 1e-5 + max_steps: ${..config.max_epochs} + apply_to_entropy: False diff --git a/scripts/cfg/alg/shac_ant.yaml b/scripts/cfg/alg/shac_ant.yaml new file mode 100644 index 00000000..c5e2a64b --- /dev/null +++ b/scripts/cfg/alg/shac_ant.yaml @@ -0,0 +1,45 @@ +name: shac +params: + diff_env: + name: AntEnv + stochastic_env: True + episode_length: 1000 + MM_caching_frequency: 16 + + network: + actor: ActorStochasticMLP # ActorDeterministicMLP + actor_mlp: + units: [128, 64, 32] + activation: elu + + critic: CriticMLP + critic_mlp: + units: [64, 64] + activation: elu + + config: + name: df_ant_shac + actor_learning_rate: 2e-3 # adam + critic_learning_rate: 2e-3 # adam + lr_schedule: linear # ['constant', 'linear'] + target_critic_alpha: 0.2 + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda # ['td-lambda', 'one-step'] + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: [0.7, 0.95] # adam + max_epochs: 2000 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: 64 + save_interval: 400 + + player: + determenistic: True + games_num: 1 + num_actors: 1 + print_stats: True diff --git a/scripts/cfg/alg/shac_cartpole.yaml b/scripts/cfg/alg/shac_cartpole.yaml new file mode 100644 index 00000000..ae2fe2e0 --- /dev/null +++ b/scripts/cfg/alg/shac_cartpole.yaml @@ -0,0 +1,53 @@ +name: shac +params: + diff_env: + name: CartPoleSwingUpEnv + stochastic_env: True + episode_length: 240 + + network: + actor: ActorStochasticMLP #ActorDeterministicMLP + actor_mlp: + units: + - 64 + - 64 + activation: elu + + critic: CriticMLP + critic_mlp: + units: [64, 64] + activation: elu + + general: + device: ${general.device} + seed: ${general.seed} + render: ${general.render} + train: ${general.train} + logdir: ${general.logdir} + + config: + name: df_cartpole_swing_up_shac + actor_learning_rate: 1e-3 # adam + critic_learning_rate: 1e-2 # adam + lr_schedule: linear # ['constant', 'linear'] + target_critic_alpha: 0.2 + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda # ['td-lambda', 'one-step'] + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: [0.7, 0.95] # adam + max_epochs: 200 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: 64 + save_interval: 100 + + player: + determenistic: True + games_num: 4 + num_actors: 4 + print_stats: True diff --git a/scripts/cfg/alg/shac_cartpole_warp.yaml b/scripts/cfg/alg/shac_cartpole_warp.yaml new file mode 100644 index 00000000..b61a8b24 --- /dev/null +++ b/scripts/cfg/alg/shac_cartpole_warp.yaml @@ -0,0 +1,50 @@ +name: shac2 +params: + diff_env: + name: CartPoleSwingUpWarpEnv + stochastic_env: True + episode_length: 240 + + network: + actor: ActorStochasticMLP #ActorDeterministicMLP + actor_mlp: + units: + - 64 + - 64 + activation: elu + + critic: CriticMLP + critic_mlp: + units: + - 64 + - 64 + activation: elu + + config: + name: warp_cartpole_swing_up_shac + actor_learning_rate: 1e-3 # adam + critic_learning_rate: 1e-2 # adam + lr_schedule: linear # ['constant', 'linear'] + target_critic_alpha: 0.2 + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda # ['td-lambda', 'one-step'] + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: + - 0.7 + - 0.95 # adam + max_epochs: 200 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: 64 + save_interval: 100 + + player: + determenistic: True + games_num: 4 + num_actors: 4 + print_stats: True diff --git a/scripts/cfg/alg/shac_hopper.yaml b/scripts/cfg/alg/shac_hopper.yaml new file mode 100644 index 00000000..79761cc1 --- /dev/null +++ b/scripts/cfg/alg/shac_hopper.yaml @@ -0,0 +1,54 @@ +name: shac +params: + diff_env: + name: HopperEnv + stochastic_env: False + episode_length: 1000 + MM_caching_frequency: 16 + num_envs: 2 + early_termination: False + + network: + actor: ActorStochasticMLP + actor_mlp: + units: [128, 64, 32] + activation: elu + + critic: CriticMLP + critic_mlp: + units: [64, 64] + activation: elu + + general: + device: ${general.device} + seed: ${general.seed} + render: ${general.render} + train: ${general.train} + logdir: ${general.logdir} + + config: + name: df_hopper_shac + actor_learning_rate: 2e-3 # adam + critic_learning_rate: 2e-4 # adam + lr_schedule: linear # ['constant', 'linear'] + target_critic_alpha: 0.2 + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: [0.7, 0.95] # adam + max_epochs: 2000 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: 256 + save_interval: 400 + + player: + determenistic: False + games_num: 1 + num_actors: 1 + print_stats: True diff --git a/scripts/cfg/alg/shac_hopper_warp.yaml b/scripts/cfg/alg/shac_hopper_warp.yaml new file mode 100644 index 00000000..2191601f --- /dev/null +++ b/scripts/cfg/alg/shac_hopper_warp.yaml @@ -0,0 +1,44 @@ +name: shac +params: + diff_env: + name: HopperWarpEnv + stochastic_env: True + episode_length: 500 + + network: + actor: ActorStochasticMLP + actor_mlp: + units: [128, 64, 32] + activation: elu + + critic: CriticMLP + critic_mlp: + units: [64, 64] + activation: elu + + config: + name: warp_hopper_shac + actor_learning_rate: 2e-3 # adam + critic_learning_rate: 2e-4 # adam + lr_schedule: linear # ['constant', 'linear'] + target_critic_alpha: 0.2 + obs_rms: True + ret_rms: False + critic_iterations: 16 + critic_method: td-lambda + lambda: 0.95 + num_batch: 4 + gamma: 0.99 + betas: [0.7, 0.95] # adam + max_epochs: 2000 + steps_num: 32 + grad_norm: 1.0 + truncate_grads: True + num_actors: 256 + save_interval: 400 + + player: + determenistic: False + games_num: 1 + num_actors: 1 + print_stats: True diff --git a/scripts/cfg/config.yaml b/scripts/cfg/config.yaml new file mode 100644 index 00000000..7845b7fe --- /dev/null +++ b/scripts/cfg/config.yaml @@ -0,0 +1,57 @@ +defaults: + - _self_ + - env: cartpole + - alg: shac + +exp_name: shac_benchmarks + +resume_model: null + +general: + play: False + logdir: logs/${alg.name}/${env.name}/ + save_interval: False + no_time_stamp: False + render: False + device: cuda:0 + run_wandb: False + seed: 42 + train: True + checkpoint: + multi_gpu: False + mixed_precision: False + num_envs: + +# env-specific defaults for different algs +env: + gamma: 0.99 + player: + games_num: 12 + num_actors: 4 + + shac: + lambda: 0.95 + actor_mlp: + units: + - 64 + - 64 + critic_mlp: + units: + - 64 + - 64 + target_critic_alpha: 0.4 + actor_lr: 1e-3 + critic_lr: 1e-3 + max_epochs: 2000 + save_interval: 400 + steps_num: 32 + betas: + - 0.7 + - 0.95 + + shac2: ${.shac} + +wandb: + project: shac + group: ${exp_name} + sweep_name_prefix: ${env.name}_${alg.name}-run diff --git a/scripts/cfg/env/ant.yaml b/scripts/cfg/env/ant.yaml new file mode 100644 index 00000000..546dd150 --- /dev/null +++ b/scripts/cfg/env/ant.yaml @@ -0,0 +1,22 @@ +name: df_ant +config: + _target_: shac.envs.AntEnv + render: ${general.render} + device: ${general.device} + num_envs: ${resolve_default:512,${general.num_envs}} + stochastic_init: True + seed: ${general.seed} + no_grad: ${general.play} + episode_length: 1000 + MM_caching_frequency: 16 + early_termination: True + +actor_mlp: + units: [128, 64, 64] + +shac: + actor_lr: 2e-3 + critic_lr: 2e-3 + max_epochs: 2000 + num_actors: 64 + target_critic_alpha: 0.2 diff --git a/scripts/cfg/env/cartpole.yaml b/scripts/cfg/env/cartpole.yaml new file mode 100644 index 00000000..ce34be09 --- /dev/null +++ b/scripts/cfg/env/cartpole.yaml @@ -0,0 +1,44 @@ +name: df_cartpole +env_name: CartPoleSwingUpEnv + +config: + _target_: shac.envs.CartPoleSwingUpEnv + render: ${general.render} + device: ${general.device} + num_envs: 1024 + seed: ${general.seed} + episode_length: 240 + no_grad: ${general.play} # ${resolve_default:general.play,False} + stochastic_init: True + MM_caching_frequency: 4 + early_termination: False + +shac: + actor_lr: 1e-3 + critic_lr: 1e-2 + max_epochs: 500 + save_interval: 100 + steps_num: 32 + betas: + - 0.7 + - 0.95 + actor_mlp: + units: + - 64 + - 64 + target_critic_alpha: 0.2 + +shac2: + critic_lr: 1e-3 + +ppo: + max_epochs: 500 + minibatch_size: 1920 + save_interval: 100 + save_best_after: 50 + num_actors: 32 + steps_num: 240 + +player: + games_num: 12 + num_actors: 4 diff --git a/scripts/cfg/env/cartpole_warp.yaml b/scripts/cfg/env/cartpole_warp.yaml new file mode 100644 index 00000000..22bbf6f6 --- /dev/null +++ b/scripts/cfg/env/cartpole_warp.yaml @@ -0,0 +1,35 @@ +name: warp_cartpole +config: + _target_: shac.envs.CartPoleSwingUpWarpEnv + render: ${general.render} + device: ${general.device} + num_envs: 1024 + seed: ${general.seed} + episode_length: 240 + no_grad: ${general.play} + stochastic_init: False + early_termination: False + ag_return_body: True + +shac: + actor_lr: 1e-3 + critic_lr: 1e-2 + max_epochs: 500 + betas: + - 0.7 + - 0.95 + +shac2: + critic_lr: 1e-3 + +ppo: + max_epochs: 500 + minibatch_size: 1920 + save_interval: 100 + save_best_after: 50 + num_actors: 32 + steps_num: 240 + +player: + games_num: 12 + num_actors: 4 diff --git a/scripts/cfg/env/cheetah.yaml b/scripts/cfg/env/cheetah.yaml new file mode 100644 index 00000000..11bfe3c3 --- /dev/null +++ b/scripts/cfg/env/cheetah.yaml @@ -0,0 +1,33 @@ +name: df_cheetah + +config: + _target_: shac.envs.CheetahEnv + render: ${general.render} + device: ${general.device} + num_envs: ${resolve_default:1024,${general.num_envs}} + seed: ${general.seed} + episode_length: 1000 + no_grad: False + stochastic_init: True + MM_caching_frequency: 16 + early_termination: True + +shac: + actor_lr: 2e-3 + critic_lr: 2e-3 + max_epochs: 2000 + betas: [0.7, 0.95] + actor_mlp: + units: [128, 64, 32] + +ppo: + max_epochs: 500 + minibatch_size: 1920 + save_interval: 100 + save_best_after: 50 + num_actors: 32 + steps_num: 240 + +player: + games_num: 12 + num_actors: 4 diff --git a/scripts/cfg/env/claw.yaml b/scripts/cfg/env/claw.yaml new file mode 100644 index 00000000..c92558e1 --- /dev/null +++ b/scripts/cfg/env/claw.yaml @@ -0,0 +1,26 @@ +params: + diff_env: + name: ClawWarpEnv + stochastic_env: True + episode_length: 250 + goal_type: 1 # 0: position, 1: orientation, 2: both, 3: position trajectory, 4: orientation trajectory + object_type: 8 # 0-3 are meshes, 4 - 8 are primitives + action_type: 0 # 0: position, 1: torque + rew_kw: + c_act: 0. + c_q: 10. + c_finger: 0.2 + + + network: + actor: ActorStochasticMLP # ActorDeterministicMLP + actor_mlp: + units: [128, 64, 32] + activation: elu + + critic: CriticMLP + critic_mlp: + units: [64, 64] + activation: elu + + diff --git a/scripts/cfg/env/double_pendulum.yaml b/scripts/cfg/env/double_pendulum.yaml new file mode 100644 index 00000000..8102afa7 --- /dev/null +++ b/scripts/cfg/env/double_pendulum.yaml @@ -0,0 +1,10 @@ +_target_: shac.envs.DoublePendulumEnv +render: ${general.render} +device: ${general.device} +num_envs: 1024 +seed: ${general.seed} +episode_length: 200 +no_grad: False +stochastic_init: False +MM_caching_frequency: 4 +early_termination: False diff --git a/scripts/cfg/env/hopper.yaml b/scripts/cfg/env/hopper.yaml new file mode 100644 index 00000000..f5d1ea27 --- /dev/null +++ b/scripts/cfg/env/hopper.yaml @@ -0,0 +1,38 @@ +name: df_hopper +env_name: HopperEnv + +config: + _target_: shac.envs.HopperEnv + render: ${general.render} + device: ${general.device} + num_envs: 512 # ${resolve_default:512,${general.num_envs}} + seed: ${general.seed} + episode_length: 1000 + no_grad: False + stochastic_init: True + MM_caching_frequency: 4 + early_termination: True + +shac: + actor_lr: 1e-3 + critic_lr: 1e-2 + max_epochs: 2000 + betas: + - 0.7 + - 0.95 + actor_mlp: + units: + - 128 + - 128 + +ppo: + max_epochs: 500 + minibatch_size: 1920 + save_interval: 100 + save_best_after: 50 + num_actors: 32 + steps_num: 240 + +player: + games_num: 12 + num_actors: 4 diff --git a/scripts/cfg/env/hopper_warp.yaml b/scripts/cfg/env/hopper_warp.yaml new file mode 100644 index 00000000..86dafa94 --- /dev/null +++ b/scripts/cfg/env/hopper_warp.yaml @@ -0,0 +1,11 @@ +_target_: shac.envs.HopperWarpEnv +render: ${general.render} +device: ${general.device} +num_envs: 1024 +seed: ${general.seed} +episode_length: 200 +no_grad: False +stochastic_init: False +early_termination: False +ag_return_body: True +MM_caching_frequency: 4 diff --git a/scripts/cfg/env/humanoid.yaml b/scripts/cfg/env/humanoid.yaml new file mode 100644 index 00000000..0d1fbfbd --- /dev/null +++ b/scripts/cfg/env/humanoid.yaml @@ -0,0 +1,20 @@ +name: df_humanoid +config: + diff_env: + name: shac.envs.HumanoidEnv + render: ${general.render} + device: ${general.device} + num_envs: 64 + stochastic_env: True + seed: ${general.seed} + episode_length: 1000 + MM_caching_frequency: 48 + +actor_mlp: + units: [256, 128] +critic_mlp: + units: [256, 128] + +actor_lr: 2e-3 +critic_lr: 5e-4 +target_critic_alpha: 0.995 diff --git a/scripts/grad_collect.py b/scripts/grad_collect.py new file mode 100644 index 00000000..ae508375 --- /dev/null +++ b/scripts/grad_collect.py @@ -0,0 +1,91 @@ +import hydra +from hydra.utils import instantiate +from omegaconf import OmegaConf, DictConfig +from shac import envs +import numpy as np +import torch +from tqdm import tqdm +from torchviz import make_dot + + +@hydra.main(version_base="1.2", config_path="cfg", config_name="config.yaml") +def main(config: DictConfig): + device = torch.device(config.general.device) + torch.random.manual_seed(config.general.seed) + + # create environment + env = instantiate(config.env.config) + + n = env.num_obs + m = env.num_acts + N = env.num_envs + H = env.episode_length + h_step = 1 + + # create a random set of actions + std = 0.5 + w = torch.normal(0.0, std, (N, m)).to(device) + w[0] = w[0].zero_() + fobgs = [] + zobgs = [] + losses = [] + baseline = [] + + hh = np.arange(1, config.env.config.episode_length + 1, h_step) + for h in tqdm(hh): + env.clear_grad() + env.reset() + + ww = w.clone() + ww.requires_grad_(True) + loss = torch.zeros(config.env.config.num_envs).to(device) + + # apply first noisy action + obs, rew, done, info = env.step(ww) + loss += rew + + # let episode play out + for t in range(1, h): + obs, rew, done, info = env.step(torch.zeros_like(ww)) + loss += rew + # NOTE: commented out code below is for the debugging of more efficient grad computation + # make_dot(loss.sum(), show_attrs=True, show_saved=True).render("correct_graph") + # loss.sum().backward() + # print(ww.grad) + # exit(1) + + loss.sum().backward() + losses.append(loss.detach().cpu().numpy()) + baseline.append(loss[0].detach().cpu().numpy()) + + # get First-order Batch Gradients (FoBGs) + fobgs.append(ww.grad.cpu().numpy()) + + # get Zero-order Batch Gradients (ZoBGs) + zobg = 1 / std**2 * (loss.unsqueeze(1) - loss[0]) * ww + zobgs.append(zobg.detach().cpu().numpy()) + + filename = "{:}_grads_{:}".format( + env.__class__.__name__, config.env.config.episode_length + ) + if "warp" in config.env.config._target_: + filename = "Warp" + filename + filename = f"outputs/grads/{filename}" + if hasattr(env, "start_state"): + filename += "_" + str(env.start_state) + print("Saving to", filename) + np.savez( + filename, + h=hh, + zobgs=zobgs, + fobgs=fobgs, + losses=losses, + baseline=baseline, + std=std, + n=n, + m=m, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/grad_collect_iter.py b/scripts/grad_collect_iter.py new file mode 100644 index 00000000..b808e934 --- /dev/null +++ b/scripts/grad_collect_iter.py @@ -0,0 +1,86 @@ +import hydra +from hydra.utils import instantiate +from omegaconf import OmegaConf, DictConfig +from shac import envs +import numpy as np +import torch +from tqdm import tqdm +from torchviz import make_dot + + +@hydra.main(version_base="1.2", config_path="cfg", config_name="config.yaml") +def main(config: DictConfig): + device = torch.device(config.general.device) + + torch.random.manual_seed(config.general.seed) + + env = instantiate(config.env.config) + + n = env.num_obs + m = env.num_acts + N = env.num_envs + H = env.episode_length + + # create a random set of actions + std = 0.5 + w = torch.normal(0.0, std, (N, m)).to(device) + w[0] = w[0].zero_() + fobgs = [] + losses = [] + baseline = [] + zobgs = [] + + h = 200 + env.clear_grad() + env.reset() + + ww = w.clone() + ww.requires_grad_(True) + loss = torch.zeros(config.env.config.num_envs).to(device) + + # apply first noisy action + obs, rew, done, info = env.step(ww) + rew.sum().backward(retain_graph=True) + loss += rew.detach() + + # let episode play out + for t in tqdm(range(1, h)): + obs, rew, done, info = env.step(torch.zeros_like(ww)) + rew.sum().backward(retain_graph=True) + loss += rew.detach() + # ww.grad.zero_() # do to make gradients correct + # make_dot(loss.sum(), show_attrs=True, show_saved=True).render("bad_graph") + losses.append(loss.cpu().numpy()) + baseline.append(loss[0].cpu().numpy()) + # print(ww.grad) + # exit(1) + + fobgs.append(ww.grad.cpu().numpy()) + + # now get ZoBGs + zobg = 1 / std**2 * (loss.unsqueeze(1) - loss[0]) * ww + zobgs.append(zobg.detach().cpu().numpy()) + + filename = "{:}_grads2_{:}".format( + env.__class__.__name__, config.env.config.episode_length + ) + if "warp" in config.env.config._target_: + filename = "Warp" + filename + filename = f"outputs/grads/{filename}" + if hasattr(env, "start_state"): + filename += "_" + str(env.start_state) + print("Saving to", filename) + np.savez( + filename, + zobgs=zobgs, + fobgs=fobgs, + losses=losses, + baseline=baseline, + std=std, + n=n, + m=m, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/grad_collect_multistep.py b/scripts/grad_collect_multistep.py new file mode 100644 index 00000000..4bc10da3 --- /dev/null +++ b/scripts/grad_collect_multistep.py @@ -0,0 +1,133 @@ +from typing import Union +import hydra +from hydra.utils import instantiate +from omegaconf import OmegaConf, DictConfig +from shac import envs +import numpy as np +import torch +from tqdm import tqdm +from torchviz import make_dot +from shac.envs import DFlexEnv +from warp.envs import WarpEnv + + +@hydra.main(version_base="1.2", config_path="cfg", config_name="config.yaml") +def main(config: DictConfig): + device = torch.device(config.general.device) + torch.random.manual_seed(config.general.seed) + + # create environment + env: Union[DFlexEnv, WarpEnv] = instantiate(config.env.config) + + n = env.num_obs + m = env.num_acts + N = env.num_envs + H = env.episode_length + + # Create actions + # TODO theta should be only 1 parameter but I don't know how to get the grads + # with respect to different rollouts + o = m # parameter space TODO hardcoded + th = torch.ones((N, o)).to(device) + th.requires_grad_(True) + + # cartpole + def policy(obs): + # returns (N x m) + # observation should be of shape (n_envs, n_obses) + a = -th * obs[..., [1]] + assert a.shape[-2:] == (N, m), a.shape + return a + + # hopper + def policy(obs): + # returns (N x m) + # observation should be of shape (n_envs, n_obses) + a = -th * obs[..., [5, 5, 6]] + assert a.shape[-2:] == (N, m), a.shape + return a + + # create a random set of actions + std = 0.5 + w = torch.normal(0.0, std, (H, N, m)).to(device) + w[:, 0] = w[:, 0].zero_() + fobgs = [] + zobgs = [] + zobgs_no_grad = [] + losses = [] + baseline = [] + + for h in tqdm(range(1, H)): + env.clear_grad() + obs = env.reset() + dpis = [] + loss = torch.zeros(N).to(device) + + # let episode play out + for t in range(0, h): + # compute policy gradients along the way for FoBGs later + (dpi,) = torch.autograd.grad(policy(obs.detach()).sum(), th) + dpis.append(dpi) + action = policy(obs) + w[t] + obs, rew, term, trunc, info = env.step(action) + loss += rew + # NOTE: commented out code below is for the debugging of more efficient grad computation + # make_dot(loss.sum(), show_attrs=True, show_saved=True).render("correct_graph") + # loss.sum().backward() + # print(ww.grad) + # exit(1) + + # get losses + loss.sum().backward() + losses.append(loss.detach().cpu().numpy()) + baseline.append(loss[0].detach().cpu().numpy()) + + # get First-order Batch Gradients (FoBGs) + fobg = th.grad.cpu().numpy() + assert fobg.shape == (N, o), fobg.shape + fobgs.append(fobg) + + # get Zero-order Batch Gradients (ZoBGs) + dpis = torch.stack(dpis) + assert dpis.shape == (h, N, o), dpis.shape + policy_grad = dpis * w[:h] + assert policy_grad.shape == (h, N, o), policy_grad.shape + policy_grad = policy_grad.sum(0) + assert policy_grad.shape == (N, o), policy_grad.shape + value = loss.unsqueeze(1) - loss[0] + assert value.shape == (N, 1), value.shape + zobg = 1 / std**2 * value * policy_grad + assert zobg.shape == (N, o), zobg.shape + zobgs.append(zobg.detach().cpu().numpy()) + + # Now get ZoBGs without poliy gradients + policy_grad = w[:h].sum(0) # without policy gradients + assert policy_grad.shape == (N, o), policy_grad.shape + zobg_no_grad = 1 / std**2 * value * policy_grad + zobgs_no_grad.append(zobg_no_grad.detach().cpu().numpy()) + + # Save data + filename = "{:}_grads_ms_{:}".format( + env.__class__.__name__, config.env.config.episode_length + ) + if "warp" in config.env.config._target_: + filename = "Warp" + filename + filename = f"outputs/grads/{filename}" + if hasattr(env, "start_state"): + filename += "_" + str(env.start_state) + print("Saving to", filename) + np.savez( + filename, + zobgs=zobgs, + fobgs=fobgs, + losses=losses, + baseline=baseline, + zobgs_no_grad=zobgs_no_grad, + std=std, + n=n, + m=m, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/grad_collect_multistep_single_theta.py b/scripts/grad_collect_multistep_single_theta.py new file mode 100644 index 00000000..043af83d --- /dev/null +++ b/scripts/grad_collect_multistep_single_theta.py @@ -0,0 +1,116 @@ +import hydra +from typing import Union +from hydra.utils import instantiate +from omegaconf import OmegaConf, DictConfig +from shac import envs +import numpy as np +import torch +from tqdm import tqdm +from torchviz import make_dot +from shac.envs import DFlexEnv +from warp.envs import WarpEnv + + +@hydra.main(version_base="1.2", config_path="cfg", config_name="config.yaml") +def main(config: DictConfig): + device = torch.device(config.general.device) + torch.random.manual_seed(config.general.seed) + + # create environment + env: Union[DFlexEnv, WarpEnv] = instantiate(config.env.config) + + n = env.num_obs + m = env.num_acts + N = env.num_envs + H = env.episode_length + + # Create actions + # TODO theta should be only 1 parameter but I don't know how to get the grads + # with respect to different rollouts + th = torch.tensor([1.0]).to(device) + th.requires_grad_(True) + o = th.shape # parameter space + + def policy(obs): + # returns (N x m) + # observation should be of shape (n_envs, n_obses) + a = -th * obs[:, 1].view((-1, 1)) + assert a.shape == (N, m) + return a + + # create a random set of actions + std = 0.5 + w = torch.normal(0.0, std, (H, N, m)).to(device) + w[:, 0] = w[:, 0].zero_() + fobgs = [] + zobgs = [] + zobgs_no_grad = [] + + for h in tqdm(range(1, H)): + env.clear_grad() + obs = env.reset() + loss = torch.zeros(N).to(device) + + # let episode play out + for t in range(0, h): + obs, rew, done, info = env.step(policy(obs) + w[t]) + loss += rew + # NOTE: commented out code below is for the debugging of more efficient grad computation + # make_dot(loss.sum(), show_attrs=True, show_saved=True).render("correct_graph") + # loss.sum().backward() + # print(ww.grad) + # exit(1) + + # get FoBGs per environment + # This here is a more efficient attempt at computing batch gradients which still doesn't work + # (grads,) = torch.autograd.grad( + # loss.sum(), (th,), (torch.ones_like(loss),), is_grads_batched=True + # ) + + grads = [] + for i in range(N): + (grad,) = torch.autograd.grad(loss[i], (th,), retain_graph=True) + grads.append(grad) + grads = torch.stack(grads) + print(grads.shape) + print(grads) + exit(1) + fobg = [] + for i in range(len(loss)): + (grad,) = torch.autograd.grad(loss[i], th, retain_graph=True) + fobg.append(grad.cpu().numpy()) + + fobg = np.stack(fobg) + assert fobg.shape == (N, 1), fobg.shape + fobgs.append(fobg) + + # now get ZoBGs + policy_grad = -obs_hist[:, :, [1]] * w[:h] + assert policy_grad.shape == (h, N, m), policy_grad.shape + policy_grad = policy_grad.sum(0) + assert policy_grad.shape == (N, m), policy_grad.shape + baseline = loss[0] + value = loss.unsqueeze(1) - baseline + assert value.shape == (N, 1), value.shape + zobg = 1 / std**2 * value * policy_grad + assert zobg.shape == (N, 1), zobg.shape + zobgs.append(zobg.detach().cpu().numpy()) + + # Now get ZoBGs without poliy gradients + policy_grad = w[:h].sum(0) # without policy gradients + assert policy_grad.shape == (N, m), policy_grad.shape + zobg_no_grad = 1 / std**2 * value * policy_grad + zobgs_no_grad.append(zobg_no_grad.detach().cpu().numpy()) + + np.savez( + "{:}_grads_ms_{:}".format( + env.__class__.__name__, config.env.config.episode_length + ), + zobgs=zobgs, + zobgs_no_grad=zobgs_no_grad, + fobgs=fobgs, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/grad_collect_shac.py b/scripts/grad_collect_shac.py new file mode 100644 index 00000000..28439329 --- /dev/null +++ b/scripts/grad_collect_shac.py @@ -0,0 +1,166 @@ +import hydra +from hydra.utils import instantiate +from omegaconf import OmegaConf, DictConfig +from shac import envs +import numpy as np +import torch +from tqdm import tqdm +from torchviz import make_dot +from shac.envs import DFlexEnv +from shac.algorithms.shac import SHAC +from copy import deepcopy +from time import time +from functorch import combine_state_for_ensamble, vmap + +# from torch.nn.utils import param +from torch.nn.utils import parameters_to_vector + + +@hydra.main(version_base="1.2", config_path="cfg", config_name="config.yaml") +def main(config: DictConfig): + device = torch.device(config.general.device) + torch.random.manual_seed(config.general.seed) + + # create environment + env: DFlexEnv = instantiate(config.env) + + n = env.num_obs + m = env.num_acts + N = env.num_envs + H = env.episode_length + o = 4865 + + # Load policy + shac_path = "/home/ignat/git/SHAC/scripts/outputs/2023-04-08/18-59-28/logs/tmp/shac/04-08-2023-18-59-32/best_policy.pt" + print("Loading policies") + policies = [] + shac = SHAC(OmegaConf.to_container(config.alg, resolve=True), config.env) + shac.load(shac_path) + shac.actor.eval() + for _ in range(N): + new_actor = deepcopy(shac.actor) + new_actor.eval() + policies.append(new_actor) + fmodel, params, buffers = combine_state_for_ensamble(policies) + [p.requires_grad_() for p in params]; + print("Loaded policies") + + def policy(obs: torch.Tensor): + # obs should be (NxO) + # pre-process observations + obs = torch.stack([obs for i in range(N)]) + actions = vmap(fmodel)(params, buffers, obs) + # act should be [N, N, m] + actions = torch.stack([actions[i, i] for i in range(N)]) + + # post process observations + # actions = [] + # for i in range(obs.shape[0]): + # a = policies[i](obs[i], deterministic=True) + # actions.append(a) + # actions = torch.stack(actions) + assert actions.shape == (N, m), actions.shape + return actions + + parameters = [list(actor.parameters())[1:] for actor in policies] + parameters = [] + for actor in policies: + parameters.extend(list(actor.parameters())[1:]) + + # create a random set of actions + std = 0.5 + w = torch.normal(0.0, std, (H, N, m)).to(device) + w[:, 0] = w[:, 0].zero_() + fobgs = [] + zobgs = [] + # zobgs_no_grad = [] + + for h in tqdm(range(1, H)): + env.clear_grad() + obs = env.reset() + dpis = [] + loss = torch.zeros(N).to(device) + + # let episode play out + for t in range(0, h): + + start = time() + # Accumulate ZoBGs along the way + # compute policy gradients along the way for FoBGs later + grads = torch.autograd.grad(policy(obs.detach()).sum(), parameters) + duration = time() - start + print("ZoBG computation took {:.3f}s".format(duration)) + # reshape parameters into the shapes we want them in + dpi = [] + for i in range(N): + dpi_per_batch = grads[i*10: (i+1)*10] + dpi_per_batch = [each.flatten() for each in dpi_per_batch] + dpi_per_batch = torch.concat(dpi_per_batch) + dpi.append(dpi_per_batch) + dpi = torch.stack(dpi) + assert dpi.shape == (N, o), dpi.shape + dpis.append(dpi) + duration = time() - start + print("ZoBG accumulation took {:.3f}s".format(duration)) + + # Rollout environment + action = policy(obs) + w[t] + obs, rew, done, info = env.step(action) + loss += rew + # NOTE: commented out code below is for the debugging of more efficient grad computation + # make_dot(loss.sum(), show_attrs=True, show_saved=True).render("correct_graph") + # loss.sum().backward() + # print(ww.grad) + # exit(1) + + # get first order gradients per environment + start = time() + loss.sum().backward() + grads = [] + for actor in policies: + grad_batch = [] + for name, param in actor.named_parameters(): + # print(name, param.shape) + if name not in "logstd": + grad_batch.append(param.grad.flatten()) + grad_batch = torch.concat(grad_batch) + grads.append(grad_batch) + fobg = torch.stack(grads) + assert fobg.shape == (N, o), dpis.shpae + fobgs.append(fobg) + duration = time() - start + print("FoBG took {:.3f}s".format(duration)) + + # now get ZoBGs + start = time() + dpis = torch.stack(dpis) + assert dpis.shape == (h, N, o), dpis.shape + policy_grad = dpis * w[:h] + assert policy_grad.shape == (h, N, o), policy_grad.shape + policy_grad = policy_grad.sum(0) + assert policy_grad.shape == (N, o), policy_grad.shape + baseline = loss[0] + value = loss.unsqueeze(1) - baseline + assert value.shape == (N, 1), value.shape + zobg = 1 / std**2 * value * policy_grad + assert zobg.shape == (N, o), zobg.shape + zobgs.append(zobg.detach().cpu().numpy()) + duration = time() - start + print("ZoBG took {:.3f}s",format(duration)) + + # Now get ZoBGs without poliy gradients + # policy_grad = w[:h].sum(0) # without policy gradients + # assert policy_grad.shape == (N, o), policy_grad.shape + # zobg_no_grad = 1 / std**2 * value * policy_grad + # zobgs_no_grad.append(zobg_no_grad.detach().cpu().numpy()) + + np.savez( + "{:}_grads_ms_{:}".format(env.__class__.__name__, config.env.episode_length), + zobgs=zobgs, + # zobgs_no_grad=zobgs_no_grad, + fobgs=fobgs, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/hopper.ipynb b/scripts/hopper.ipynb new file mode 100644 index 00000000..dbe99641 --- /dev/null +++ b/scripts/hopper.ipynb @@ -0,0 +1,3199 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 8, + "id": "65c15b4a", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.patches as patches\n", + "from numpy.linalg import norm\n" + ] + }, + { + "cell_type": "markdown", + "id": "5a5c0b89", + "metadata": {}, + "source": [ + "Hopepr:\n", + "* semi-trained policy `/home/ignat/git/SHAC/scripts/outputs/2023-05-08/18-22-41/logs/shac/df_hopper/jacobians.npz`\n", + "* optimal policy `/home/ignat/git/SHAC/scripts/outputs/2023-05-08/22-32-09/logs/shac/df_hopper/best_policy.pt`\n", + "* jacobians `/home/ignat/git/SHAC/scripts/outputs/2023-05-08/18-22-41/logs/shac/df_hopper/jacobians.npz`\n", + "\n", + "Cheetah:\n", + "* optimal policy `/home/ignat/git/SHAC/scripts/outputs/2023-05-10/12-46-54/logs/shac/df_cheetah/best_policy.pt`\n", + "* jacobians `/home/ignat/git/SHAC/scripts/outputs/2023-05-13/11-13-51/logs/shac/df_cheetah/jacobians.npz`\n", + "\n", + "Ant:\n", + "* optimal policy `/home/ignat/git/SHAC/scripts/outputs/2023-05-10/12-33-29/logs/shac/df_ant/best_policy.pt`\n", + "* jacobians `/home/ignat/git/SHAC/scripts/outputs/2023-05-13/16-38-09/logs/shac/df_ant/jacobians.npz`" + ] + }, + { + "cell_type": "markdown", + "id": "a572ca37", + "metadata": {}, + "source": [ + "# Hopper" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "20500f07", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "median 52.53721 std 178.38405\n", + "total truncations 1\n", + "total early 0\n", + "total ep ends 1\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "ax = plt.gca()\n", + "\n", + "# hopper jacobians for many OK trained policies\n", + "hopper = np.load(\"/home/ignat/git/SHAC/scripts/outputs/2023-05-08/18-22-41/logs/shac/df_hopper/jacobians.npz\", allow_pickle=True)\n", + "\n", + "# converged policy for H=32\n", + "hopper = np.load(\"/home/ignat/git/SHAC/scripts/outputs/2023-05-10/12-27-03/logs/shac/df_hopper/jacobians.npz\", allow_pickle=True)\n", + "jacs = hopper['jacobians']\n", + "contact_changes = hopper['contact_changes']\n", + "trunc = hopper['truncations']\n", + "early_stops = hopper['early_stops']\n", + "ends = hopper['episode_ends']\n", + "\n", + "norms = norm(jacs, axis=(2,3))\n", + "\n", + "for i in range(norms.shape[1]):\n", + " ax.plot(norms[: ,i])\n", + " \n", + "ax.plot(np.median(norms).repeat(len(norms)), linewidth=3, c='r')\n", + "print(\"median {:.5f} std {:.5f}\".format(np.median(norms), np.std(norms)))\n", + "\n", + "mi, ma = np.min(norms), np.max(norms)\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=trunc.flatten(), color='g', alpha=0.2, label=\"trunc\")\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=early_stops.flatten(), color='r', alpha=0.2, label=\"early\")\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=ends.flatten(), color='purple', alpha=0.2, label=\"ends\")\n", + "short_horizons = [False]*31 + [True]\n", + "short_horizons*=len(norms)//32\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=short_horizons, color='blue', alpha=0.2, label=\"short horizons\")\n", + "\n", + "\n", + "print(\"total truncations\", trunc.sum())\n", + "print(\"total early\", early_stops.sum())\n", + "print(\"total ep ends\", ends.sum())" + ] + }, + { + "cell_type": "markdown", + "id": "23abcceb", + "metadata": {}, + "source": [ + "# Cheetah" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8699c48f", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "median 80.77976 std 109.61178\n", + "total truncations 1\n", + "total early 0\n", + "total ep ends 1\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "ax = plt.gca()\n", + "\n", + "# hopper jacobians for many OK trained policies\n", + "hopper = np.load(\"/home/ignat/git/SHAC/scripts/outputs/2023-05-13/11-13-51/logs/shac/df_cheetah/jacobians.npz\", allow_pickle=True)\n", + "jacs = hopper['jacobians']\n", + "contact_changes = hopper['contact_changes']\n", + "trunc = hopper['truncations']\n", + "early_stops = hopper['early_stops']\n", + "ends = hopper['episode_ends']\n", + "\n", + "norms = norm(jacs, axis=(2,3))\n", + "\n", + "# for i in range(norms.shape[1]):\n", + "# ax.plot(norms[0 ,i])\n", + "i = 0\n", + "ax.plot(norms[:, i])\n", + " \n", + "ax.plot(np.median(norms).repeat(len(norms)), linewidth=3, c='r')\n", + "print(\"median {:.5f} std {:.5f}\".format(np.median(norms), np.std(norms)))\n", + "\n", + "mi, ma = np.min(norms), np.max(norms)\n", + "# ax.fill_between(np.arange(len(norms)), mi, ma, where=trunc[:, i], color='g', alpha=0.2, label=\"trunc\")\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=early_stops[:, i], color='r', alpha=0.2, label=\"early\")\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=ends[:, i], color='purple', alpha=0.2, label=\"ends\")\n", + "short_horizons = [False]*31 + [True]\n", + "short_horizons*=len(norms)//32\n", + "ax.fill_between(np.arange(len(norms)), mi, ma, where=short_horizons, color='blue', alpha=0.2, label=\"short horizons\")\n", + "\n", + "\n", + "print(\"total truncations\", trunc.sum())\n", + "print(\"total early\", early_stops.sum())\n", + "print(\"total ep ends\", ends.sum())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04ed41a4", + "metadata": {}, + "outputs": [], + "source": [ + "# Ant" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "be7459ba", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "total truncations 1\n", + "total early 0\n", + "total ep ends 1\n" + ] + } + ], + "source": [ + "%matplotlib notebook\n", + "\n", + "ax = plt.gca()\n", + "\n", + "# hopper jacobians for many OK trained policies\n", + "hopper = np.load(\"/home/ignat/git/SHAC/scripts/outputs/2023-05-13/17-31-52/logs/shac/df_ant/jacobians.npz\", allow_pickle=True)\n", + "jacs = hopper['jacobians']\n", + "contact_changes = hopper['contact_changes']\n", + "trunc = hopper['truncations']\n", + "early_stops = hopper['early_stops']\n", + "ends = hopper['episode_ends']\n", + "\n", + "norms = norm(jacs, axis=(2,3))\n", + "\n", + "# for i in range(norms.shape[1]):\n", + "# ax.plot(norms[0 ,i])\n", + "i = 0\n", + "ax.plot(norms[:, i])\n", + "\n", + "ax.set_ylim((-1, 10000))\n", + " \n", + "# ax.plot(np.median(norms).repeat(len(norms)), linewidth=3, c='r')\n", + "# print(\"median {:.5f} std {:.5f}\".format(np.median(norms), np.std(norms)))\n", + "\n", + "# mi, ma = np.min(norms), np.max(norms)\n", + "# ax.fill_between(np.arange(len(norms)), mi, ma, where=trunc[:, i], color='g', alpha=0.2, label=\"trunc\")\n", + "# ax.fill_between(np.arange(len(norms)), mi, ma, where=early_stops[:, i], color='r', alpha=0.2, label=\"early\")\n", + "# ax.fill_between(np.arange(len(norms)), mi, ma, where=ends[:, i], color='purple', alpha=0.2, label=\"ends\")\n", + "# short_horizons = [False]*31 + [True]\n", + "# short_horizons*=len(norms)//32\n", + "# ax.fill_between(np.arange(len(norms)), mi, ma, where=short_horizons, color='blue', alpha=0.2, label=\"short horizons\")\n", + "\n", + "\n", + "print(\"total truncations\", trunc.sum())\n", + "print(\"total early\", early_stops.sum())\n", + "print(\"total ep ends\", ends.sum())" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "452c6228", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.max(norms)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/mpc_cartpole.py b/scripts/mpc_cartpole.py new file mode 100644 index 00000000..3735116d --- /dev/null +++ b/scripts/mpc_cartpole.py @@ -0,0 +1,25 @@ +# coding: utf-8 +from shac.algorithms.mpc import Policy, Planner +from warp.envs.cartpole_swing_up import CartPoleSwingUpEnv +import numpy as np +from tqdm import trange + +EP_LEN = 240 +env = CartPoleSwingUpEnv(num_envs=512, episode_length=EP_LEN) +eval_env = CartPoleSwingUpEnv( + num_envs=1, + episode_length=EP_LEN, + render=True, + stage_path="eval_mpc2", +) +p = Planner(Policy(env.num_acts, horizon=0.25), env) +rewards = [] +for _ in trange(EP_LEN): + p.optimize_policy() + obs, rew, done, info = p.step(eval_env) + rewards.append(rew.detach().cpu().numpy()) + +import matplotlib.pyplot as plt + +plt.plot(rewards) +plt.savefig("rewards.png") diff --git a/scripts/run_mpc.py b/scripts/run_mpc.py new file mode 100644 index 00000000..2cba6a62 --- /dev/null +++ b/scripts/run_mpc.py @@ -0,0 +1,37 @@ +import hydra +from tqdm import trange +import shac.algorithms.mpc +import matplotlib.pyplot as plt +import numpy as np +from shac.utils import custom_resolvers +from hydra.utils import instantiate +from omegaconf import DictConfig, OmegaConf + + +@hydra.main(config_path="cfg", config_name="config") +def main(cfg: DictConfig) -> None: + print(OmegaConf.to_yaml(cfg)) + env = instantiate(cfg.env.config) + eval_env = instantiate(cfg.env.config, num_envs=1) + policy = instantiate(cfg.alg.config.policy, num_actions=env.num_acts) + planner = instantiate(cfg.alg.config.planner, env=env, policy=policy) + rewards = run_planner(planner, eval_env) + + plt.plot(rewards) + plt.savefig("rewards.png") + + +def run_planner(planner, eval_env): + planner.reset() + eval_env.reset() + rewards = [] + for _ in trange(eval_env.episode_length): + planner.optimize_policy() + obs, rew, done, info = planner.step(eval_env) + rewards.append(rew.detach().cpu().numpy()) + eval_env.render() # ignored if render flag not passed + return rewards + + +if __name__ == "__main__": + main() diff --git a/scripts/train.py b/scripts/train.py new file mode 100644 index 00000000..642746ee --- /dev/null +++ b/scripts/train.py @@ -0,0 +1,116 @@ +import traceback +import hydra, os, wandb, yaml +from omegaconf import DictConfig, OmegaConf +from hydra.core.hydra_config import HydraConfig +from shac.utils import hydra_utils +from shac.algorithms.shac import SHAC +from shac.algorithms.shac2 import SHAC as SHAC2 +from shac.algorithms.ahac import AHAC +from shac.utils.common import * + + +def create_wandb_run(wandb_cfg, job_config, run_id=None, run_wandb=False): + try: + job_id = HydraConfig().get().job.num + override_dirname = HydraConfig().get().job.override_dirname + name = f"{wandb_cfg.sweep_name_prefix}-{job_id}" + notes = f"{override_dirname}" + except: + name, notes = None, None + if run_wandb: + return wandb.init( + project=wandb_cfg.project, + config=job_config, + group=wandb_cfg.group, + sync_tensorboard=True, + monitor_gym=True, + save_code=True, + name=name, + notes=notes, + id=run_id, + resume=run_id is not None, + ) + + +cfg_path = os.path.dirname(__file__) +cfg_path = os.path.join(cfg_path, "cfg") + + +@hydra.main(config_path="cfg", config_name="config.yaml") +def train(cfg: DictConfig): + try: + cfg_full = OmegaConf.to_container(cfg, resolve=True) + cfg_yaml = yaml.dump(cfg_full["alg"]) + + resume_model = cfg.resume_model + if os.path.exists("exp_config.yaml"): + old_config = yaml.load(open("exp_config.yaml", "r")) + params, wandb_id = old_config["params"], old_config["wandb_id"] + run = create_wandb_run( + cfg.wandb, params, wandb_id, run_wandb=cfg.general.run_wandb + ) + resume_model = "restore_checkpoint.zip" + assert os.path.exists( + resume_model + ), "restore_checkpoint.zip does not exist!" + else: + defaults = HydraConfig.get().runtime.choices + + params = yaml.safe_load(cfg_yaml) + params["defaults"] = {k: defaults[k] for k in ["alg"]} + + run = create_wandb_run(cfg.wandb, params, run_wandb=cfg.general.run_wandb) + # wandb_id = run.id if run != None else None + save_dict = dict(wandb_id=run.id if run != None else None, params=params) + yaml.dump(save_dict, open("exp_config.yaml", "w")) + print("Config:") + print(cfg_yaml) + + if cfg.alg.name == "shac": + cfg_train = cfg_full["alg"] + if cfg.general.play: + cfg_train["params"]["config"]["num_actors"] = ( + cfg_train["params"]["config"].get("player", {}).get("num_actors", 1) + ) + if not cfg.general.no_time_stamp: + cfg.general.logdir = os.path.join(cfg.general.logdir, get_time_stamp()) + + cfg_train["params"]["general"] = cfg_full["general"] + cfg_train["params"]["diff_env"] = cfg_full["env"]["config"] + env_name = cfg_train["params"]["diff_env"].pop("_target_") + cfg_train["params"]["diff_env"]["name"] = env_name.split(".")[-1] + print(cfg_train["params"]["general"]) + traj_optimizer = SHAC(cfg_train) + elif cfg.alg.name == "shac2": + traj_optimizer = SHAC2(cfg) + elif cfg.alg.name == "ahac": + cfg_train = cfg_full["alg"] + if cfg.general.play: + cfg_train["params"]["config"]["num_actors"] = ( + cfg_train["params"]["config"].get("player", {}).get("num_actors", 1) + ) + if not cfg.general.no_time_stamp: + cfg.general.logdir = os.path.join(cfg.general.logdir, get_time_stamp()) + + cfg_train["params"]["general"] = cfg_full["general"] + cfg_train["params"]["diff_env"] = cfg_full["env"]["config"] + env_name = cfg_train["params"]["diff_env"].pop("_target_") + cfg_train["params"]["diff_env"]["name"] = env_name.split(".")[-1] + print(cfg_train["params"]["general"]) + traj_optimizer = AHAC(cfg_train) + + if not cfg.general.play: + if cfg_train["params"]["general"]["checkpoint"]: + traj_optimizer.load(cfg_train["params"]["general"]["checkpoint"]) + traj_optimizer.train() + else: + traj_optimizer.play(cfg_train) + wandb.finish() + except: + traceback.print_exc(file=open("exception.log", "w")) + with open("exception.log", "r") as f: + print(f.read()) + + +if __name__ == "__main__": + train() diff --git a/setup.py b/setup.py index ff03f03c..8fa13333 100644 --- a/setup.py +++ b/setup.py @@ -12,8 +12,7 @@ # Minimum dependencies required prior to installation -INSTALL_REQUIRES = [ -] +INSTALL_REQUIRES = [] # Installation operation setup( @@ -23,12 +22,11 @@ description="Short horizon actor critic", keywords=["robotics", "rl"], include_package_data=True, - python_requires=">=3.6.*", install_requires=INSTALL_REQUIRES, package_dir={"": "src"}, packages=find_packages( where="src", exclude=["*.tests", "*.tests.*", "tests.*", "tests", "externals"] - ), + ), classifiers=[ "Natural Language :: English", "Programming Language :: Python :: 3.7, 3.8", diff --git a/src/shac/algorithms/ahac.py b/src/shac/algorithms/ahac.py new file mode 100644 index 00000000..04a4f844 --- /dev/null +++ b/src/shac/algorithms/ahac.py @@ -0,0 +1,960 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +# Adaptive Horizon Actor Critic (AHAC) is an alteration of SHAC. Instead +# of rolling out all envs in parallel for a fixed horizon, this attempts +# to rollout each env until it needs to be truncated. This can be viewed +# as an asynchronus rollout scheme where the gradients flowing back from +# each env are truncated independently from the others. + +# NOTE: Currently plagued with tech issues that don't let us do this efficiently. +# Still sorting that out and possible might never happen :( + +import sys, os + +from torch.nn.utils.clip_grad import clip_grad_norm_ + +project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(project_dir) + +import time +import copy +from tensorboardX import SummaryWriter +import yaml + +from shac import envs +import shac.models.actor as actor_models +import shac.models.critic as critic_models +from shac.utils.common import * +import shac.utils.torch_utils as tu +from shac.utils.running_mean_std import RunningMeanStd +from shac.utils.dataset import CriticDataset, QCriticDataset +from shac.utils.time_report import TimeReport +from shac.utils.average_meter import AverageMeter + + +class AHAC: + def __init__(self, cfg): + env_name = cfg["params"]["diff_env"].pop("name") + env_fn = getattr(envs, env_name) + + if "stochastic_init" in cfg["params"]["diff_env"]: + stochastic_init = cfg["params"]["diff_env"].pop("stochastic_init") + else: + stochastic_init = True + + config = dict( + num_envs=cfg["params"]["config"]["num_actors"], + device=cfg["params"]["general"]["device"], + render=cfg["params"]["general"]["render"], + seed=cfg["params"]["general"]["seed"], + episode_length=cfg["params"]["diff_env"].get("episode_length", 250), + stochastic_init=stochastic_init, + no_grad=False, + ) + + config.update(cfg["params"].get("diff_env", {})) + seeding(config["seed"]) + + self.env = env_fn(**config) + # reset diff_env config for yaml + cfg["params"]["diff_env"] = config + cfg["params"]["diff_env"]["name"] = env_name + + print("num_envs = ", self.env.num_envs) + print("num_actions = ", self.env.num_actions) + print("num_obs = ", self.env.num_obs) + + self.num_envs = self.env.num_envs + self.num_obs = self.env.num_obs + self.num_actions = self.env.num_actions + self.max_episode_length = self.env.episode_length + self.device = cfg["params"]["general"]["device"] + + self.gamma = cfg["params"]["config"].get("gamma", 0.99) + + self.critic_method = cfg["params"]["config"]["critic_method"] + assert self.critic_method in ["one-step", "td-lambda"] + self.lam = cfg["params"]["config"].get("lambda", 0.95) + + self.steps_min = cfg["params"]["config"].get("steps_min", 0) + self.steps_num = cfg["params"]["config"]["steps_num"] + self.contact_th = cfg["params"]["config"].get("contact_theshold", 1e9) + self.max_epochs = cfg["params"]["config"]["max_epochs"] + self.actor_lr = float(cfg["params"]["config"]["actor_learning_rate"]) + self.critic_lr = float(cfg["params"]["config"]["critic_learning_rate"]) + self.lr_schedule = cfg["params"]["config"].get("lr_schedule", "linear") + + self.target_critic_alpha = cfg["params"]["config"].get( + "target_critic_alpha", 0.4 + ) + + self.obs_rms = None + if cfg["params"]["config"].get("obs_rms", False): + self.obs_rms = RunningMeanStd(shape=(self.num_obs), device=self.device) + + self.ret_rms = None + if cfg["params"]["config"].get("ret_rms", False): + self.ret_rms = RunningMeanStd(shape=(), device=self.device) + + self.critic_iterations = cfg["params"]["config"].get("critic_iterations", 16) + self.num_batch = cfg["params"]["config"].get("num_batch", 4) + self.batch_size = self.num_envs * self.steps_num // self.num_batch + self.name = cfg["params"]["config"].get("name", "Ant") + + self.truncate_grad = cfg["params"]["config"]["truncate_grads"] + self.grad_norm = cfg["params"]["config"]["grad_norm"] + + if cfg["params"]["general"]["train"]: + self.log_dir = cfg["params"]["general"]["logdir"] + os.makedirs(self.log_dir, exist_ok=True) + # save config + save_cfg = copy.deepcopy(cfg) + if "general" in save_cfg["params"]: + deleted_keys = [] + for key in save_cfg["params"]["general"].keys(): + if key in save_cfg["params"]["config"]: + deleted_keys.append(key) + for key in deleted_keys: + del save_cfg["params"]["general"][key] + + yaml.dump(save_cfg, open(os.path.join(self.log_dir, "cfg.yaml"), "w")) + self.writer = SummaryWriter(os.path.join(self.log_dir, "log")) + # save interval + self.save_interval = cfg["params"]["config"].get("save_interval", 500) + # stochastic inference + self.stochastic_evaluation = True + else: + self.stochastic_evaluation = not ( + cfg["params"]["config"]["player"].get("determenistic", False) + or cfg["params"]["config"]["player"].get("deterministic", False) + ) + self.steps_num = self.env.episode_length + + self.eval_runs = cfg["params"]["config"]["player"]["games_num"] + + # create actor critic network + # choices: ['ActorDeterministicMLP', 'ActorStochasticMLP'] + self.actor_name = cfg["params"]["network"].get("actor", "ActorStochasticMLP") + actor_fn = getattr(actor_models, self.actor_name) + self.actor = actor_fn( + self.num_obs, self.num_actions, cfg["params"]["network"], device=self.device + ) + self.critic_name = "CriticMLP" # NOTE: hardcoded for future proofness + critic_fn = getattr(critic_models, self.critic_name) + self.critic = critic_fn( + self.num_obs, cfg["params"]["network"], device=self.device + ) + self.all_params = list(self.actor.parameters()) + list(self.critic.parameters()) + self.target_critic = copy.deepcopy(self.critic) + + if cfg["params"]["general"]["train"]: + self.save("init_policy") + + # initialize optimizer + self.actor_optimizer = torch.optim.Adam( + self.actor.parameters(), + betas=cfg["params"]["config"]["betas"], + lr=self.actor_lr, + ) + self.critic_optimizer = torch.optim.Adam( + self.critic.parameters(), + betas=cfg["params"]["config"]["betas"], + lr=self.critic_lr, + ) + + # accumulate rewards for each environment + # TODO make this vectorized somehow + self.rew_acc = [ + torch.tensor([0.0], dtype=torch.float32, device=self.device) + ] * self.num_envs + + # keep check of rollout length per environment + self.rollout_len = torch.zeros( + (self.num_envs,), dtype=torch.int32, device=self.device + ) + + # replay buffer + self.obs_buf = torch.zeros( + (self.steps_num, self.num_envs, self.num_obs), + dtype=torch.float32, + device=self.device, + ) + self.rew_buf = torch.zeros( + (self.steps_num, self.num_envs), dtype=torch.float32, device=self.device + ) + self.done_mask = torch.zeros( + (self.steps_num, self.num_envs), dtype=torch.float32, device=self.device + ) + self.next_values = torch.zeros( + (self.steps_num, self.num_envs), dtype=torch.float32, device=self.device + ) + self.target_values = torch.zeros( + (self.steps_num, self.num_envs), dtype=torch.float32, device=self.device + ) + self.ret = torch.zeros((self.num_envs), dtype=torch.float32, device=self.device) + + # for kl divergence computing + self.old_mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.old_sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + + # counting variables + self.iter_count = 0 + self.step_count = 0 + + # loss variables + self.episode_length_his = [] + self.episode_loss_his = [] + self.episode_discounted_loss_his = [] + self.episode_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + self.episode_discounted_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + self.episode_gamma = torch.ones( + self.num_envs, dtype=torch.float32, device=self.device + ) + self.episode_length = torch.zeros(self.num_envs, dtype=int) + self.done_buf = torch.zeros(self.num_envs, dtype=bool, device=self.device) + self.best_policy_loss = np.inf + self.actor_loss = np.inf + self.value_loss = np.inf + self.jacobians = [] + self.truncations = [] + self.contact_changes = [] + self.early_stops = [] + self.episode_ends = [] + + # average meter + self.episode_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_discounted_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_length_meter = AverageMeter(1, 100).to(self.device) + self.score_keys = cfg["params"]["config"].get("score_keys", []) + self.episode_scores_meter_map = { + key + "_final": AverageMeter(1, 100).to(self.device) + for key in self.score_keys + } + + # timer + self.time_report = TimeReport() + + def compute_actor_loss(self, deterministic=False): + actor_loss = torch.tensor(0.0, dtype=torch.float32, device=self.device) + actor_loss_terms = 0 # number of additions to actor_loss + + with torch.no_grad(): + if self.obs_rms is not None: + obs_rms = copy.deepcopy(self.obs_rms) + + if self.ret_rms is not None: + ret_var = self.ret_rms.var.clone() + + # fetch last observations + obs = self.env.obs_buf + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + # accumulates all rollout lengths after they have been cut + rollout_lens = [] + + # Start short horizon rollout + while actor_loss_terms < self.num_envs: + # collect data for critic training + with torch.no_grad(): + self.obs_buf[self.rollout_len] = obs.clone() + + # act in environment + actions = self.actor(obs, deterministic=deterministic) + obs, rew, term, trunc, info = self.env.step(torch.tanh(actions)) + + # episode is done because we have reset the environment + ep_done = trunc | term + ep_done_env_ids = ep_done.nonzero(as_tuple=False).squeeze(-1).cpu() + + self.done_buf = self.done_buf | ep_done + + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + if self.ret_rms is not None: + # update ret rms + with torch.no_grad(): + self.ret = self.ret * self.gamma + rew + self.ret_rms.update(self.ret) + + rew = rew / torch.sqrt(ret_var + 1e-6) + + self.episode_length += 1 + self.rollout_len += 1 + + # for logging + self.early_stops.append(term.cpu().numpy()) + self.episode_ends.append(trunc.cpu().numpy()) + + if "jacobian" in info: + jac = info["jacobian"] # shape NxSxA + self.jacobians.append(jac) + self.contact_changes.append(info["contacts_changed"].cpu().numpy()) + + # do horizon trunction + jac_norm = np.linalg.norm(jac, axis=(1, 2)) + contact_trunc = jac_norm > self.contact_th + contact_trunc = tu.to_torch(contact_trunc, dtype=torch.int64) + # ensure that we're not truncating envs before the minimum step size + contact_trunc = contact_trunc & (self.rollout_len >= self.steps_min) + # trunc = trunc | contact_trunc # NOTE: I don't think we need this anymore + + # for logging + # self.truncations.append(trunc.cpu().numpy()) + + real_obs = info["obs_before_reset"] + # sanity check + if (~torch.isfinite(real_obs)).sum() > 0: + print("Got inf obs") + raise ValueError + + if self.obs_rms is not None: + real_obs = obs_rms.normalize(real_obs) + + next_values = self.target_critic(real_obs).squeeze(-1) + + # handle terminated environments + term_env_ids = term.nonzero(as_tuple=False).squeeze(-1) + for id in term_env_ids: + next_values[id] = 0.0 + + # sanity check + if (next_values > 1e6).sum() > 0 or (next_values < -1e6).sum() > 0: + print("next value error") + raise ValueError + + self.rew_acc += self.gamma**self.rollout_len * rew + + # now merge truncation and termination into done + cutoff = self.rollout_len >= self.steps_num + if "jacobian" in info: + cutoff = cutoff | contact_trunc + # print("terminated", term.nonzero().flatten().tolist()) + # print("truncated", trunc.nonzero().flatten().tolist()) + # print("cutoff", cutoff.nonzero().flatten().tolist()) + done = term | trunc | cutoff + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + # terminate all done environments + # TODO vectorize somehow + for k in done_env_ids: + actor_loss -= ( + self.rew_acc[k] + self.gamma ** self.rollout_len[k] * next_values[k] + ).sum() + + # keep count of number of loss terms we've added so far + actor_loss_terms += done.sum().item() + + # clear up buffers + for k in done_env_ids: + self.rew_acc[k] = torch.zeros_like(self.rew_acc[k]) + rollout_lens.extend(self.rollout_len[done_env_ids].tolist()) + self.rollout_len[done_env_ids] = 0 + + # cut off gradients of all done envs + # TODO do I still need this? + # self.env.clear_grad_ids(done_env_ids) + + # get observations again since we need them detached + obs = self.env.obs_buf + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + # collect data for critic training + with torch.no_grad(): + self.rew_buf[self.rollout_len] = rew.clone() + self.done_mask[self.rollout_len] = done.clone().to(torch.float32) + self.next_values[self.rollout_len] = next_values.clone() + + # collect episode loss + with torch.no_grad(): + self.episode_loss -= rew + self.episode_discounted_loss -= self.episode_gamma * rew + self.episode_gamma *= self.gamma + if len(ep_done_env_ids) > 0: + self.episode_loss_meter.update(self.episode_loss[ep_done_env_ids]) + self.episode_discounted_loss_meter.update( + self.episode_discounted_loss[ep_done_env_ids] + ) + self.episode_length_meter.update( + self.episode_length[ep_done_env_ids] + ) + for k, v in filter(lambda x: x[0] in self.score_keys, info.items()): + self.episode_scores_meter_map[k + "_final"].update( + v[ep_done_env_ids] + ) + for ep_done_env_ids in ep_done_env_ids: + if ( + self.episode_loss[ep_done_env_ids] > 1e6 + or self.episode_loss[ep_done_env_ids] < -1e6 + ): + print("ep loss error") + raise ValueError + + self.episode_loss_his.append( + self.episode_loss[ep_done_env_ids].item() + ) + self.episode_discounted_loss_his.append( + self.episode_discounted_loss[ep_done_env_ids].item() + ) + self.episode_length_his.append( + self.episode_length[ep_done_env_ids].item() + ) + self.episode_loss[ep_done_env_ids] = 0.0 + self.episode_discounted_loss[ep_done_env_ids] = 0.0 + self.episode_length[ep_done_env_ids] = 0 + self.episode_gamma[ep_done_env_ids] = 1.0 + + steps = np.sum(rollout_lens) + actor_loss /= steps + + if self.ret_rms is not None: + actor_loss = actor_loss * torch.sqrt(ret_var + 1e-6) + + self.actor_loss = actor_loss.detach().item() + + self.step_count += steps + + self.mean_horizon = np.mean(rollout_lens) + + if torch.all(self.done_buf): + print("RESETTING ALL ENVS") + # self.env.reset(force_reset=True) + # self.env.reset(torch.arange(0, self.num_envs)) + self.env.initialize_trajectory() + self.done_buf = torch.zeros( + (self.num_envs,), dtype=bool, device=self.device + ) + + # technically reduces performance + # self.rew_acc = [ + # torch.tensor([0.0], dtype=torch.float32, device=self.device) + # ] * self.num_envs + # self.rollout_len = torch.zeros_like(self.rollout_len) + + return actor_loss + + @torch.no_grad() + def evaluate_policy(self, num_games, deterministic=False): + episode_length_his = [] + episode_loss_his = [] + episode_discounted_loss_his = [] + episode_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + episode_length = torch.zeros(self.num_envs, dtype=int) + episode_gamma = torch.ones( + self.num_envs, dtype=torch.float32, device=self.device + ) + episode_discounted_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + + obs = self.env.reset() + + games_cnt = 0 + while games_cnt < num_games: + if self.obs_rms is not None: + obs = self.obs_rms.normalize(obs) + + actions = self.actor(obs, deterministic=deterministic) + + obs, rew, term, trunc, _ = self.env.step(torch.tanh(actions), play=True) + done = term | trunc + + episode_length += 1 + + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + episode_loss -= rew + episode_discounted_loss -= episode_gamma * rew + episode_gamma *= self.gamma + if len(done_env_ids) > 0: + for done_env_id in done_env_ids: + print( + "loss = {:.2f}, len = {}".format( + episode_loss[done_env_id].item(), + episode_length[done_env_id], + ) + ) + episode_loss_his.append(episode_loss[done_env_id].item()) + episode_discounted_loss_his.append( + episode_discounted_loss[done_env_id].item() + ) + episode_length_his.append(episode_length[done_env_id].item()) + episode_loss[done_env_id] = 0.0 + episode_discounted_loss[done_env_id] = 0.0 + episode_length[done_env_id] = 0 + episode_gamma[done_env_id] = 1.0 + games_cnt += 1 + + mean_episode_length = np.mean(np.array(episode_length_his)) + mean_policy_loss = np.mean(np.array(episode_loss_his)) + mean_policy_discounted_loss = np.mean(np.array(episode_discounted_loss_his)) + + return mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + + @torch.no_grad() + def compute_target_values(self): + if self.critic_method == "one-step": + self.target_values = self.rew_buf + self.gamma * self.next_values + elif self.critic_method == "td-lambda": + Ai = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + Bi = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + lam = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + for i in reversed(range(self.steps_num)): + lam = lam * self.lam * (1.0 - self.done_mask[i]) + self.done_mask[i] + Ai = (1.0 - self.done_mask[i]) * ( + self.lam * self.gamma * Ai + + self.gamma * self.next_values[i] + + (1.0 - lam) / (1.0 - self.lam) * self.rew_buf[i] + ) + Bi = ( + self.gamma + * ( + self.next_values[i] * self.done_mask[i] + + Bi * (1.0 - self.done_mask[i]) + ) + + self.rew_buf[i] + ) + self.target_values[i] = (1.0 - self.lam) * Ai + lam * Bi + else: + raise NotImplementedError + + def compute_critic_loss(self, batch_sample): + predicted_values = self.critic(batch_sample["obs"]).squeeze(-1) + target_values = batch_sample["target_values"] + critic_loss = ((predicted_values - target_values) ** 2).mean() + + return critic_loss + + def initialize_env(self): + self.env.clear_grad() + self.env.reset() + + @torch.no_grad() + def run(self, num_games): + ( + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + ) = self.evaluate_policy( + num_games=num_games, deterministic=not self.stochastic_evaluation + ) + print_info( + "mean episode loss = {}, mean discounted loss = {}, mean episode length = {}".format( + mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + ) + ) + + def train(self): + self.start_time = time.time() + + # add timers + self.time_report.add_timer("algorithm") + self.time_report.add_timer("compute actor loss") + self.time_report.add_timer("forward simulation") + self.time_report.add_timer("backward simulation") + self.time_report.add_timer("prepare critic dataset") + self.time_report.add_timer("actor training") + self.time_report.add_timer("critic training") + + self.time_report.start_timer("algorithm") + + # initializations + self.initialize_env() + self.episode_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + self.episode_discounted_loss = torch.zeros( + self.num_envs, dtype=torch.float32, device=self.device + ) + self.episode_length = torch.zeros(self.num_envs, dtype=int) + self.episode_gamma = torch.ones( + self.num_envs, dtype=torch.float32, device=self.device + ) + + def actor_closure(): + self.actor_optimizer.zero_grad() + + self.time_report.start_timer("compute actor loss") + + self.time_report.start_timer("forward simulation") + actor_loss = self.compute_actor_loss() + self.time_report.end_timer("forward simulation") + + self.time_report.start_timer("backward simulation") + # need to retain the graph so that we can backprop through the reward + actor_loss.backward(retain_graph=True) + self.time_report.end_timer("backward simulation") + + with torch.no_grad(): + self.grad_norm_before_clip = tu.grad_norm(self.actor.parameters()) + if self.truncate_grad: + clip_grad_norm_(self.actor.parameters(), self.grad_norm) + self.grad_norm_after_clip = tu.grad_norm(self.actor.parameters()) + + # sanity check + if ( + torch.isnan(self.grad_norm_before_clip) + or self.grad_norm_before_clip > 1000000.0 + ): + print("NaN gradient") + raise ValueError + + self.time_report.end_timer("compute actor loss") + + return actor_loss + + # main training process + for epoch in range(self.max_epochs): + time_start_epoch = time.time() + + # learning rate schedule + if self.lr_schedule == "linear": + actor_lr = (1e-5 - self.actor_lr) * float( + epoch / self.max_epochs + ) + self.actor_lr + for param_group in self.actor_optimizer.param_groups: + param_group["lr"] = actor_lr + lr = actor_lr + critic_lr = (1e-5 - self.critic_lr) * float( + epoch / self.max_epochs + ) + self.critic_lr + for param_group in self.critic_optimizer.param_groups: + param_group["lr"] = critic_lr + else: + lr = self.actor_lr + + # clear buffers for critic + self.obs_buf = torch.zeros_like(self.obs_buf) + + # train actor + self.time_report.start_timer("actor training") + self.actor_optimizer.step(actor_closure).detach().item() + self.time_report.end_timer("actor training") + + # train critic + # prepare dataset + self.time_report.start_timer("prepare critic dataset") + with torch.no_grad(): + rew_backup = self.rew_buf.clone() + value_backup = self.next_values.clone() + obs_backup = self.obs_buf.clone() + + # set all rewards and values that haven't been finished to 0 + for n in range(self.num_envs): + # TODO not sure if this would make the critic learn 0 values + if self.rollout_len[n] != 0: + self.rew_buf[-self.rollout_len[n] :, n] = 0.0 + self.next_values[-self.rollout_len[n] :, n] = 0.0 + self.obs_buf[-self.rollout_len[n] :, n, :] = torch.nan + # NOTE: nans equal invalid data in the dataset below + + self.compute_target_values() + dataset = CriticDataset( + self.batch_size, + self.obs_buf, + self.target_values, + drop_last=False, + ) + # reset buffers correctly for next iteration + for n in range(self.num_envs): + if self.rollout_len[n] != 0: + self.rew_buf[: self.rollout_len[n], n] = rew_backup[ + -self.rollout_len[n] :, n + ] + self.next_values[: self.rollout_len[n], n] = value_backup[ + -self.rollout_len[n] :, n + ] + self.obs_buf[: self.rollout_len[n], n] = obs_backup[ + -self.rollout_len[n] :, n + ] + + self.time_report.end_timer("prepare critic dataset") + + self.time_report.start_timer("critic training") + self.value_loss = 0.0 + for j in range(self.critic_iterations): + total_critic_loss = 0.0 + batch_cnt = 0 + for i in range(len(dataset)): + batch_sample = dataset[i] + self.critic_optimizer.zero_grad() + training_critic_loss = self.compute_critic_loss(batch_sample) + training_critic_loss.backward() + + # ugly fix for simulation nan problem + for params in self.critic.parameters(): + params.grad.nan_to_num_(0.0, 0.0, 0.0) + + if self.truncate_grad: + clip_grad_norm_(self.critic.parameters(), self.grad_norm) + + self.critic_optimizer.step() + + total_critic_loss += training_critic_loss + batch_cnt += 1 + + self.value_loss = (total_critic_loss / batch_cnt).detach().cpu().item() + print( + "value iter {}/{}, loss = {:7.6f}".format( + j + 1, self.critic_iterations, self.value_loss + ), + end="\r", + ) + + self.time_report.end_timer("critic training") + + self.iter_count += 1 + + time_end_epoch = time.time() + + fps = self.steps_num * self.num_envs / (time_end_epoch - time_start_epoch) + + # logging + time_elapse = time.time() - self.start_time + self.writer.add_scalar("lr/iter", lr, self.iter_count) + self.writer.add_scalar("actor_loss/step", self.actor_loss, self.step_count) + self.writer.add_scalar("actor_loss/iter", self.actor_loss, self.iter_count) + self.writer.add_scalar("value_loss/step", self.value_loss, self.step_count) + self.writer.add_scalar("value_loss/iter", self.value_loss, self.iter_count) + self.writer.add_scalar( + "rollout_len/iter", self.mean_horizon, self.iter_count + ) + self.writer.add_scalar( + "rollout_len/step", self.mean_horizon, self.step_count + ) + self.writer.add_scalar("rollout_len/time", self.mean_horizon, time_elapse) + self.writer.add_scalar("fps/iter", fps, self.iter_count) + self.writer.add_scalar("fps/step", fps, self.step_count) + self.writer.add_scalar("fps/time", fps, time_elapse) + if len(self.episode_loss_his) > 0: + mean_episode_length = self.episode_length_meter.get_mean() + mean_policy_loss = self.episode_loss_meter.get_mean() + mean_policy_discounted_loss = ( + self.episode_discounted_loss_meter.get_mean() + ) + + if mean_policy_loss < self.best_policy_loss: + print_info( + "save best policy with loss {:.2f}".format(mean_policy_loss) + ) + self.save() + self.best_policy_loss = mean_policy_loss + + self.writer.add_scalar( + "policy_loss/step", mean_policy_loss, self.step_count + ) + self.writer.add_scalar( + "policy_loss/time", mean_policy_loss, time_elapse + ) + self.writer.add_scalar( + "policy_loss/iter", mean_policy_loss, self.iter_count + ) + self.writer.add_scalar( + "rewards/step", -mean_policy_loss, self.step_count + ) + self.writer.add_scalar("rewards/time", -mean_policy_loss, time_elapse) + self.writer.add_scalar( + "rewards/iter", -mean_policy_loss, self.iter_count + ) + if ( + self.score_keys + and len( + self.episode_scores_meter_map[self.score_keys[0] + "_final"] + ) + > 0 + ): + for score_key in self.score_keys: + score = self.episode_scores_meter_map[ + score_key + "_final" + ].get_mean() + self.writer.add_scalar( + "scores/{}/iter".format(score_key), score, self.iter_count + ) + self.writer.add_scalar( + "scores/{}/step".format(score_key), score, self.step_count + ) + self.writer.add_scalar( + "scores/{}/time".format(score_key), score, time_elapse + ) + self.writer.add_scalar( + "policy_discounted_loss/step", + mean_policy_discounted_loss, + self.step_count, + ) + self.writer.add_scalar( + "policy_discounted_loss/iter", + mean_policy_discounted_loss, + self.iter_count, + ) + self.writer.add_scalar( + "best_policy_loss/step", self.best_policy_loss, self.step_count + ) + self.writer.add_scalar( + "best_policy_loss/iter", self.best_policy_loss, self.iter_count + ) + self.writer.add_scalar( + "episode_lengths/iter", mean_episode_length, self.iter_count + ) + self.writer.add_scalar( + "episode_lengths/step", mean_episode_length, self.step_count + ) + self.writer.add_scalar( + "episode_lengths/time", mean_episode_length, time_elapse + ) + ac_stddev = self.actor.get_logstd().exp().mean().detach().cpu().item() + self.writer.add_scalar("ac_std/iter", ac_stddev, self.iter_count) + self.writer.add_scalar("ac_std/step", ac_stddev, self.step_count) + self.writer.add_scalar("ac_std/time", ac_stddev, time_elapse) + self.writer.add_scalar( + "actor_grad_norm/iter", self.grad_norm_before_clip, self.iter_count + ) + self.writer.add_scalar( + "actor_grad_norm/step", self.grad_norm_before_clip, self.step_count + ) + else: + mean_policy_loss = np.inf + mean_policy_discounted_loss = np.inf + mean_episode_length = 0 + + np.savez( + open(os.path.join(self.log_dir, "jacobians.npz"), "wb"), + jacobians=self.jacobians, + contact_changes=self.contact_changes, + truncations=self.truncations, + early_stops=self.early_stops, + episode_ends=self.episode_ends, + ) + + print( + "iter {:}/{:}, ep loss {:.2f}, ep discounted loss {:.2f}, ep len {:.1f}, avg rollout {:.1f}, total steps {:}, fps {:.2f}, value loss {:.2f}, grad norm before clip {:.2f}, grad norm after clip {:.2f}".format( + self.iter_count, + self.max_epochs, + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + self.mean_horizon, + self.step_count, + fps, + self.value_loss, + self.grad_norm_before_clip, + self.grad_norm_after_clip, + ) + ) + + self.writer.flush() + + if self.save_interval > 0 and (self.iter_count % self.save_interval == 0): + self.save( + self.name + + "policy_iter{}_reward{:.3f}".format( + self.iter_count, -mean_policy_loss + ) + ) + + # update target critic + with torch.no_grad(): + alpha = self.target_critic_alpha + for param, param_targ in zip( + self.critic.parameters(), self.target_critic.parameters() + ): + param_targ.data.mul_(alpha) + param_targ.data.add_((1.0 - alpha) * param.data) + + self.time_report.end_timer("algorithm") + + self.time_report.report() + + self.save("final_policy") + + # save reward/length history + self.episode_loss_his = np.array(self.episode_loss_his) + self.episode_discounted_loss_his = np.array(self.episode_discounted_loss_his) + self.episode_length_his = np.array(self.episode_length_his) + np.save( + open(os.path.join(self.log_dir, "episode_loss_his.npy"), "wb"), + self.episode_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_discounted_loss_his.npy"), "wb"), + self.episode_discounted_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_length_his.npy"), "wb"), + self.episode_length_his, + ) + + # evaluate the final policy's performance + self.run(self.eval_runs) + + self.close() + + def play(self, cfg): + self.load(cfg["params"]["general"]["checkpoint"]) + self.run(cfg["params"]["config"]["player"]["games_num"]) + + def save(self, filename=None): + if filename is None: + filename = "best_policy" + torch.save( + [self.actor, self.critic, self.target_critic, self.obs_rms, self.ret_rms], + os.path.join(self.log_dir, "{}.pt".format(filename)), + ) + + def load(self, path): + print("Loading policy from", path) + checkpoint = torch.load(path) + self.actor = checkpoint[0].to(self.device) + self.critic = checkpoint[1].to(self.device) + self.target_critic = checkpoint[2].to(self.device) + self.obs_rms = checkpoint[3].to(self.device) + self.ret_rms = ( + checkpoint[4].to(self.device) + if checkpoint[4] is not None + else checkpoint[4] + ) + + def close(self): + self.writer.close() diff --git a/src/shac/algorithms/mpc.py b/src/shac/algorithms/mpc.py new file mode 100644 index 00000000..ec0b6987 --- /dev/null +++ b/src/shac/algorithms/mpc.py @@ -0,0 +1,168 @@ +# only reset environment every 30 steps + +import numpy as np +import torch +from typing import Optional, Union +from collections import namedtuple +from scipy.interpolate import CubicSpline +from warp.envs import WarpEnv +from shac.envs import DFlexEnv + + +CheckpointState = namedtuple("CheckpointState", ["joint_q", "joint_qd"]) + + +class Policy: + def __init__( + self, + num_actions: int, + horizon: float = 0.5, + dt: float = 1.0 / 60.0, + max_steps: int = 512, + params: Optional[np.ndarray] = None, + policy_type: str = "zero", + step: float = 0.0, + ): + self.num_actions = num_actions + self.horizon = horizon + self.step = step + self.dt = dt + self.max_steps = max_steps + + # Spline points + steps = int(min(horizon / dt + 1, max_steps)) + self.timesteps = np.arange(steps) # np.linspace(0, horizon, steps) + self.params = params if params is not None else np.zeros((steps, num_actions)) + self.policy_type = policy_type + self._pi = None + self.pi = self.get_policy(self.params) + + @property + def pi(self): + return self._pi + + @pi.setter + def pi(self, new_pi): + self._pi = new_pi + self.step = 0 # reset step for nominal policy + + def get_policy(self, params=None, noise=None): + pol = self._pi + if pol is not None and params is None and noise is None: + return pol # early exit, return cached policy + if params is None: + params = self.params + if noise is not None: + params = params + noise * np.random.randn(*params.shape) + + if self.policy_type == "cubic": + print(params.shape) + pol = CubicSpline(np.arange(params.shape[0]), params, bc_type="natural") + elif self.policy_type == "zero": + pol = lambda x: params[min(self.max_steps - 1, np.searchsorted(self.timesteps[:-1], x))] + else: + assert self.policy_type == "linear" + pol = lambda x: np.stack([np.interp(x, self.timesteps, params[:, i]) for i in range(self.num_actions)]) + return pol + + def action(self, t, params=None): + if params is None: + return self._pi(t) + return self.get_policy(params)(t) + + +# Add a reset function +class Planner: + """A sampling-based planner""" + + def __init__( + self, + policy: Policy, + env: Union[WarpEnv, DFlexEnv], + noise: float = 0.1, + ): + self.policy = policy + self.noise = noise + self.env = env + self.num_trajectories = self.env.num_envs + + def optimize_policy(self): + """Optimize the policy""" + params = [self.policy.params] + [ + self.policy.params.copy() + self.noise * np.random.randn(*self.policy.params.shape) + for _ in range(self.num_trajectories - 1) + ] + policies = [self.policy.get_policy(p) for p in params] + + rewards = self.rollout(policies) + best_traj = torch.argmax(rewards).item() + winner = params[best_traj] + # with torch.no_grad(): + # self.clone_state(best_traj) + self.policy.params = winner + self.policy.pi = policies[best_traj] + + def step(self, eval_env): + """Step the environment, and all parallel envs to the same next state""" + action = self.policy.action(self.policy.step) + self.policy.step += self.policy.dt + obs, reward, done, info = eval_env.step(torch.tensor(action, dtype=torch.float32, device=eval_env.device)) + if isinstance(self.env, DFlexEnv): + joint_q, joint_qd = eval_env.get_state() + eval_ckpt = CheckpointState(joint_q=joint_q, joint_qd=joint_qd) + else: + eval_ckpt = eval_env.get_checkpoint() + self.copy_eval_checkpoint(eval_ckpt) + return obs, reward, done, info + + def clone_state(self, env_idx): + """Clone the state of the environment""" + if isinstance(self.env, DFlexEnv): + joint_q = self.env.state.joint_q.view(self.num_trajectories, -1)[env_idx : env_idx + 1] + # joint_q[:] = joint_q[env_idx : env_idx + 1] + joint_qd = self.env.state.joint_qd.view(self.num_trajectories, -1)[env_idx : env_idx + 1] + # joint_qd[:] = joint_qd[env_idx : env_idx + 1] + eval_ckpt = CheckpointState(joint_q=joint_q, joint_qd=joint_qd) + else: + ckpt = self.env.get_checkpoint() + eval_ckpt = {} + for k, v in ckpt.items(): + if not k.endswith("buf") and k != "actions": + eval_ckpt[k] = v[env_idx : env_idx + 1] + self.copy_eval_checkpoint(eval_ckpt) + + def copy_eval_checkpoint(self, eval_ckpt): + if isinstance(eval_ckpt, CheckpointState): + self.env.state.joint_q.view(self.num_trajectories, -1)[:] = eval_ckpt.joint_q.view(1, -1) + self.env.state.joint_qd.view(self.num_trajectories, -1)[:] = eval_ckpt.joint_qd.view(1, -1) + else: + ckpt = self.env.get_checkpoint() + for k in ckpt: + ckpt_v = eval_ckpt[k].view(1, -1) + ckpt[k] = ckpt_v.repeat(self.num_trajectories, 1) + self.env.load_checkpoint(ckpt) + + def rollout(self, policies=None, render=False): + """Rollout the policy""" + acc_rew = 0.0 + self.policy.step = 0 + if policies is None: + policies = [self.policy.get_policy()] * self.num_trajectories + + # rollout policy until horizon or end of episode reached + for t in range(self.policy.max_steps): + action = torch.tensor( + [policy(t) for policy in policies], + device=self.env.device, + dtype=torch.float32, + ) + obs, reward, _, _ = self.env.step(action) + if render: + self.env.render() + acc_rew += reward + self.policy.step += 1 + + return acc_rew + + def reset(self): + self.env.reset() diff --git a/src/shac/algorithms/shac.py b/src/shac/algorithms/shac.py index 66d60b4b..29b0cbc2 100644 --- a/src/shac/algorithms/shac.py +++ b/src/shac/algorithms/shac.py @@ -5,7 +5,6 @@ # distribution of this software and related documentation without an express # license agreement from NVIDIA CORPORATION is strictly prohibited. -from multiprocessing.sharedctypes import Value import sys, os from torch.nn.utils.clip_grad import clip_grad_norm_ @@ -24,7 +23,7 @@ from shac.utils.common import * import shac.utils.torch_utils as tu from shac.utils.running_mean_std import RunningMeanStd -from shac.utils.dataset import CriticDataset +from shac.utils.dataset import CriticDataset, QCriticDataset from shac.utils.time_report import TimeReport from shac.utils.average_meter import AverageMeter @@ -34,27 +33,28 @@ def __init__(self, cfg): env_name = cfg["params"]["diff_env"].pop("name") env_fn = getattr(envs, env_name) - seeding(cfg["params"]["general"]["seed"]) + if "stochastic_init" in cfg["params"]["diff_env"]: + stochastic_init = cfg["params"]["diff_env"].pop("stochastic_init") + else: + stochastic_init = True + config = dict( num_envs=cfg["params"]["config"]["num_actors"], device=cfg["params"]["general"]["device"], render=cfg["params"]["general"]["render"], seed=cfg["params"]["general"]["seed"], episode_length=cfg["params"]["diff_env"].get("episode_length", 250), - stochastic_init=cfg["params"]["diff_env"].get("stochastic_env", True), + stochastic_init=stochastic_init, no_grad=False, ) + config.update(cfg["params"].get("diff_env", {})) - if env_name.lower().find("warp") < 0: - config["MM_caching_frequency"] = cfg["params"]["diff_env"].get( - "MM_caching_frequency", 1 - ) - if env_name == "ClawWarpEnv": - from dmanip.config import ClawWarpConfig + seeding(config["seed"]) - self.env = env_fn(ClawWarpConfig(**config)) - else: - self.env = env_fn(**config) + self.env = env_fn(**config) + # reset diff_env config for yaml + cfg["params"]["diff_env"] = config + cfg["params"]["diff_env"]["name"] = env_name print("num_envs = ", self.env.num_envs) print("num_actions = ", self.env.num_actions) @@ -68,13 +68,13 @@ def __init__(self, cfg): self.gamma = cfg["params"]["config"].get("gamma", 0.99) - self.critic_method = cfg["params"]["config"].get( - "critic_method", "one-step" - ) # ['one-step', 'td-lambda'] - if self.critic_method == "td-lambda": - self.lam = cfg["params"]["config"].get("lambda", 0.95) + self.critic_method = cfg["params"]["config"]["critic_method"] + assert self.critic_method in ["one-step", "td-lambda"] + self.lam = cfg["params"]["config"].get("lambda", 0.95) + self.steps_min = cfg["params"]["config"].get("steps_min", 0) self.steps_num = cfg["params"]["config"]["steps_num"] + self.contact_th = cfg["params"]["config"].get("contact_theshold", 1e9) self.max_epochs = cfg["params"]["config"]["max_epochs"] self.actor_lr = float(cfg["params"]["config"]["actor_learning_rate"]) self.critic_lr = float(cfg["params"]["config"]["critic_learning_rate"]) @@ -92,8 +92,6 @@ def __init__(self, cfg): if cfg["params"]["config"].get("ret_rms", False): self.ret_rms = RunningMeanStd(shape=(), device=self.device) - self.rew_scale = cfg["params"]["config"].get("rew_scale", 1.0) - self.critic_iterations = cfg["params"]["config"].get("critic_iterations", 16) self.num_batch = cfg["params"]["config"].get("num_batch", 4) self.batch_size = self.num_envs * self.steps_num // self.num_batch @@ -128,15 +126,16 @@ def __init__(self, cfg): ) self.steps_num = self.env.episode_length + self.eval_runs = cfg["params"]["config"]["player"]["games_num"] + # create actor critic network - self.actor_name = cfg["params"]["network"].get( - "actor", "ActorStochasticMLP" - ) # choices: ['ActorDeterministicMLP', 'ActorStochasticMLP'] - self.critic_name = cfg["params"]["network"].get("critic", "CriticMLP") + # choices: ['ActorDeterministicMLP', 'ActorStochasticMLP'] + self.actor_name = cfg["params"]["network"].get("actor", "ActorStochasticMLP") actor_fn = getattr(actor_models, self.actor_name) self.actor = actor_fn( self.num_obs, self.num_actions, cfg["params"]["network"], device=self.device ) + self.critic_name = "CriticMLP" # NOTE: hardcoded for future proofness critic_fn = getattr(critic_models, self.critic_name) self.critic = critic_fn( self.num_obs, cfg["params"]["network"], device=self.device @@ -222,6 +221,11 @@ def __init__(self, cfg): self.best_policy_loss = np.inf self.actor_loss = np.inf self.value_loss = np.inf + self.jacobians = [] + self.truncations = [] + self.contact_changes = [] + self.early_stops = [] + self.episode_ends = [] # average meter self.episode_loss_meter = AverageMeter(1, 100).to(self.device) @@ -262,20 +266,25 @@ def compute_actor_loss(self, deterministic=False): self.obs_rms.update(obs) # normalize the current obs obs = obs_rms.normalize(obs) + + # accumulates all rollout lengths after they have been cut + rollout_lens = [] + # keeps track of the current length of the rollout + rollout_len = torch.zeros((self.num_envs,), device=self.device) + + # Start short horizon rollout for i in range(self.steps_num): # collect data for critic training with torch.no_grad(): self.obs_buf[i] = obs.clone() + # act in environment actions = self.actor(obs, deterministic=deterministic) + obs, rew, term, trunc, info = self.env.step(torch.tanh(actions)) - obs, rew, done, extra_info = self.env.step(torch.tanh(actions)) - - with torch.no_grad(): - raw_rew = rew.clone() - - # scale the reward - rew = rew * self.rew_scale + # episode is done because we have reset the environment + ep_done = trunc | term + ep_done_env_ids = ep_done.nonzero(as_tuple=False).squeeze(-1).cpu() if self.obs_rms is not None: # update obs rms @@ -293,29 +302,43 @@ def compute_actor_loss(self, deterministic=False): rew = rew / torch.sqrt(ret_var + 1e-6) self.episode_length += 1 + rollout_len += 1 + + # for logging + self.early_stops.append(term.cpu().numpy()) + self.episode_ends.append(trunc.cpu().numpy()) + self.truncations.append(trunc.cpu().numpy()) + + if "jacobian" in info: + jac = info["jacobian"] # shape NxSxA + self.jacobians.append(jac) + self.contact_changes.append(info["contacts_changed"].cpu().numpy()) + + # do horizon trunction + jac_norm = np.linalg.norm(jac, axis=(1, 2)) + contact_trunc = jac_norm > self.contact_th + contact_trunc = tu.to_torch(contact_trunc, dtype=torch.int64) + # ensure that we're not truncating envs before the minimum step size + contact_trunc = contact_trunc & (rollout_len >= self.steps_min) + # trunc = trunc | contact_trunc # NOTE: I don't think we need this anymore + + real_obs = info["obs_before_reset"] + # sanity check + if (~torch.isfinite(real_obs)).sum() > 0: + print("Got inf obs") + raise ValueError - done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + if self.obs_rms is not None: + real_obs = obs_rms.normalize(real_obs) - next_values[i + 1] = self.target_critic(obs).squeeze(-1) + next_values[i + 1] = self.target_critic(real_obs).squeeze(-1) - for id in done_env_ids: - if ( - torch.isnan(extra_info["obs_before_reset"][id]).sum() > 0 - or torch.isinf(extra_info["obs_before_reset"][id]).sum() > 0 - or (torch.abs(extra_info["obs_before_reset"][id]) > 1e6).sum() > 0 - ): # ugly fix for nan values - next_values[i + 1, id] = 0.0 - elif ( - self.episode_length[id] < self.max_episode_length - ): # early termination - next_values[i + 1, id] = 0.0 - else: # otherwise, use terminal value critic to estimate the long-term performance - if self.obs_rms is not None: - real_obs = obs_rms.normalize(extra_info["obs_before_reset"][id]) - else: - real_obs = extra_info["obs_before_reset"][id] - next_values[i + 1, id] = self.target_critic(real_obs).squeeze(-1) + # handle terminated environments + term_env_ids = term.nonzero(as_tuple=False).squeeze(-1) + for id in term_env_ids: + next_values[i + 1, id] = 0.0 + # sanity check if (next_values[i + 1] > 1e6).sum() > 0 or ( next_values[i + 1] < -1e6 ).sum() > 0: @@ -324,24 +347,23 @@ def compute_actor_loss(self, deterministic=False): rew_acc[i + 1, :] = rew_acc[i, :] + gamma * rew + # now merge truncation and termination into done + done = term | trunc + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + if i < self.steps_num - 1: - actor_loss = ( - actor_loss - + ( - -rew_acc[i + 1, done_env_ids] - - self.gamma - * gamma[done_env_ids] - * next_values[i + 1, done_env_ids] - ).sum() - ) + # first terminate all rollouts which are 'done' + actor_loss += ( + -rew_acc[i + 1, done_env_ids] + - self.gamma + * gamma[done_env_ids] + * next_values[i + 1, done_env_ids] + ).sum() else: - # terminate all envs at the end of optimization iteration - actor_loss = ( - actor_loss - + ( - -rew_acc[i + 1, :] - self.gamma * gamma * next_values[i + 1, :] - ).sum() - ) + # terminate all envs because we reached the end of our rollout + actor_loss += ( + -rew_acc[i + 1, :] - self.gamma * gamma * next_values[i + 1, :] + ).sum() # compute gamma for next step gamma = gamma * self.gamma @@ -349,8 +371,11 @@ def compute_actor_loss(self, deterministic=False): # clear up gamma and rew_acc for done envs gamma[done_env_ids] = 1.0 rew_acc[i + 1, done_env_ids] = 0.0 + rollout_lens.extend(rollout_len[done_env_ids].tolist()) + rollout_len[done_env_ids] = 0 # collect data for critic training + # TODO should behaviour before be the same for all of them? with torch.no_grad(): self.rew_buf[i] = rew.clone() if i < self.steps_num - 1: @@ -361,52 +386,55 @@ def compute_actor_loss(self, deterministic=False): # collect episode loss with torch.no_grad(): - self.episode_loss -= raw_rew - self.episode_discounted_loss -= self.episode_gamma * raw_rew + self.episode_loss -= rew + self.episode_discounted_loss -= self.episode_gamma * rew self.episode_gamma *= self.gamma - if len(done_env_ids) > 0: - self.episode_loss_meter.update(self.episode_loss[done_env_ids]) + if len(ep_done_env_ids) > 0: + self.episode_loss_meter.update(self.episode_loss[ep_done_env_ids]) self.episode_discounted_loss_meter.update( - self.episode_discounted_loss[done_env_ids] + self.episode_discounted_loss[ep_done_env_ids] ) - self.episode_length_meter.update(self.episode_length[done_env_ids]) - for k, v in filter( - lambda x: x[0] in self.score_keys, extra_info.items() - ): + self.episode_length_meter.update( + self.episode_length[ep_done_env_ids] + ) + for k, v in filter(lambda x: x[0] in self.score_keys, info.items()): self.episode_scores_meter_map[k + "_final"].update( - v[done_env_ids] + v[ep_done_env_ids] ) - for done_env_id in done_env_ids: + for ep_done_env_ids in ep_done_env_ids: if ( - self.episode_loss[done_env_id] > 1e6 - or self.episode_loss[done_env_id] < -1e6 + self.episode_loss[ep_done_env_ids] > 1e6 + or self.episode_loss[ep_done_env_ids] < -1e6 ): print("ep loss error") raise ValueError self.episode_loss_his.append( - self.episode_loss[done_env_id].item() + self.episode_loss[ep_done_env_ids].item() ) self.episode_discounted_loss_his.append( - self.episode_discounted_loss[done_env_id].item() + self.episode_discounted_loss[ep_done_env_ids].item() ) self.episode_length_his.append( - self.episode_length[done_env_id].item() + self.episode_length[ep_done_env_ids].item() ) - self.episode_loss[done_env_id] = 0.0 - self.episode_discounted_loss[done_env_id] = 0.0 - self.episode_length[done_env_id] = 0 - self.episode_gamma[done_env_id] = 1.0 + self.episode_loss[ep_done_env_ids] = 0.0 + self.episode_discounted_loss[ep_done_env_ids] = 0.0 + self.episode_length[ep_done_env_ids] = 0 + self.episode_gamma[ep_done_env_ids] = 1.0 actor_loss /= self.steps_num * self.num_envs if self.ret_rms is not None: actor_loss = actor_loss * torch.sqrt(ret_var + 1e-6) - self.actor_loss = actor_loss.detach().cpu().item() + self.actor_loss = actor_loss.detach().item() self.step_count += self.steps_num * self.num_envs + rollout_lens.extend(rollout_len.tolist()) + self.mean_horizon = np.mean(rollout_lens) + return actor_loss @torch.no_grad() @@ -434,7 +462,8 @@ def evaluate_policy(self, num_games, deterministic=False): actions = self.actor(obs, deterministic=deterministic) - obs, rew, done, _ = self.env.step(torch.tanh(actions)) + obs, rew, term, trunc, _ = self.env.step(torch.tanh(actions), play=True) + done = term | trunc episode_length += 1 @@ -610,7 +639,10 @@ def actor_closure(): with torch.no_grad(): self.compute_target_values() dataset = CriticDataset( - self.batch_size, self.obs_buf, self.target_values, drop_last=False + self.batch_size, + self.obs_buf, + self.target_values, + drop_last=False, ) self.time_report.end_timer("prepare critic dataset") @@ -651,6 +683,8 @@ def actor_closure(): time_end_epoch = time.time() + fps = self.steps_num * self.num_envs / (time_end_epoch - time_start_epoch) + # logging time_elapse = time.time() - self.start_time self.writer.add_scalar("lr/iter", lr, self.iter_count) @@ -658,6 +692,16 @@ def actor_closure(): self.writer.add_scalar("actor_loss/iter", self.actor_loss, self.iter_count) self.writer.add_scalar("value_loss/step", self.value_loss, self.step_count) self.writer.add_scalar("value_loss/iter", self.value_loss, self.iter_count) + self.writer.add_scalar( + "rollout_len/iter", self.mean_horizon, self.iter_count + ) + self.writer.add_scalar( + "rollout_len/step", self.mean_horizon, self.step_count + ) + self.writer.add_scalar("rollout_len/time", self.mean_horizon, time_elapse) + self.writer.add_scalar("fps/iter", fps, self.iter_count) + self.writer.add_scalar("fps/step", fps, self.step_count) + self.writer.add_scalar("fps/time", fps, time_elapse) if len(self.episode_loss_his) > 0: mean_episode_length = self.episode_length_meter.get_mean() mean_policy_loss = self.episode_loss_meter.get_mean() @@ -733,20 +777,40 @@ def actor_closure(): self.writer.add_scalar( "episode_lengths/time", mean_episode_length, time_elapse ) + ac_stddev = self.actor.get_logstd().exp().mean().detach().cpu().item() + self.writer.add_scalar("ac_std/iter", ac_stddev, self.iter_count) + self.writer.add_scalar("ac_std/step", ac_stddev, self.step_count) + self.writer.add_scalar("ac_std/time", ac_stddev, time_elapse) + self.writer.add_scalar( + "actor_grad_norm/iter", self.grad_norm_before_clip, self.iter_count + ) + self.writer.add_scalar( + "actor_grad_norm/step", self.grad_norm_before_clip, self.step_count + ) else: mean_policy_loss = np.inf mean_policy_discounted_loss = np.inf mean_episode_length = 0 + np.savez( + open(os.path.join(self.log_dir, "jacobians.npz"), "wb"), + jacobians=self.jacobians, + contact_changes=self.contact_changes, + truncations=self.truncations, + early_stops=self.early_stops, + episode_ends=self.episode_ends, + ) + print( - "iter {}: ep loss {:.2f}, ep discounted loss {:.2f}, ep len {:.1f}, fps total {:.2f}, value loss {:.2f}, grad norm before clip {:.2f}, grad norm after clip {:.2f}".format( + "iter {:}/{:}, ep loss {:.2f}, ep discounted loss {:.2f}, ep len {:.1f}, avg rollout {:.1f}, total steps {:}, fps {:.2f}, value loss {:.2f}, grad norm before clip {:.2f}, grad norm after clip {:.2f}".format( self.iter_count, + self.max_epochs, mean_policy_loss, mean_policy_discounted_loss, mean_episode_length, - self.steps_num - * self.num_envs - / (time_end_epoch - time_start_epoch), + self.mean_horizon, + self.step_count, + fps, self.value_loss, self.grad_norm_before_clip, self.grad_norm_after_clip, @@ -796,7 +860,7 @@ def actor_closure(): ) # evaluate the final policy's performance - self.run(self.num_envs) + self.run(self.eval_runs) self.close() @@ -813,6 +877,7 @@ def save(self, filename=None): ) def load(self, path): + print("Loading policy from", path) checkpoint = torch.load(path) self.actor = checkpoint[0].to(self.device) self.critic = checkpoint[1].to(self.device) diff --git a/src/shac/algorithms/shac2.py b/src/shac/algorithms/shac2.py new file mode 100644 index 00000000..b125f686 --- /dev/null +++ b/src/shac/algorithms/shac2.py @@ -0,0 +1,926 @@ +# Added option to resume training and loa Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import copy +import math +from hydra.utils import instantiate +import os +import sys +import time + +import torch +from omegaconf import DictConfig, OmegaConf +from rl_games.common import schedulers +from rl_games.algos_torch import torch_ext +from shac.utils.average_meter import AverageMeter +from shac.utils.common import * +from shac.utils.dataset import QCriticDataset +from shac.utils.running_mean_std import RunningMeanStd +from shac.utils.time_report import TimeReport +from shac.models import actor as actor_models +from shac.models import critic as critic_model +import shac.utils.torch_utils as tu +from tensorboardX import SummaryWriter +import torch.distributed as dist +from torch.nn.utils.clip_grad import clip_grad_norm_ + + +project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(project_dir) + + +class SHAC: + """SHAC algorithm implementation""" + + def __init__(self, cfg: DictConfig): + seeding(cfg.general.seed) + self.env = instantiate(cfg.env.config) + + print("num_envs = ", self.env.num_envs) + print("num_actions = ", self.env.num_actions) + print("num_obs = ", self.env.num_obs) + + self.multi_gpu = cfg.general.multi_gpu + self.rank = 0 + self.rank_size = 1 + + if self.multi_gpu: + self.rank = int(os.getenv("LOCAL_RANK", "0")) + self.rank_size = int(os.getenv("WORLD_SIZE", "1")) + dist.init_process_group("nccl", rank=self.rank, world_size=self.rank_size) + + self.device_name = "cuda:" + str(self.rank) + cfg.general.device = self.device_name + if self.rank != 0: + cfg.alg.params.config.print_stats = False + # cfg["params"]["config"]["lr_schedule"] = None + + self.num_envs = self.env.num_envs + self.num_obs = self.env.num_obs + self.num_actions = self.env.num_actions + self.max_episode_length = self.env.episode_length + self.device = cfg.general.device + + self.gamma = cfg.alg.params.config.gamma + + self.critic_method = cfg.alg.params.config.critic_method + if self.critic_method == "td-lambda": + self.lam = cfg.alg.params.config.lam + + self.steps_num = cfg.alg.params.config.steps_num + self.max_epochs = cfg.alg.params.config.max_epochs + self.actor_lr = float(cfg.env.shac2.actor_lr) + self.critic_lr = float(cfg.env.shac2.critic_lr) + self.lr_schedule = cfg.alg.params.config.lr_schedule + + self.is_adaptive_lr = self.lr_schedule == "adaptive" + self.is_linear_lr = self.lr_schedule == "linear" + + if self.is_adaptive_lr: + self.scheduler = instantiate(cfg.alg.params.default_adaptive_scheduler) + elif self.is_linear_lr: + self.scheduler = instantiate(cfg.alg.params.default_linear_scheduler) + else: + self.scheduler = schedulers.IdentityScheduler() + + self.target_critic_alpha = cfg.alg.params.config.target_critic_alpha + + self._obs_rms = None + self.curr_epoch = 0 + self.sub_traj_per_epoch = math.ceil(self.max_episode_length / self.steps_num) + # number of epochs of no improvement for early stopping + self.early_stopping_patience = cfg.alg.params.config.early_stopping_patience + if cfg.alg.params.config.obs_rms: + # generate obs_rms for each subtrajectory + self._obs_rms = [ + RunningMeanStd(shape=(self.num_obs), device=self.device) + # for _ in range(self.sub_traj_per_epoch) + ] + + self.ret_rms = None + if cfg.alg.params.config.ret_rms: + self.ret_rms = RunningMeanStd(shape=(), device=self.device) + + self.rew_scale = cfg.alg.params.config.rew_scale + + self.critic_iterations = cfg.alg.params.config.critic_iterations + self.num_batch = cfg.alg.params.config.num_batch + self.batch_size = self.num_envs * self.steps_num // self.num_batch + self.name = cfg.alg.params.config.name + + self.truncate_grad = cfg.alg.params.config.truncate_grads + self.grad_norm = cfg.alg.params.config.grad_norm + + if cfg.general.train: + self.log_dir = cfg.general.logdir + if not self.multi_gpu or self.rank == 0: + os.makedirs(self.log_dir, exist_ok=True) + with open(os.path.join(self.log_dir, "cfg.yaml"), "w") as f: + f.write(OmegaConf.to_yaml(cfg)) + self.writer = SummaryWriter(os.path.join(self.log_dir, "log")) + # save interval + self.save_interval = cfg.alg.params.config.save_interval + # stochastic inference + self.stochastic_evaluation = True + else: + self.stochastic_evaluation = not cfg.alg.params.config.player.determenistic + self.steps_num = self.env.episode_length + + # create actor critic network + self.actor = instantiate( + cfg.alg.params.network.actor, + obs_dim=self.num_obs, + action_dim=self.num_actions, + device=self.device, + ) + self.critic = instantiate( + cfg.alg.params.network.critic, + obs_dim=self.num_obs, + action_dim=self.num_actions, + device=self.device, + ) + self.all_params = list(self.actor.parameters()) + list(self.critic.parameters()) + self.target_critic = copy.deepcopy(self.critic) + + # initialize optimizer + self.actor_optimizer = instantiate(cfg.alg.params.config.actor_optimizer, params=self.actor.parameters()) + self.critic_optimizer = instantiate(cfg.alg.params.config.critic_optimizer, params=self.critic.parameters()) + + self.mixed_precision = cfg.general.mixed_precision + self.scaler = torch.cuda.amp.GradScaler(enabled=self.mixed_precision) + + # replay buffer + self.obs_buf = torch.zeros( + (self.steps_num, self.num_envs, self.num_obs), + dtype=torch.float32, + device=self.device, + ) + self.act_buf = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.rew_buf = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.done_mask = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.next_values = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.target_values = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.ret = torch.zeros((self.num_envs), dtype=torch.float32, device=self.device) + + # for kl divergence computing + self.old_mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.old_sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + + if cfg.general.train: + self.save("init_policy") + + # counting variables + self.iter_count = 0 + self.step_count = 0 + + # loss variables + self.episode_length_his = [] + self.episode_loss_his = [] + self.episode_discounted_loss_his = [] + self.episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_length = torch.zeros(self.num_envs, dtype=int, device=self.device) + self.best_policy_loss = np.inf + self.best_policy_epoch = 0 + self.actor_loss = np.inf + self.value_loss = np.inf + + # average meter + self.episode_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_discounted_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_length_meter = AverageMeter(1, 100).to(self.device) + self.score_keys = cfg.alg.params.config.score_keys + self.episode_scores_meter_map = { + key + "_final": AverageMeter(1, 100).to(self.device) for key in self.score_keys + } + + # timer + self.time_report = TimeReport() + + @property + def obs_rms(self): + if self._obs_rms is None: + return self._obs_rms + return self._obs_rms[0] # self.curr_epoch % self.sub_traj_per_epoch] + + def compute_actor_loss(self, deterministic=False): + rew_acc = torch.zeros((self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device) + gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + next_values = torch.zeros((self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device) + # next_values_model_free = torch.zeros( + # (self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device + # ) + + actor_loss = torch.tensor(0.0, dtype=torch.float32, device=self.device) + # actor_model_free_loss = torch.tensor(0.0, dtype=torch.float32, device=self.device) + + with torch.no_grad(): + if self.obs_rms is not None: + obs_rms = copy.deepcopy(self.obs_rms) + + if self.ret_rms is not None: + ret_var = self.ret_rms.var.clone() + + # initialize trajectory to cut off gradients between episodes. + obs = self.env.initialize_trajectory() + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + # copy previous state mus, sigma to old_mus, old_simgas + if self.curr_epoch > 1: + self.old_mus[:] = self.mus.clone() + self.old_sigmas[:] = self.sigmas.clone() + + self.ep_kls = [] + next_actions = torch.tanh(self.actor(obs, deterministic=deterministic)) + + for i in range(self.steps_num): + # collect data for critic training + with torch.no_grad(): + self.obs_buf[i] = obs.clone() + + # normalized sampled action: pi(s) + actions = next_actions + + with torch.no_grad(): + self.act_buf[i] = actions.clone() + + with torch.no_grad(): + _, mus_i, sigmas_i = self.actor.forward_with_dist(obs, deterministic=True) + self.mus[i, :], self.sigmas[i, :] = mus_i.clone(), sigmas_i.clone() + + obs, rew, done, extra_info = self.env.step(actions) + + with torch.no_grad(): + raw_rew = rew.clone() + + # scale the reward + rew = rew * self.rew_scale + + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + if self.ret_rms is not None: + # update ret rms + with torch.no_grad(): + self.ret[:] = self.ret * self.gamma + rew + self.ret_rms.update(self.ret) + + rew = rew / torch.sqrt(ret_var + 1e-6) + + self.episode_length += 1 + + # done = done.clone() | extra_info.get("contact_changed", torch.zeros_like(done)) + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + next_actions = torch.tanh(self.actor(obs, deterministic=True)) + next_values[i + 1] = self.target_critic(obs, next_actions).squeeze(-1) + # next_values_model_free[i + 1] = self.target_critic(obs.detach(), next_actions.detach()).squeeze(-1) + # next_values_model_free[i + 1] = torch.minimum( + # self.critic(obs.requires_grad_(False), actions).squeeze(-1), + # self.target_critic(obs.require_grad_(False), actions).squeeze(-1), + # ) + + # zero next_values for done envs with inf, nan, or >1e6 values in obs_before_reset + # or early termination + + if done_env_ids.shape[0] > 0: + zero_next_values = torch.where( + torch.isnan(extra_info["obs_before_reset"][done_env_ids]).any(dim=-1) + | torch.isinf(extra_info["obs_before_reset"][done_env_ids]).any(dim=-1) + | (torch.abs(extra_info["obs_before_reset"][done_env_ids]) > 1e6).any(dim=-1) + | (self.episode_length[done_env_ids] < self.max_episode_length), + torch.ones_like(done_env_ids, dtype=bool), + torch.zeros_like(done_env_ids, dtype=bool), + ) + zero_next_values, assign_next_values = done_env_ids[zero_next_values], done_env_ids[~zero_next_values] + if zero_next_values.shape[0] > 0: + next_values[i + 1, zero_next_values] = 0.0 + # next_values_model_free[i + 1, zero_next_values] = 0.0 + # use terminal value critic to estimate the long-term performance + if assign_next_values.shape[0] > 0: + if self.obs_rms is not None: + real_obs = obs_rms.normalize(extra_info["obs_before_reset"][assign_next_values]) + real_act = actions[assign_next_values] + else: + real_obs = extra_info["obs_before_reset"][assign_next_values] + real_act = actions[assign_next_values] + next_values[i + 1, assign_next_values] = self.critic(real_obs, real_act).squeeze(-1) + # next_values_model_free[i + 1, assign_next_values] = self.critic( + # real_obs.detach(), real_actions + # ).squeeze(-1) + if (next_values[i + 1] > 1e6).sum() > 0 or (next_values[i + 1] < -1e6).sum() > 0: + print("next value error") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + rew_acc[i + 1, :] = rew_acc[i, :] + gamma * rew + + if i < self.steps_num - 1: + actor_loss += ( + -rew_acc[i + 1, done_env_ids] - self.gamma * gamma[done_env_ids] * next_values[i + 1, done_env_ids] + ).sum() + # actor_model_free_loss += ( + # -rew_acc[i + 1, done_env_ids].detach() + # - self.gamma * gamma[done_env_ids] * next_values_model_free[i + 1, done_env_ids] + # ).sum() + else: + # terminate all envs at the end of optimization iteration + actor_loss += (-rew_acc[i + 1, :] - self.gamma * gamma * next_values[i + 1, :]).sum() + # actor_model_free_loss += ( + # -rew_acc[i + 1, :].detach() - self.gamma * gamma * next_values_model_free[i + 1, :] + # ).sum() + + # compute gamma for next step + gamma = gamma * self.gamma + + # clear up gamma and rew_acc for done envs + gamma[done_env_ids] = 1.0 + rew_acc[i + 1, done_env_ids] = 0.0 + + # collect data for critic training + with torch.no_grad(): + self.rew_buf[i] = rew.clone() + if i < self.steps_num - 1: + self.done_mask[i] = done.clone().to(torch.float32) + else: + self.done_mask[i, :] = 1.0 + self.next_values[i] = next_values[i + 1].clone() + # collect episode loss + with torch.no_grad(): + self.episode_loss -= raw_rew + self.episode_discounted_loss -= self.episode_gamma * raw_rew + self.episode_gamma *= self.gamma + if len(done_env_ids) > 0: + self.episode_loss_meter.update(self.episode_loss[done_env_ids]) + self.episode_discounted_loss_meter.update(self.episode_discounted_loss[done_env_ids]) + self.episode_length_meter.update(self.episode_length[done_env_ids]) + for k, v in filter(lambda x: x[0] in self.score_keys, extra_info.items()): + self.episode_scores_meter_map[k + "_final"].update(v[done_env_ids]) + for done_env_id in done_env_ids: + if self.episode_loss[done_env_id] > 1e6 or self.episode_loss[done_env_id] < -1e6: + print("ep loss error") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + self.episode_loss_his.append(self.episode_loss[done_env_id].item()) + self.episode_discounted_loss_his.append(self.episode_discounted_loss[done_env_id].item()) + self.episode_length_his.append(self.episode_length[done_env_id].item()) + self.episode_loss[done_env_id] = 0.0 + self.episode_discounted_loss[done_env_id] = 0.0 + self.episode_length[done_env_id] = 0 + self.episode_gamma[done_env_id] = 1.0 + + # if first epoch, clone old_mus, sigmas + if self.curr_epoch == 1: + self.old_mus[:] = self.mus.clone() + self.old_sigmas[:] = self.sigmas.clone() + + actor_loss /= self.steps_num * self.num_envs + # actor_model_free_loss /= self.steps_num * self.num_envs + + if self.ret_rms is not None: + actor_loss = actor_loss * torch.sqrt(ret_var + 1e-6) + # actor_model_free_loss = actor_model_free_loss * torch.sqrt(ret_var + 1e-6) + + self.actor_loss = actor_loss.detach().cpu().item() + # self.actor_model_free_loss = actor_loss.detach().cpu().item() + + self.step_count += self.steps_num * self.num_envs + + return actor_loss, None + + @torch.no_grad() + def evaluate_policy(self, num_games, deterministic=False): + episode_length_his = [] + episode_loss_his = [] + episode_discounted_loss_his = [] + episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + episode_length = torch.zeros(self.num_envs, dtype=int, device=self.device) + episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + + obs = self.env.reset() + + games_cnt = 0 + while games_cnt < num_games: + if self.obs_rms is not None: + obs = self.obs_rms.normalize(obs) + + actions = torch.tanh(self.actor(obs, deterministic=deterministic)) + + obs, rew, done, _ = self.env.step(actions) + + episode_length += 1 + + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + episode_loss -= rew + episode_discounted_loss -= episode_gamma * rew + episode_gamma *= self.gamma + if len(done_env_ids) > 0: + for done_env_id in done_env_ids: + print( + "loss = {:.2f}, len = {}".format( + episode_loss[done_env_id].item(), + episode_length[done_env_id], + ) + ) + episode_loss_his.append(episode_loss[done_env_id].item()) + episode_discounted_loss_his.append(episode_discounted_loss[done_env_id].item()) + episode_length_his.append(episode_length[done_env_id].item()) + episode_loss[done_env_id] = 0.0 + episode_discounted_loss[done_env_id] = 0.0 + episode_length[done_env_id] = 0 + episode_gamma[done_env_id] = 1.0 + games_cnt += 1 + + mean_episode_length = np.mean(np.array(episode_length_his)) + mean_policy_loss = np.mean(np.array(episode_loss_his)) + mean_policy_discounted_loss = np.mean(np.array(episode_discounted_loss_his)) + + return mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + + @torch.no_grad() + def compute_target_values(self): + if self.critic_method == "one-step": + self.target_values = self.rew_buf + self.gamma * self.next_values + elif self.critic_method == "td-lambda": + Ai = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + Bi = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + lam = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + for i in reversed(range(self.steps_num)): + lam = lam * self.lam * (1.0 - self.done_mask[i]) + self.done_mask[i] + Ai = (1.0 - self.done_mask[i]) * ( + self.lam * self.gamma * Ai + + self.gamma * self.next_values[i] + + (1.0 - lam) / (1.0 - self.lam) * self.rew_buf[i] + ) + Bi = ( + self.gamma * (self.next_values[i] * self.done_mask[i] + Bi * (1.0 - self.done_mask[i])) + + self.rew_buf[i] + ) + self.target_values[i] = (1.0 - self.lam) * Ai + lam * Bi + else: + raise NotImplementedError + + def compute_critic_loss(self, batch_sample): + predicted_values = self.critic(batch_sample["obs"], batch_sample["act"]).squeeze(-1) + target_values = batch_sample["target_values"] + critic_loss = ((predicted_values - target_values) ** 2).mean() + + return critic_loss + + def initialize_env(self): + self.env.clear_grad() + self.env.reset() + + @torch.no_grad() + def run(self, num_games): + ( + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + ) = self.evaluate_policy(num_games=num_games, deterministic=not self.stochastic_evaluation) + print_info( + "mean episode loss = {}, mean discounted loss = {}, mean episode length = {}".format( + mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + ) + ) + + def train(self): + self.start_time = time.time() + + # add timers + self.time_report.add_timer("algorithm") + self.time_report.add_timer("compute actor loss") + self.time_report.add_timer("forward simulation") + self.time_report.add_timer("backward simulation") + self.time_report.add_timer("prepare critic dataset") + self.time_report.add_timer("actor training") + self.time_report.add_timer("critic training") + + self.time_report.start_timer("algorithm") + + # initializations + self.initialize_env() + if self.multi_gpu: + torch.cuda.set_device(self.rank) + if self.rank == 0: + print("====================broadcasting parameters") + print("====actor parameters") + actor_params = [self.actor.state_dict()] + dist.broadcast_object_list(actor_params, 0) + self.actor.load_state_dict(actor_params[0]) + if self.rank == 0: + print("====critic parameters") + critic_params = [self.critic.state_dict()] + dist.broadcast_object_list(critic_params, 0) + self.critic.load_state_dict(critic_params[0]) + if self.rank == 0: + print("done broadcasting parameters====================") + + self.episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_length = torch.zeros(self.num_envs, dtype=int, device=self.device) + self.episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + + def actor_closure(): + self.actor_optimizer.zero_grad(set_to_none=True) + + self.time_report.start_timer("compute actor loss") + + self.time_report.start_timer("forward simulation") + + # use autoscaling for mixed precision + with torch.cuda.amp.autocast(enabled=self.mixed_precision): + actor_loss, _ = self.compute_actor_loss() + self.time_report.end_timer("forward simulation") + + self.time_report.start_timer("backward simulation") + self.scaler.scale(actor_loss).backward() + # actor_loss.backward() + self.time_report.end_timer("backward simulation") + + with torch.no_grad(): + # unscale here to get grad norm before clipping + self.scaler.unscale_(self.actor_optimizer) + self.grad_norm_before_clip = tu.grad_norm(self.actor.parameters()) + self.clip_gradients(self.actor.parameters(), self.actor_optimizer, unscale=False) + self.grad_norm_after_clip = tu.grad_norm(self.actor.parameters()) + self.scaler.step(self.actor_optimizer) + self.scaler.update() + + # sanity check + if torch.isnan(self.grad_norm_before_clip) or self.grad_norm_before_clip > 1000000.0: + print("shac training crashed due to unstable gradient") + # torch.save(env_state, os.path.join(self.log_dir, "bad_state.pt")) + print("NaN gradient") + if not self.multi_gpu or self.rank == 0: + self.save("crashed") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + self.time_report.end_timer("compute actor loss") + + return + + actor_lr, critic_lr = self.actor_lr, self.critic_lr + print("starting training: lr = {}, {}".format(actor_lr, critic_lr)) + start_epoch = self.curr_epoch + # main training process + for epoch in range(start_epoch, self.max_epochs): + self.curr_epoch += 1 + time_start_epoch = time.time() + + # learning rate schedule + if self.lr_schedule == "linear": + if self.rank == 0: + actor_lr, _ = self.scheduler.update(actor_lr, None, self.curr_epoch, None, None) + critic_lr, _ = self.scheduler.update(critic_lr, None, self.curr_epoch, None, None) + + if self.multi_gpu: + lr_tensor = torch.tensor([actor_lr, critic_lr], device=self.device) + dist.broadcast(lr_tensor, 0) + actor_lr = lr_tensor[0].item() + critic_lr = lr_tensor[1].item() + + for param_group in self.critic_optimizer.param_groups: + param_group["lr"] = critic_lr + + for param_group in self.actor_optimizer.param_groups: + param_group["lr"] = actor_lr + lr = actor_lr + elif self.is_adaptive_lr and len(self.ep_kls) > 0: + av_kls = torch_ext.mean_list(self.ep_kls) + if self.multi_gpu: + dist.all_reduce(av_kls, op=dist.ReduceOp.SUM) + av_kls /= self.rank_size + + self.actor_lr, self.entropy_coef = self.scheduler.update( + self.actor_lr, self.entropy_coef, self.epoch_num, 0, av_kls.item() + ) + self.critic_lr = self.actor_lr + self.ep_kls = [] + else: + lr = self.actor_lr + + # train actor + self.act_buf = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.time_report.start_timer("actor training") + actor_closure() + self.time_report.end_timer("actor training") + + # train critic + # prepare dataset + self.time_report.start_timer("prepare critic dataset") + with torch.no_grad(): + self.compute_target_values() + dataset = QCriticDataset( + self.batch_size, + self.obs_buf, + self.act_buf, + self.target_values, + drop_last=False, + ) + # compute KL divergence of the current policy + self.ep_kls.append( + torch_ext.policy_kl( + self.mus.detach(), + self.sigmas.detach(), + self.old_mus, + self.old_sigmas, + ) + ) + + self.time_report.end_timer("prepare critic dataset") + + self.time_report.start_timer("critic training") + self.value_loss = 0.0 + for j in range(self.critic_iterations): + total_critic_loss = 0.0 + batch_cnt = 0 + for i in range(len(dataset)): + batch_sample = dataset[i] + self.critic_optimizer.zero_grad(set_to_none=True) + with torch.cuda.amp.autocast(enabled=self.mixed_precision): + training_critic_loss = self.compute_critic_loss(batch_sample) + self.scaler.scale(training_critic_loss).backward() + + # ugly fix for simulation nan problem + for params in self.critic.parameters(): + params.grad.nan_to_num_(0.0, 0.0, 0.0) + + self.clip_gradients(self.critic.parameters(), self.critic_optimizer) + self.scaler.step(self.actor_optimizer) + self.scaler.update() + + total_critic_loss += training_critic_loss + batch_cnt += 1 + # recompute Q-target + self.compute_target_values() + + self.value_loss = (total_critic_loss / batch_cnt).detach().cpu().item() + print( + "value iter {}/{}, loss = {:7.6f}".format(j + 1, self.critic_iterations, self.value_loss), + end="\r", + ) + del self.act_buf + del dataset + + self.time_report.end_timer("critic training") + + self.iter_count += 1 + + time_end_epoch = time.time() + should_exit = False + + # update target critic + with torch.no_grad(): + alpha = self.target_critic_alpha + for param, param_targ in zip(self.critic.parameters(), self.target_critic.parameters()): + param_targ.data.mul_(alpha) + param_targ.data.add_((1.0 - alpha) * param.data) + + # skip logging if not the head node + if self.rank != 0 and self.multi_gpu: + continue + + time_elapse = time.time() - self.start_time + self.writer.add_scalar("lr/iter", lr, self.iter_count) + self.writer.add_scalar("actor_loss/step", self.actor_loss, self.step_count) + self.writer.add_scalar("actor_loss/iter", self.actor_loss, self.iter_count) + self.writer.add_scalar("value_loss/step", self.value_loss, self.step_count) + self.writer.add_scalar("value_loss/iter", self.value_loss, self.iter_count) + if len(self.episode_loss_his) > 0: + mean_episode_length = self.episode_length_meter.get_mean() + mean_policy_loss = self.episode_loss_meter.get_mean() + mean_policy_discounted_loss = self.episode_discounted_loss_meter.get_mean() + + if mean_policy_loss < self.best_policy_loss: + print_info("save best policy with loss {:.2f}".format(mean_policy_loss)) + self.save() + self.best_policy_loss = mean_policy_loss + self.best_policy_epoch = self.curr_epoch + # number of episodes with no improvement + else: + last_improved_ep = self.best_policy_epoch - self.curr_epoch + if last_improved_ep > self.early_stopping_patience: + should_exit = True + + self.writer.add_scalar("policy_loss/step", mean_policy_loss, self.step_count) + self.writer.add_scalar("policy_loss/time", mean_policy_loss, time_elapse) + self.writer.add_scalar("policy_loss/iter", mean_policy_loss, self.iter_count) + self.writer.add_scalar("rewards/step", -mean_policy_loss, self.step_count) + self.writer.add_scalar("rewards/time", -mean_policy_loss, time_elapse) + self.writer.add_scalar("rewards/iter", -mean_policy_loss, self.iter_count) + if self.score_keys and len(self.episode_scores_meter_map[self.score_keys[0] + "_final"]) > 0: + for score_key in self.score_keys: + score = self.episode_scores_meter_map[score_key + "_final"].get_mean() + self.writer.add_scalar("scores/{}/iter".format(score_key), score, self.iter_count) + self.writer.add_scalar("scores/{}/step".format(score_key), score, self.step_count) + self.writer.add_scalar("scores/{}/time".format(score_key), score, time_elapse) + self.writer.add_scalar( + "policy_discounted_loss/step", + mean_policy_discounted_loss, + self.step_count, + ) + self.writer.add_scalar( + "policy_discounted_loss/iter", + mean_policy_discounted_loss, + self.iter_count, + ) + + self.writer.add_scalar("best_policy_loss/step", self.best_policy_loss, self.step_count) + self.writer.add_scalar("best_policy_loss/iter", self.best_policy_loss, self.iter_count) + self.writer.add_scalar("episode_lengths/iter", mean_episode_length, self.iter_count) + self.writer.add_scalar("episode_lengths/step", mean_episode_length, self.step_count) + self.writer.add_scalar("episode_lengths/time", mean_episode_length, time_elapse) + ac_stddev = self.actor.get_logstd().exp().mean().detach().cpu().item() + self.writer.add_scalar("ac_std/iter", ac_stddev, self.iter_count) + self.writer.add_scalar("ac_std/step", ac_stddev, self.step_count) + self.writer.add_scalar("ac_std/time", ac_stddev, time_elapse) + else: + mean_policy_loss = np.inf + mean_policy_discounted_loss = np.inf + mean_episode_length = 0 + + self.writer.flush() + + print( + "iter {}: ep loss {:.2f}, ep discounted loss {:.2f}, ep len {:.1f}, fps total {:.2f}, value loss {:.2f}, grad norm before clip {:.2f}, grad norm after clip {:.2f}".format( + self.iter_count, + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + self.steps_num * self.num_envs * self.rank_size / (time_end_epoch - time_start_epoch), + self.value_loss, + self.grad_norm_before_clip, + self.grad_norm_after_clip, + ) + ) + if self.save_interval > 0 and (self.iter_count % self.save_interval == 0): + self.save(self.name + "policy_iter{}_reward{:.3f}".format(self.iter_count, -mean_policy_loss)) + + if should_exit: + break + + self.time_report.end_timer("algorithm") + + self.time_report.report() + + if self.rank == 0 or not self.multi_gpu: + self.save("final_policy") + # save reward/length history + self.episode_loss_his = np.array(self.episode_loss_his) + self.episode_discounted_loss_his = np.array(self.episode_discounted_loss_his) + self.episode_length_his = np.array(self.episode_length_his) + np.save( + open(os.path.join(self.log_dir, "episode_loss_his.npy"), "wb"), + self.episode_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_discounted_loss_his.npy"), "wb"), + self.episode_discounted_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_length_his.npy"), "wb"), + self.episode_length_his, + ) + + # evaluate the final policy's performance + self.run(self.num_envs) + self.close() + + if self.multi_gpu and should_exit: + dist.destroy_process_group() + + def clip_gradients(self, parameters, optimizer, unscale=True): + if self.multi_gpu: + # batch allreduce ops: see https://github.com/entity-neural-network/incubator/pull/220 + all_grads_list = [] + for param in parameters: + if param.grad is not None: + all_grads_list.append(param.grad.view(-1)) + all_grads = torch.cat(all_grads_list) + dist.all_reduce(all_grads, op=dist.ReduceOp.SUM) + offset = 0 + for param in parameters: + if param.grad is not None: + param.grad.data.copy_( + all_grads[offset : offset + param.numel()].view_as(param.grad.data) / self.rank_size + ) + offset += param.numel() + + if self.truncate_grad: + if unscale: + self.scaler.unscale_(optimizer) + clip_grad_norm_(parameters, self.grad_norm) + + def play(self, cfg): + self.load(cfg.alg.params.general.checkpoint, cfg) + self.run(cfg.alg.params.config.player.games_num) + + def save(self, filename=None): + if filename is None: + filename = "best_policy" + torch.save( + { + "actor": self.actor.state_dict(), + "critic": self.critic.state_dict(), + "target_critic": self.target_critic.state_dict(), + "obs_rms": self._obs_rms, + "ret_rms": self.ret_rms, + "actor_optimizer": self.actor_optimizer.state_dict(), + "critic_optimizer": self.critic_optimizer.state_dict(), + "mus": self.mus, + "sigmas": self.sigmas, + "old_mus": self.old_mus, + "old_sigmas": self.old_sigmas, + }, + os.path.join(self.log_dir, "{}.pt".format(filename)), + ) + + def load(self, path, cfg, map_location=None): + checkpoint = torch.load(path, map_location=map_location) + self.actor.load_state_dict(checkpoint["actor"]) + self.critic.load_state_dict(checkpoint["critic"]) + + self.target_critic.load_state_dict(checkpoint["target_critic"]) + + if checkpoint["obs_rms"] is not None: + self._obs_rms = checkpoint["obs_rms"] + self._obs_rms = [x.to(self.device) for x in self._obs_rms] + else: + self._obs_rms = None + + self.ret_rms = checkpoint["ret_rms"].to(self.device) if checkpoint["ret_rms"] is not None else None + self.actor_optimizer.load_state_dict(checkpoint["actor_optimizer"]) + self.critic_optimizer.load_state_dict(checkpoint["critic_optimizer"]) + self.mus = checkpoint["mus"].to(self.device) + self.sigmas = checkpoint["sigmas"].to(self.device) + self.old_mus = checkpoint["old_mus"].to(self.device) + self.old_sigmas = checkpoint["old_sigmas"].to(self.device) + + def resume_from(self, path, cfg, epoch, step_count=None, loss=None): + self.curr_epoch = epoch + if loss: + self.best_policy_loss = loss + if step_count: + self.step_count = step_count + if self.multi_gpu: + ep_tensor = torch.tensor( + [self.curr_epoch, self.step_count, self.best_policy_loss], + device=self.device, + ) + dist.broadcast(ep_tensor, 0) + if self.rank != 0: + self.curr_epoch = int(ep_tensor[0].item()) + self.step_count = int(ep_tensor[1].item()) + self.best_policy_loss = ep_tensor[2].item() + self.load(path, cfg) + + def close(self): + self.writer.close() diff --git a/src/shac/algorithms/shac2_mpc.py b/src/shac/algorithms/shac2_mpc.py new file mode 100644 index 00000000..51787ce8 --- /dev/null +++ b/src/shac/algorithms/shac2_mpc.py @@ -0,0 +1,883 @@ +# Added option to resume training and loa Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import copy +import math +from hydra.utils import instantiate +from omegaconf import OmegaConf +import os +import sys +import time + +import yaml + +from rl_games.common import schedulers +from rl_games.algos_torch import torch_ext +from shac.utils.average_meter import AverageMeter +from shac.utils.common import * +from shac.utils.dataset import CriticDataset +from shac.utils.running_mean_std import RunningMeanStd +from shac.utils.time_report import TimeReport +import shac.utils.torch_utils as tu +from tensorboardX import SummaryWriter +import torch.distributed as dist +from torch.nn.utils.clip_grad import clip_grad_norm_ + +project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.append(project_dir) + + +class SHAC: + def __init__(self, cfg: dict): + seeding(cfg["general"]["seed"]) + if "diff_env" not in cfg.env.config: + self.env = instantiate(cfg.env.config) + else: + self.env = instantiate(cfg.env.config.diff_env) + + print("num_envs = ", self.env.num_envs) + print("num_actions = ", self.env.num_actions) + print("num_obs = ", self.env.num_obs) + + self.multi_gpu = cfg["alg"]["params"]["config"].get("multi_gpu", False) + self.rank = 0 + self.rank_size = 1 + + if self.multi_gpu: + self.rank = int(os.getenv("LOCAL_RANK", "0")) + self.rank_size = int(os.getenv("WORLD_SIZE", "1")) + dist.init_process_group("nccl", rank=self.rank, world_size=self.rank_size) + + self.device_name = "cuda:" + str(self.rank) + cfg["general"]["device"] = self.device_name + if self.rank != 0: + cfg["alg"]["params"]["config"]["print_stats"] = False + # cfg["params"]["config"]["lr_schedule"] = None + + self.num_envs = self.env.num_envs + self.num_obs = self.env.num_obs + self.num_actions = self.env.num_actions + self.max_episode_length = self.env.episode_length + self.device = cfg["general"]["device"] + + self.gamma = cfg["alg"]["params"]["config"].get("gamma", 0.99) + + self.critic_method = cfg["alg"]["params"]["config"].get( + "critic_method", "one-step" + ) # ['one-step', 'td-lambda'] + if self.critic_method == "td-lambda": + self.lam = cfg["alg"]["params"]["config"].get("lambda", 0.95) + + self.steps_num = cfg["alg"]["params"]["config"]["steps_num"] + self.max_epochs = cfg["alg"]["params"]["config"]["max_epochs"] + self.actor_lr = float(cfg["alg"]["params"]["default_actor_opt"]["lr"]) + self.critic_lr = float(cfg["alg"]["params"]["default_critic_opt"]["lr"]) + self.lr_schedule = cfg["alg"]["params"]["config"].get("lr_schedule", "linear") + + self.is_adaptive_lr = self.lr_schedule == "adaptive" + self.is_linear_lr = self.lr_schedule == "linear" + + if self.is_adaptive_lr: + self.scheduler = instantiate(cfg["alg"]["params"]["default_adaptive_scheduler"]) + elif self.is_linear_lr: + self.scheduler = instantiate(cfg["alg"]["params"]["default_linear_scheduler"]) + else: + self.scheduler = schedulers.IdentityScheduler() + + self.target_critic_alpha = cfg["alg"]["params"]["config"].get("target_critic_alpha", 0.4) + + self._obs_rms = None + self.curr_epoch = 0 + self.sub_traj_per_epoch = math.ceil(self.max_episode_length / self.steps_num) + # number of epochs of no improvement for early stopping + self.early_stopping_patience = cfg["alg"]["params"]["config"].get("early_stopping_patience", self.max_epochs) + if cfg["alg"]["params"]["config"].get("obs_rms", False): + # generate obs_rms for each subtrajectory + self._obs_rms = [ + RunningMeanStd(shape=(self.num_obs), device=self.device) + # for _ in range(self.sub_traj_per_epoch) + ] + + self.ret_rms = None + if cfg["alg"]["params"]["config"].get("ret_rms", False): + self.ret_rms = RunningMeanStd(shape=(), device=self.device) + + self.rew_scale = cfg["alg"]["params"]["config"].get("rew_scale", 1.0) + + self.critic_iterations = cfg["alg"]["params"]["config"].get("critic_iterations", 16) + self.num_batch = cfg["alg"]["params"]["config"].get("num_batch", 4) + self.batch_size = self.num_envs * self.steps_num // self.num_batch + self.name = cfg["alg"]["params"]["config"] + + self.truncate_grad = cfg["alg"]["params"]["config"]["truncate_grads"] + self.grad_norm = cfg["alg"]["params"]["config"]["grad_norm"] + + if cfg["general"]["train"]: + self.log_dir = cfg["general"]["logdir"] + if not self.multi_gpu or self.rank == 0: + os.makedirs(self.log_dir, exist_ok=True) + # save config + save_cfg = OmegaConf.to_yaml(cfg) + yaml.dump(save_cfg, open(os.path.join(self.log_dir, "cfg.yaml"), "w")) + self.writer = SummaryWriter(os.path.join(self.log_dir, "log")) + # save interval + self.save_interval = cfg["alg"]["params"]["config"].get("save_interval", 500) + # stochastic inference + self.stochastic_evaluation = True + else: + self.stochastic_evaluation = not ( + cfg["params"]["config"]["player"].get("determenistic", False) + or cfg["params"]["config"]["player"].get("deterministic", False) + ) + self.steps_num = self.env.episode_length + + # create actor critic network + self.actor = instantiate( + cfg["alg"]["params"]["network"]["actor"], + num_obs=self.num_obs, + num_actions=self.num_actions, + device=self.device, + ) + self.critic = instantiate( + cfg["alg"]["params"]["network"]["critic"], + num_obs=self.num_obs, + num_actions=self.num_actions, + device=self.device, + ) + self.all_params = list(self.actor.parameters()) + list(self.critic.parameters()) + self.target_critic = copy.deepcopy(self.critic) + + # initialize optimizer + self.actor_optimizer = instantiate( + cfg["alg"]["params"]["config"]["actor_optimizer"], params=self.actor.parameters() + ) + self.critic_optimizer = instantiate( + cfg["alg"]["params"]["config"]["critic_optimizer"], params=self.critic.parameters() + ) + + if cfg["general"]["train"]: + self.save("init_policy") + + self.mixed_precision = cfg["alg"]["params"]["config"].get("mixed_precision", False) + self.scaler = torch.cuda.amp.GradScaler(enabled=self.mixed_precision) + + # replay buffer + self.obs_buf = torch.zeros( + (self.steps_num, self.num_envs, self.num_obs), + dtype=torch.float32, + device=self.device, + ) + self.rew_buf = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.done_mask = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.next_values = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.target_values = torch.zeros((self.steps_num, self.num_envs), dtype=torch.float32, device=self.device) + self.ret = torch.zeros((self.num_envs), dtype=torch.float32, device=self.device) + + # for kl divergence computing + self.old_mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.old_sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.mus = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + self.sigmas = torch.zeros( + (self.steps_num, self.num_envs, self.num_actions), + dtype=torch.float32, + device=self.device, + ) + + # counting variables + self.iter_count = 0 + self.step_count = 0 + + # loss variables + self.episode_length_his = [] + self.episode_loss_his = [] + self.episode_discounted_loss_his = [] + self.episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_length = torch.zeros(self.num_envs, dtype=int) + self.best_policy_loss = np.inf + self.best_policy_epoch = 0 + self.actor_loss = np.inf + self.value_loss = np.inf + + # average meter + self.episode_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_discounted_loss_meter = AverageMeter(1, 100).to(self.device) + self.episode_length_meter = AverageMeter(1, 100).to(self.device) + self.score_keys = cfg["alg"]["params"]["config"].get("score_keys", []) + self.episode_scores_meter_map = { + key + "_final": AverageMeter(1, 100).to(self.device) for key in self.score_keys + } + + # timer + self.time_report = TimeReport() + + @property + def obs_rms(self): + if self._obs_rms is None: + return self._obs_rms + return self._obs_rms[0] # self.curr_epoch % self.sub_traj_per_epoch] + + def compute_actor_loss(self, deterministic=False): + rew_acc = torch.zeros((self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device) + gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + next_values = torch.zeros((self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device) + next_values_model_free = torch.zeros( + (self.steps_num + 1, self.num_envs), dtype=torch.float32, device=self.device + ) + + actor_loss = torch.tensor(0.0, dtype=torch.float32, device=self.device) + actor_model_free_loss = torch.tensor(0.0, dtype=torch.float32, device=self.device) + + with torch.no_grad(): + if self.obs_rms is not None: + obs_rms = copy.deepcopy(self.obs_rms) + + if self.ret_rms is not None: + ret_var = self.ret_rms.var.clone() + + # initialize trajectory to cut off gradients between episodes. + obs = self.env.initialize_trajectory() + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + for i in range(self.steps_num): + # collect data for critic training + with torch.no_grad(): + self.obs_buf[i] = obs.clone() + + actions = self.actor(obs, deterministic=deterministic) + self.mus[i, :], _, self.sigmas[i, :] = self.actor.forward_with_dist(obs, deterministic=False) + + obs, rew, done, extra_info = self.env.step(torch.tanh(actions)) + + with torch.no_grad(): + raw_rew = rew.clone() + + # scale the reward + rew = rew * self.rew_scale + + if self.obs_rms is not None: + # update obs rms + with torch.no_grad(): + self.obs_rms.update(obs) + # normalize the current obs + obs = obs_rms.normalize(obs) + + if self.ret_rms is not None: + # update ret rms + with torch.no_grad(): + self.ret = self.ret * self.gamma + rew + self.ret_rms.update(self.ret) + + rew = rew / torch.sqrt(ret_var + 1e-6) + + self.episode_length += 1 + + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + next_values[i + 1] = torch.minimum(self.critic(obs).squeeze(-1), self.target_critic(obs).squeeze(-1)) + next_values_model_free[i + 1] = torch.minimum( + self.critic(obs.requires_grad_(False)).squeeze(-1), + self.target_critic(obs.require_grad_(False)).squeeze(-1), + ) + + for id in done_env_ids: + if ( + torch.isnan(extra_info["obs_before_reset"][id]).sum() > 0 + or torch.isinf(extra_info["obs_before_reset"][id]).sum() > 0 + or (torch.abs(extra_info["obs_before_reset"][id]) > 1e6).sum() > 0 + ): # ugly fix for nan values + next_values[i + 1, id] = 0.0 + elif self.episode_length[id] < self.max_episode_length: # early termination + next_values[i + 1, id] = 0.0 + else: # otherwise, use terminal value critic to estimate the long-term performance + if self.obs_rms is not None: + real_obs = obs_rms.normalize(extra_info["obs_before_reset"][id]) + else: + real_obs = extra_info["obs_before_reset"][id] + next_values[i + 1, id] = torch.minimum( + self.critic(real_obs).squeeze(-1), + self.target_critic(real_obs).squeeze(-1), + ) + + if (next_values[i + 1] > 1e6).sum() > 0 or (next_values[i + 1] < -1e6).sum() > 0: + print("next value error") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + rew_acc[i + 1, :] = rew_acc[i, :] + gamma * rew + + if i < self.steps_num - 1: + actor_loss += ( + -rew_acc[i + 1, done_env_ids] - self.gamma * gamma[done_env_ids] * next_values[i + 1, done_env_ids] + ).sum() + actor_model_free_loss += ( + -rew_acc[i + 1, done_env_ids].detach() + - self.gamma * gamma[done_env_ids] * next_values_model_free[i + 1, done_env_ids] + ).sum() + else: + # terminate all envs at the end of optimization iteration + actor_loss += (-rew_acc[i + 1, :] - self.gamma * gamma * next_values[i + 1, :]).sum() + actor_model_free_loss += ( + -rew_acc[i + 1, :].detach() - self.gamma * gamma * next_values_model_free[i + 1, :] + ).sum() + + # compute gamma for next step + gamma = gamma * self.gamma + + # clear up gamma and rew_acc for done envs + gamma[done_env_ids] = 1.0 + rew_acc[i + 1, done_env_ids] = 0.0 + + # collect data for critic training + with torch.no_grad(): + self.rew_buf[i] = rew.clone() + if i < self.steps_num - 1: + self.done_mask[i] = done.clone().to(torch.float32) + else: + self.done_mask[i, :] = 1.0 + self.next_values[i] = next_values[i + 1].clone() + + # collect episode loss + with torch.no_grad(): + self.episode_loss -= raw_rew + self.episode_discounted_loss -= self.episode_gamma * raw_rew + self.episode_gamma *= self.gamma + if len(done_env_ids) > 0: + self.episode_loss_meter.update(self.episode_loss[done_env_ids]) + self.episode_discounted_loss_meter.update(self.episode_discounted_loss[done_env_ids]) + self.episode_length_meter.update(self.episode_length[done_env_ids]) + for k, v in filter(lambda x: x[0] in self.score_keys, extra_info.items()): + self.episode_scores_meter_map[k + "_final"].update(v[done_env_ids]) + for done_env_id in done_env_ids: + if self.episode_loss[done_env_id] > 1e6 or self.episode_loss[done_env_id] < -1e6: + print("ep loss error") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + self.episode_loss_his.append(self.episode_loss[done_env_id].item()) + self.episode_discounted_loss_his.append(self.episode_discounted_loss[done_env_id].item()) + self.episode_length_his.append(self.episode_length[done_env_id].item()) + self.episode_loss[done_env_id] = 0.0 + self.episode_discounted_loss[done_env_id] = 0.0 + self.episode_length[done_env_id] = 0 + self.episode_gamma[done_env_id] = 1.0 + + actor_loss /= self.steps_num * self.num_envs + actor_model_free_loss /= self.steps_num * self.num_envs + + if self.ret_rms is not None: + actor_loss = actor_loss * torch.sqrt(ret_var + 1e-6) + actor_model_free_loss = actor_model_free_loss * torch.sqrt(ret_var + 1e-6) + + self.actor_loss = actor_loss.detach().cpu().item() + self.actor_model_free_loss = actor_loss.detach().cpu().item() + + self.step_count += self.steps_num * self.num_envs + + return actor_loss, actor_model_free_loss + + @torch.no_grad() + def evaluate_policy(self, num_games, deterministic=False): + episode_length_his = [] + episode_loss_his = [] + episode_discounted_loss_his = [] + episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + episode_length = torch.zeros(self.num_envs, dtype=int) + episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + + obs = self.env.reset() + + games_cnt = 0 + while games_cnt < num_games: + if self.obs_rms is not None: + obs = self.obs_rms.normalize(obs) + + actions = self.actor(obs, deterministic=deterministic) + + obs, rew, done, _ = self.env.step(torch.tanh(actions)) + + episode_length += 1 + + done_env_ids = done.nonzero(as_tuple=False).squeeze(-1) + + episode_loss -= rew + episode_discounted_loss -= episode_gamma * rew + episode_gamma *= self.gamma + if len(done_env_ids) > 0: + for done_env_id in done_env_ids: + print( + "loss = {:.2f}, len = {}".format( + episode_loss[done_env_id].item(), + episode_length[done_env_id], + ) + ) + episode_loss_his.append(episode_loss[done_env_id].item()) + episode_discounted_loss_his.append(episode_discounted_loss[done_env_id].item()) + episode_length_his.append(episode_length[done_env_id].item()) + episode_loss[done_env_id] = 0.0 + episode_discounted_loss[done_env_id] = 0.0 + episode_length[done_env_id] = 0 + episode_gamma[done_env_id] = 1.0 + games_cnt += 1 + + mean_episode_length = np.mean(np.array(episode_length_his)) + mean_policy_loss = np.mean(np.array(episode_loss_his)) + mean_policy_discounted_loss = np.mean(np.array(episode_discounted_loss_his)) + + return mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + + @torch.no_grad() + def compute_target_values(self): + if self.critic_method == "one-step": + self.target_values = self.rew_buf + self.gamma * self.next_values + elif self.critic_method == "td-lambda": + Ai = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + Bi = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + lam = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + for i in reversed(range(self.steps_num)): + lam = lam * self.lam * (1.0 - self.done_mask[i]) + self.done_mask[i] + Ai = (1.0 - self.done_mask[i]) * ( + self.lam * self.gamma * Ai + + self.gamma * self.next_values[i] + + (1.0 - lam) / (1.0 - self.lam) * self.rew_buf[i] + ) + Bi = ( + self.gamma * (self.next_values[i] * self.done_mask[i] + Bi * (1.0 - self.done_mask[i])) + + self.rew_buf[i] + ) + self.target_values[i] = (1.0 - self.lam) * Ai + lam * Bi + else: + raise NotImplementedError + + def compute_critic_loss(self, batch_sample): + predicted_values = self.critic(batch_sample["obs"]).squeeze(-1) + target_values = batch_sample["target_values"] + critic_loss = ((predicted_values - target_values) ** 2).mean() + + return critic_loss + + def initialize_env(self): + self.env.clear_grad() + self.env.reset() + + @torch.no_grad() + def run(self, num_games): + ( + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + ) = self.evaluate_policy(num_games=num_games, deterministic=not self.stochastic_evaluation) + print_info( + "mean episode loss = {}, mean discounted loss = {}, mean episode length = {}".format( + mean_policy_loss, mean_policy_discounted_loss, mean_episode_length + ) + ) + + def train(self): + self.start_time = time.time() + + # add timers + self.time_report.add_timer("algorithm") + self.time_report.add_timer("compute actor loss") + self.time_report.add_timer("forward simulation") + self.time_report.add_timer("backward simulation") + self.time_report.add_timer("prepare critic dataset") + self.time_report.add_timer("actor training") + self.time_report.add_timer("critic training") + + self.time_report.start_timer("algorithm") + + # initializations + self.initialize_env() + if self.multi_gpu: + torch.cuda.set_device(self.rank) + if self.rank == 0: + print("====================broadcasting parameters") + print("====actor parameters") + actor_params = [self.actor.state_dict()] + dist.broadcast_object_list(actor_params, 0) + self.actor.load_state_dict(actor_params[0]) + if self.rank == 0: + print("====critic parameters") + critic_params = [self.critic.state_dict()] + dist.broadcast_object_list(critic_params, 0) + self.critic.load_state_dict(critic_params[0]) + if self.rank == 0: + print("done broadcasting parameters====================") + + self.episode_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_discounted_loss = torch.zeros(self.num_envs, dtype=torch.float32, device=self.device) + self.episode_length = torch.zeros(self.num_envs, dtype=int) + self.episode_gamma = torch.ones(self.num_envs, dtype=torch.float32, device=self.device) + + def actor_closure(): + self.actor_optimizer.zero_grad(set_to_none=True) + + self.time_report.start_timer("compute actor loss") + + self.time_report.start_timer("forward simulation") + # env_state = self.env.get_checkpoint() + # TODO: use autoscaling for mixed precision + + with torch.cuda.amp.autocast(enabled=self.mixed_precision): + actor_loss, actor_model_free_loss = self.compute_actor_loss() + self.time_report.end_timer("forward simulation") + + self.time_report.start_timer("backward simulation") + self.scaler.scale(actor_loss).backward() + self.time_report.end_timer("backward simulation") + + with torch.no_grad(): + self.scaler.unscale_(self.actor_optimizer) + self.grad_norm_before_clip = tu.grad_norm(self.actor.parameters()) + self.truncate_gradients_and_step(self.actor.parameters(), self.actor_optimizer, unscale=False) + self.grad_norm_after_clip = tu.grad_norm(self.actor.parameters()) + + # sanity check + if torch.isnan(self.grad_norm_before_clip) or self.grad_norm_before_clip > 1000000.0: + print("shac training crashed due to unstable gradient") + # torch.save(env_state, os.path.join(self.log_dir, "bad_state.pt")) + print("NaN gradient") + if not self.multi_gpu or self.rank == 0: + self.save("crashed") + if self.multi_gpu: + dist.destroy_process_group() + raise ValueError + + self.time_report.end_timer("compute actor loss") + + return actor_loss + + actor_lr, critic_lr = self.actor_lr, self.critic_lr + print("starting training: lr = {}, {}".format(actor_lr, critic_lr)) + start_epoch = self.curr_epoch + # main training process + for epoch in range(start_epoch, self.max_epochs): + self.curr_epoch += 1 + time_start_epoch = time.time() + + # learning rate schedule + if self.lr_schedule == "linear": + if self.rank == 0: + actor_lr, _ = self.scheduler.update(actor_lr, None, self.curr_epoch, None, None) + critic_lr, _ = self.scheduler.update(critic_lr, None, self.curr_epoch, None, None) + + if self.multi_gpu: + lr_tensor = torch.tensor([actor_lr, critic_lr], device=self.device) + dist.broadcast(lr_tensor, 0) + actor_lr = lr_tensor[0].item() + critic_lr = lr_tensor[1].item() + + for param_group in self.critic_optimizer.param_groups: + param_group["lr"] = critic_lr + + for param_group in self.actor_optimizer.param_groups: + param_group["lr"] = actor_lr + lr = actor_lr + elif self.lr_schedule == "adaptive": + av_kls = torch_ext.mean_list(ep_kls) + if self.multi_gpu: + dist.all_reduce(av_kls, op=dist.ReduceOp.SUM) + av_kls /= self.rank_size + kl_dist = torch_ext.policy_kl( + self.mus.detach(), + self.sigmas.detach(), + self.old_mus, + self.old_sigmas, + reduce=True, + ) + + else: + lr = self.actor_lr + + # train actor + self.time_report.start_timer("actor training") + actor_loss = actor_closure() + # self.truncate_gradients_and_step( + # self.actor.parameters(), self.actor_optimizer, unscale=False + # ) + # self.actor_optimizer.step(actor_closure).detach().item() + self.time_report.end_timer("actor training") + + # train critic + # prepare dataset + self.time_report.start_timer("prepare critic dataset") + with torch.no_grad(): + self.compute_target_values() + dataset = CriticDataset(self.batch_size, self.obs_buf, self.target_values, drop_last=False) + self.time_report.end_timer("prepare critic dataset") + + self.time_report.start_timer("critic training") + self.value_loss = 0.0 + for j in range(self.critic_iterations): + total_critic_loss = 0.0 + batch_cnt = 0 + for i in range(len(dataset)): + batch_sample = dataset[i] + self.critic_optimizer.zero_grad(set_to_none=True) + with torch.cuda.amp.autocast(enabled=self.mixed_precision): + training_critic_loss = self.compute_critic_loss(batch_sample) + self.scaler.scale(training_critic_loss).backward() + + # ugly fix for simulation nan problem + for params in self.critic.parameters(): + params.grad.nan_to_num_(0.0, 0.0, 0.0) + + self.truncate_gradients_and_step(self.critic.parameters(), self.critic_optimizer) + + total_critic_loss += training_critic_loss + batch_cnt += 1 + + self.value_loss = (total_critic_loss / batch_cnt).detach().cpu().item() + print( + "value iter {}/{}, loss = {:7.6f}".format(j + 1, self.critic_iterations, self.value_loss), + end="\r", + ) + + self.time_report.end_timer("critic training") + + self.iter_count += 1 + + time_end_epoch = time.time() + should_exit = False + + # update target critic + with torch.no_grad(): + alpha = self.target_critic_alpha + for param, param_targ in zip(self.critic.parameters(), self.target_critic.parameters()): + param_targ.data.mul_(alpha) + param_targ.data.add_((1.0 - alpha) * param.data) + + # skip logging if not the head node + if self.rank != 0 and self.multi_gpu: + continue + + time_elapse = time.time() - self.start_time + self.writer.add_scalar("lr/iter", lr, self.iter_count) + self.writer.add_scalar("actor_loss/step", self.actor_loss, self.step_count) + self.writer.add_scalar("actor_loss/iter", self.actor_loss, self.iter_count) + self.writer.add_scalar("value_loss/step", self.value_loss, self.step_count) + self.writer.add_scalar("value_loss/iter", self.value_loss, self.iter_count) + if len(self.episode_loss_his) > 0: + mean_episode_length = self.episode_length_meter.get_mean() + mean_policy_loss = self.episode_loss_meter.get_mean() + mean_policy_discounted_loss = self.episode_discounted_loss_meter.get_mean() + + if mean_policy_loss < self.best_policy_loss: + print_info("save best policy with loss {:.2f}".format(mean_policy_loss)) + self.save() + self.best_policy_loss = mean_policy_loss + self.best_policy_epoch = self.curr_epoch + # number of episodes with no improvement + else: + last_improved_ep = self.best_policy_epoch - self.curr_epoch + if last_improved_ep > self.early_stopping_patience: + should_exit = True + + self.writer.add_scalar("policy_loss/step", mean_policy_loss, self.step_count) + self.writer.add_scalar("policy_loss/time", mean_policy_loss, time_elapse) + self.writer.add_scalar("policy_loss/iter", mean_policy_loss, self.iter_count) + self.writer.add_scalar("rewards/step", -mean_policy_loss, self.step_count) + self.writer.add_scalar("rewards/time", -mean_policy_loss, time_elapse) + self.writer.add_scalar("rewards/iter", -mean_policy_loss, self.iter_count) + if self.score_keys and len(self.episode_scores_meter_map[self.score_keys[0] + "_final"]) > 0: + for score_key in self.score_keys: + score = self.episode_scores_meter_map[score_key + "_final"].get_mean() + self.writer.add_scalar("scores/{}/iter".format(score_key), score, self.iter_count) + self.writer.add_scalar("scores/{}/step".format(score_key), score, self.step_count) + self.writer.add_scalar("scores/{}/time".format(score_key), score, time_elapse) + self.writer.add_scalar( + "policy_discounted_loss/step", + mean_policy_discounted_loss, + self.step_count, + ) + self.writer.add_scalar( + "policy_discounted_loss/iter", + mean_policy_discounted_loss, + self.iter_count, + ) + self.writer.add_scalar("best_policy_loss/step", self.best_policy_loss, self.step_count) + self.writer.add_scalar("best_policy_loss/iter", self.best_policy_loss, self.iter_count) + self.writer.add_scalar("episode_lengths/iter", mean_episode_length, self.iter_count) + self.writer.add_scalar("episode_lengths/step", mean_episode_length, self.step_count) + self.writer.add_scalar("episode_lengths/time", mean_episode_length, time_elapse) + else: + mean_policy_loss = np.inf + mean_policy_discounted_loss = np.inf + mean_episode_length = 0 + + self.writer.flush() + + print( + "iter {}: ep loss {:.2f}, ep discounted loss {:.2f}, ep len {:.1f}, fps total {:.2f}, value loss {:.2f}, grad norm before clip {:.2f}, grad norm after clip {:.2f}".format( + self.iter_count, + mean_policy_loss, + mean_policy_discounted_loss, + mean_episode_length, + self.steps_num * self.num_envs * self.rank_size / (time_end_epoch - time_start_epoch), + self.value_loss, + self.grad_norm_before_clip, + self.grad_norm_after_clip, + ) + ) + if self.save_interval > 0 and (self.iter_count % self.save_interval == 0): + self.save(self.name + "policy_iter{}_reward{:.3f}".format(self.iter_count, -mean_policy_loss)) + + if should_exit: + break + + self.time_report.end_timer("algorithm") + + self.time_report.report() + + if self.rank == 0 or not self.multi_gpu: + self.save("final_policy") + # save reward/length history + self.episode_loss_his = np.array(self.episode_loss_his) + self.episode_discounted_loss_his = np.array(self.episode_discounted_loss_his) + self.episode_length_his = np.array(self.episode_length_his) + np.save( + open(os.path.join(self.log_dir, "episode_loss_his.npy"), "wb"), + self.episode_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_discounted_loss_his.npy"), "wb"), + self.episode_discounted_loss_his, + ) + np.save( + open(os.path.join(self.log_dir, "episode_length_his.npy"), "wb"), + self.episode_length_his, + ) + + # evaluate the final policy's performance + self.run(self.num_envs) + self.close() + + if self.multi_gpu and should_exit: + dist.destroy_process_group() + + def truncate_gradients_and_step(self, parameters, optimizer, unscale=True): + if self.multi_gpu: + # batch allreduce ops: see https://github.com/entity-neural-network/incubator/pull/220 + all_grads_list = [] + for param in parameters: + if param.grad is not None: + all_grads_list.append(param.grad.view(-1)) + all_grads = torch.cat(all_grads_list) + dist.all_reduce(all_grads, op=dist.ReduceOp.SUM) + offset = 0 + for param in parameters: + if param.grad is not None: + param.grad.data.copy_( + all_grads[offset : offset + param.numel()].view_as(param.grad.data) / self.rank_size + ) + offset += param.numel() + + if self.truncate_grad: + if unscale: + self.scaler.unscale_(optimizer) + clip_grad_norm_(parameters, self.grad_norm) + + self.scaler.step(optimizer) + self.scaler.update() + + def play(self, cfg): + self.load(cfg["params"]["general"]["checkpoint"], cfg) + self.run(cfg["params"]["config"]["player"]["games_num"]) + + def save(self, filename=None): + if filename is None: + filename = "best_policy" + torch.save( + [ + self.actor.state_dict(), + self.critic.state_dict(), + self.target_critic.state_dict(), + self._obs_rms, + self.ret_rms, + self.actor_optimizer.state_dict(), + self.critic_optimizer.state_dict(), + ], + os.path.join(self.log_dir, "{}.pt".format(filename)), + ) + + def load(self, path, cfg, map_location=None): + checkpoint = torch.load(path, map_location=map_location) + if isinstance(checkpoint[0], dict): + self.actor.load_state_dict(checkpoint[0]) + else: + self.actor = checkpoint[0].to(self.device) + if isinstance(checkpoint[1], dict): + self.critic.load_state_dict(checkpoint[1]) + else: + self.critic = checkpoint[1].to(self.device) + + if isinstance(checkpoint[2], dict): + self.target_critic.load_state_dict(checkpoint[2]) + else: + self.target_critic = checkpoint[2].to(self.device) + + if checkpoint[3]: + self._obs_rms = checkpoint[3] + self._obs_rms = [x.to(self.device) for x in self._obs_rms] + + self.ret_rms = checkpoint[4].to(self.device) if checkpoint[4] is not None else checkpoint[4] + self.actor_optimizer = torch.optim.Adam( + self.actor.parameters(), + betas=cfg["params"]["config"]["betas"], + lr=self.actor_lr, + ) + self.critic_optimizer = torch.optim.Adam( + self.critic.parameters(), + betas=cfg["params"]["config"]["betas"], + lr=self.critic_lr, + ) + + if len(checkpoint) == 7: # backwards compatible with older checkpoints + self.actor_optimizer.load_state_dict(checkpoint[5]) + self.critic_optimizer.load_state_dict(checkpoint[6]) + + def resume_from(self, path, cfg, epoch, step_count=None, loss=None): + self.curr_epoch = epoch + if loss: + self.best_policy_loss = loss + if step_count: + self.step_count = step_count + if self.multi_gpu: + ep_tensor = torch.tensor( + [self.curr_epoch, self.step_count, self.best_policy_loss], + device=self.device, + ) + dist.broadcast(ep_tensor, 0) + if self.rank != 0: + self.curr_epoch = int(ep_tensor[0].item()) + self.step_count = int(ep_tensor[1].item()) + self.best_policy_loss = ep_tensor[2].item() + self.load(path, cfg) + + def close(self): + self.writer.close() diff --git a/src/shac/envs/__init__.py b/src/shac/envs/__init__.py index d6231904..083f761a 100644 --- a/src/shac/envs/__init__.py +++ b/src/shac/envs/__init__.py @@ -5,15 +5,18 @@ # distribution of this software and related documentation without an express # license agreement from NVIDIA CORPORATION is strictly prohibited. -# from .ant import AntEnv -# from .cartpole_swing_up import CartPoleSwingUpEnv -from .cartpole_swing_up_warp import CartPoleSwingUpWarpEnv +from .ant import AntEnv +from .cartpole_swing_up import CartPoleSwingUpEnv +from .cheetah import CheetahEnv +from .dflex_env import DFlexEnv +from .hopper import HopperEnv +from .humanoid import HumanoidEnv +from .snu_humanoid import SNUHumanoidEnv -# from .cheetah import CheetahEnv -# from .dflex_env import DFlexEnv -# from .hopper import HopperEnv -# from .humanoid import HumanoidEnv -# from .snu_humanoid import SNUHumanoidEnv # dmanip envs -from dmanip.envs import WarpEnv, ClawWarpEnv +try: + from dmanip.envs import WarpEnv, ClawWarpEnv, AllegroWarpEnv +except ImportError: + print("dmanip not found, skipping dmanip envs") + pass diff --git a/src/shac/envs/ant.py b/src/shac/envs/ant.py index ac024762..36a8528b 100644 --- a/src/shac/envs/ant.py +++ b/src/shac/envs/ant.py @@ -13,11 +13,12 @@ from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -25,20 +26,44 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class AntEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode_length=1000, no_grad=True, stochastic_init=False, MM_caching_frequency = 1, early_termination = True): + def __init__( + self, + render=False, + device="cuda:0", + num_envs=4096, + seed=0, + episode_length=1000, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=16, + early_termination=True, + contact_termination=False, + jacobians=False, + ): num_obs = 37 num_act = 8 - - super(AntEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + + super(AntEnv, self).__init__( + num_envs, + num_obs, + num_act, + episode_length, + MM_caching_frequency, + seed, + no_grad, + render, + device, + ) self.stochastic_init = stochastic_init self.early_termination = early_termination + self.contact_termination = contact_termination + self.jacobians = jacobians self.init_sim() @@ -48,10 +73,12 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode self.action_penalty = 0.0 self.joint_vel_obs_scaling = 0.1 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): - self.stage = Usd.Stage.CreateNew("outputs/" + "Ant_" + str(self.num_envs) + ".usd") + if self.visualize: + self.stage = Usd.Stage.CreateNew( + "outputs/" + "Ant_" + str(self.num_envs) + ".usd" + ) self.renderer = df.render.UsdRenderer(self.model, self.stage) self.renderer.draw_points = True @@ -62,7 +89,7 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1.0/60.0 + self.dt = 1.0 / 60.0 self.sim_substeps = 16 self.sim_dt = self.dt @@ -71,23 +98,35 @@ def init_sim(self): self.num_joint_q = 15 self.num_joint_qd = 14 - self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - - self.start_rot = df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi*0.5) - self.start_rotation = tu.to_torch(self.start_rot, device=self.device, requires_grad=False) + self.x_unit_tensor = tu.to_torch( + [1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.y_unit_tensor = tu.to_torch( + [0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.z_unit_tensor = tu.to_torch( + [0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + + self.start_rot = df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5) + self.start_rotation = tu.to_torch( + self.start_rot, device=self.device, requires_grad=False + ) # initialize some data used later on # todo - switch to z-up self.up_vec = self.y_unit_tensor.clone() self.heading_vec = self.x_unit_tensor.clone() - self.inv_start_rot = tu.quat_conjugate(self.start_rotation).repeat((self.num_envs, 1)) + self.inv_start_rot = tu.quat_conjugate(self.start_rotation).repeat( + (self.num_envs, 1) + ) self.basis_vec0 = self.heading_vec.clone() self.basis_vec1 = self.up_vec.clone() - self.targets = tu.to_torch([10000.0, 0.0, 0.0], device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.targets = tu.to_torch( + [10000.0, 0.0, 0.0], device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) self.start_pos = [] self.start_joint_q = [0.0, 1.0, 0.0, -1.0, 0.0, -1.0, 0.0, 1.0] @@ -96,58 +135,91 @@ def init_sim(self): if self.visualize: self.env_dist = 2.5 else: - self.env_dist = 0. # set to zero for training for numerical consistency + self.env_dist = 0.0 # set to zero for training for numerical consistency start_height = 0.75 - asset_folder = os.path.join(os.path.dirname(__file__), 'assets') + asset_folder = os.path.join(os.path.dirname(__file__), "assets") for i in range(self.num_environments): - lu.parse_mjcf(os.path.join(asset_folder, "ant.xml"), self.builder, + lu.parse_mjcf( + os.path.join(asset_folder, "ant.xml"), + self.builder, density=1000.0, stiffness=0.0, damping=1.0, - contact_ke=4.e+4, - contact_kd=1.e+4, - contact_kf=3.e+3, + contact_ke=4.0e4, + contact_kd=1.0e4, + contact_kf=3.0e3, contact_mu=0.75, - limit_ke=1.e+3, - limit_kd=1.e+1, - armature=0.05) + limit_ke=1.0e3, + limit_kd=1.0e1, + armature=0.05, + ) # base transform - start_pos_z = i*self.env_dist + start_pos_z = i * self.env_dist self.start_pos.append([0.0, start_height, start_pos_z]) - self.builder.joint_q[i*self.num_joint_q:i*self.num_joint_q + 3] = self.start_pos[-1] - self.builder.joint_q[i*self.num_joint_q + 3:i*self.num_joint_q + 7] = self.start_rot + self.builder.joint_q[ + i * self.num_joint_q : i * self.num_joint_q + 3 + ] = self.start_pos[-1] + self.builder.joint_q[ + i * self.num_joint_q + 3 : i * self.num_joint_q + 7 + ] = self.start_rot # set joint targets to rest pose in mjcf - self.builder.joint_q[i*self.num_joint_q + 7:i*self.num_joint_q + 15] = [0.0, 1.0, 0.0, -1.0, 0.0, -1.0, 0.0, 1.0] - self.builder.joint_target[i*self.num_joint_q + 7:i*self.num_joint_q + 15] = [0.0, 1.0, 0.0, -1.0, 0.0, -1.0, 0.0, 1.0] + self.builder.joint_q[ + i * self.num_joint_q + 7 : i * self.num_joint_q + 15 + ] = [ + 0.0, + 1.0, + 0.0, + -1.0, + 0.0, + -1.0, + 0.0, + 1.0, + ] + self.builder.joint_target[ + i * self.num_joint_q + 7 : i * self.num_joint_q + 15 + ] = [ + 0.0, + 1.0, + 0.0, + -1.0, + 0.0, + -1.0, + 0.0, + 1.0, + ] self.start_pos = tu.to_torch(self.start_pos, device=self.device) self.start_joint_q = tu.to_torch(self.start_joint_q, device=self.device) - self.start_joint_target = tu.to_torch(self.start_joint_target, device=self.device) + self.start_joint_target = tu.to_torch( + self.start_joint_target, device=self.device + ) # finalize model self.model = self.builder.finalize(self.device) self.model.ground = self.ground - self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype=torch.float32, device=self.device) + self.model.gravity = torch.tensor( + (0.0, -9.81, 0.0), dtype=torch.float32, device=self.device + ) self.integrator = df.sim.SemiImplicitIntegrator() self.state = self.model.state() - if (self.model.ground): + if self.model.ground: self.model.collide(self.state) - def render(self, mode = 'human'): + def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.renderer.update(self.state, self.render_time) render_interval = 1 - if (self.num_frames == render_interval): + if self.num_frames == render_interval: try: self.stage.Save() except: @@ -155,46 +227,97 @@ def render(self, mode = 'human'): self.num_frames -= render_interval - def step(self, actions): + def step(self, actions, play=False): actions = actions.view((self.num_envs, self.num_actions)) - actions = torch.clip(actions, -1., 1.) - + actions = torch.clip(actions, -1.0, 1.0) + unscaled_actions = actions * self.action_strength self.actions = actions.clone() - self.state.joint_act.view(self.num_envs, -1)[:, 6:] = actions * self.action_strength - - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) + self.state.joint_act.view(self.num_envs, -1)[:, 6:] = unscaled_actions + + next_state = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + ) + + # TODO this should be done conditionally + contacts_changed = next_state.body_f_s.clone().any( + dim=1 + ) != self.state.body_f_s.clone().any(dim=1) + contacts_changed = contacts_changed.view(self.num_envs, -1).any(dim=1) + # body_f_s = next_state.body_f_s.clone().view(self.num_envs, self.num_joint_q, -1) + # num_contacts = (body_f_s.abs() > 1e-1).any(dim=-1).any(dim=-1) + + # compute dynamics jacobians if requested + if self.jacobians and not play: + inputs = torch.cat((self.obs_buf.clone(), unscaled_actions.clone()), dim=1) + inputs.requires_grad_(True) + last_obs = inputs[:, : self.num_obs] + act = inputs[:, self.num_obs :] + self.setStateAct(last_obs, act) + output = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + False, + ) + outputs = self.ObservationFromState(output) + # TODO why are there no jacobians for indices 11..? + # Possibly something wrong with the jacobian computation + jac = tu.jacobian2(outputs, inputs, max_out_dim=11) + + self.state = next_state self.sim_time += self.sim_dt - self.reset_buf = torch.zeros_like(self.reset_buf) - self.progress_buf += 1 self.num_frames += 1 self.calculateObservations() + # TODO: why is reward calculated on the next state? self.calculateReward() - env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) + # Reset environments if exseeded horizon + truncation = self.progress_buf > self.episode_length - 1 + # Reset environments if agent has ended in a bad state based on heuristics + termination = torch.zeros_like(truncation) + if self.early_termination: + termination = self.obs_buf[:, 0] < self.termination_height + + extras = None if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() - self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + extras = { + "obs_before_reset": self.obs_buf_before_reset, + "episode_end": self.termination_buf, + "contacts_changed": contacts_changed, + } + if self.jacobians and not play: + extras.update({"jacobian": jac.cpu().numpy()}) + + # reset all environments which have been terminated + self.reset_buf = termination | truncation + env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) if len(env_ids) > 0: - self.reset(env_ids) + self.reset(env_ids) self.render() - return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - - def reset(self, env_ids = None, force_reset = True): + return self.obs_buf, self.rew_buf, termination, truncation, extras + + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: - env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) + env_ids = torch.arange( + self.num_envs, dtype=torch.long, device=self.device + ) if env_ids is not None: # clone the state to avoid gradient error @@ -202,55 +325,86 @@ def reset(self, env_ids = None, force_reset = True): self.state.joint_qd = self.state.joint_qd.clone() # fixed start state - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.start_pos[env_ids, :].clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = self.start_rotation.clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = self.start_joint_q.clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0. + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.start_pos[ + env_ids, : + ].clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 3:7 + ] = self.start_rotation.clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 7: + ] = self.start_joint_q.clone() + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.0 # randomization if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + 0.1 * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) * 2. - angle = (torch.rand(len(env_ids), device = self.device) - 0.5) * np.pi / 12. - axis = torch.nn.functional.normalize(torch.rand((len(env_ids), 3), device = self.device) - 0.5) - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul(self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], tu.quat_from_angle_axis(angle, axis)) - self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] + 0.2 * (torch.rand(size=(len(env_ids), self.num_joint_q - 7), device = self.device) - 0.5) * 2. - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * (torch.rand(size=(len(env_ids), 14), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + + 0.1 + * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) + * 2.0 + ) + angle = ( + (torch.rand(len(env_ids), device=self.device) - 0.5) * np.pi / 12.0 + ) + axis = torch.nn.functional.normalize( + torch.rand((len(env_ids), 3), device=self.device) - 0.5 + ) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], + tu.quat_from_angle_axis(angle, axis), + ) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] + + 0.2 + * ( + torch.rand( + size=(len(env_ids), self.num_joint_q - 7), + device=self.device, + ) + - 0.5 + ) + * 2.0 + ) + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * ( + torch.rand(size=(len(env_ids), 14), device=self.device) - 0.5 + ) # clear action self.actions = self.actions.clone() - self.actions[env_ids, :] = torch.zeros((len(env_ids), self.num_actions), device = self.device, dtype = torch.float) + self.actions[env_ids, :] = torch.zeros( + (len(env_ids), self.num_actions), device=self.device, dtype=torch.float + ) self.progress_buf[env_ids] = 0 self.calculateObservations() return self.obs_buf - - ''' - cut off the gradient from the current state to previous states - ''' - def clear_grad(self, checkpoint = None): + + def clear_grad(self, checkpoint=None): + """cut off the gradient from the current state to previous states""" + self.contact_count = self.model.contact_count + __import__("ipdb").set_trace() with torch.no_grad(): if checkpoint is None: checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() - current_joint_q = checkpoint['joint_q'].clone() - current_joint_qd = checkpoint['joint_qd'].clone() self.state = self.model.state() - self.state.joint_q = current_joint_q - self.state.joint_qd = current_joint_qd - self.actions = checkpoint['actions'].clone() - self.progress_buf = checkpoint['progress_buf'].clone() - - ''' - This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. - It has to be called every time the algorithm starts an episode and it returns the observation vectors - ''' + self.state.joint_q = checkpoint["joint_q"] + self.state.joint_qd = checkpoint["joint_qd"] + self.actions = checkpoint["actions"] + self.progress_buf = checkpoint["progress_buf"] + def initialize_trajectory(self): + """ + This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. + It has to be called every time the algorithm starts an episode and it returns the observation vectors + """ self.clear_grad() self.calculateObservations() @@ -258,13 +412,26 @@ def initialize_trajectory(self): def get_checkpoint(self): checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() return checkpoint + def setStateAct(self, obs, act): + # torso position + self.state.joint_q.view(self.num_envs, -1)[:, 1] = obs[:, 0] + # torso rotation + self.state.joint_q.view(self.num_envs, -1)[:, 3:7] = obs[:, 1:5] + # linear velocity + self.state.joint_qd.view(self.num_envs, -1)[:, 3:6] = obs[:, 5:8] + # angular velocity + self.state.joint_qd.view(self.num_envs, -1)[:, 0:3] = obs[:, 8:11] + self.state.joint_q.view(self.num_envs, -1)[:, 7:] = obs[:, 11:19] + self.state.joint_qd.view(self.num_envs, -1)[:, 6:] = obs[:, 19:27] + self.state.joint_act.view(self.num_envs, -1)[:, 6:] = act + def calculateObservations(self): torso_pos = self.state.joint_q.view(self.num_envs, -1)[:, 0:3] torso_rot = self.state.joint_q.view(self.num_envs, -1)[:, 3:7] @@ -272,27 +439,66 @@ def calculateObservations(self): ang_vel = self.state.joint_qd.view(self.num_envs, -1)[:, 0:3] # convert the linear velocity of the torso from twist representation to the velocity of the center of mass in world frame - lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim = -1) + lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim=-1) to_target = self.targets + self.start_pos - torso_pos to_target[:, 1] = 0.0 - + target_dirs = tu.normalize(to_target) torso_quat = tu.quat_mul(torso_rot, self.inv_start_rot) up_vec = tu.quat_rotate(torso_quat, self.basis_vec1) heading_vec = tu.quat_rotate(torso_quat, self.basis_vec0) - self.obs_buf = torch.cat([torso_pos[:, 1:2], # 0 - torso_rot, # 1:5 - lin_vel, # 5:8 - ang_vel, # 8:11 - self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:19 - self.joint_vel_obs_scaling * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 19:27 - up_vec[:, 1:2], # 27 - (heading_vec * target_dirs).sum(dim = -1).unsqueeze(-1), # 28 - self.actions.clone()], # 29:37 - dim = -1) + self.obs_buf = torch.cat( + [ + torso_pos[:, 1:2], # 0 + torso_rot, # 1:5 + lin_vel, # 5:8 + ang_vel, # 8:11 + self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:19 + self.joint_vel_obs_scaling + * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 19:27 + up_vec[:, 1:2], # 27 + (heading_vec * target_dirs).sum(dim=-1).unsqueeze(-1), # 28 + self.actions.clone(), # 29:37 + ], + dim=-1, + ) + + def ObservationFromState(self, state): + torso_pos = state.joint_q.view(self.num_envs, -1)[:, 0:3].clone() + torso_rot = state.joint_q.view(self.num_envs, -1)[:, 3:7].clone() + lin_vel = state.joint_qd.view(self.num_envs, -1)[:, 3:6].clone() + ang_vel = state.joint_qd.view(self.num_envs, -1)[:, 0:3].clone() + + # convert the linear velocity of the torso from twist representation to the velocity of the center of mass in world frame + lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim=-1) + + to_target = self.targets + self.start_pos - torso_pos + to_target[:, 1] = 0.0 + + target_dirs = tu.normalize(to_target) + torso_quat = tu.quat_mul(torso_rot, self.inv_start_rot) + + up_vec = tu.quat_rotate(torso_quat, self.basis_vec1) + heading_vec = tu.quat_rotate(torso_quat, self.basis_vec0) + + return torch.cat( + [ + torso_pos[:, 1:2], # 0 + torso_rot, # 1:5 + lin_vel, # 5:8 + ang_vel, # 8:11 + state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:19 + self.joint_vel_obs_scaling + * state.joint_qd.view(self.num_envs, -1)[:, 6:], # 19:27 + up_vec[:, 1:2], # 27 + (heading_vec * target_dirs).sum(dim=-1).unsqueeze(-1), # 28 + self.actions.clone(), # 29:37 + ], + dim=-1, + ) def calculateReward(self): up_reward = 0.1 * self.obs_buf[:, 27] @@ -301,9 +507,10 @@ def calculateReward(self): progress_reward = self.obs_buf[:, 5] - self.rew_buf = progress_reward + up_reward + heading_reward + height_reward + torch.sum(self.actions ** 2, dim = -1) * self.action_penalty - - # reset agents - if self.early_termination: - self.reset_buf = torch.where(self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf) - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) \ No newline at end of file + self.rew_buf = ( + progress_reward + + up_reward + + heading_reward + + height_reward + + torch.sum(self.actions**2, dim=-1) * self.action_penalty + ) diff --git a/src/shac/envs/assets/invertedcartpole.urdf b/src/shac/envs/assets/invertedcartpole.urdf new file mode 100644 index 00000000..6bf35e72 --- /dev/null +++ b/src/shac/envs/assets/invertedcartpole.urdf @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shac/envs/cartpole_swing_up.py b/src/shac/envs/cartpole_swing_up.py index a4e85f12..536b827b 100644 --- a/src/shac/envs/cartpole_swing_up.py +++ b/src/shac/envs/cartpole_swing_up.py @@ -13,11 +13,12 @@ from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -25,26 +26,56 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class CartPoleSwingUpEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=1024, seed=0, episode_length=240, no_grad=True, stochastic_init=False, MM_caching_frequency = 1, early_termination = False): - + """ " + The real state of the system is [x, x_dot, theta, theta_dot] + where x is position on slide and theta is angle of the pendulum. + theta=0 is pendulum pointing upwards + The observations are [x, x_dot, sin(theta), cos(theta), theta_dot] + The actions are [x_ddot] + """ + + def __init__( + self, + render=False, + device="cuda:0", + num_envs=1024, + seed=0, + episode_length=240, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=1, + early_termination=False, + start_state=[0.0, 0.0, 0.0, 0.0], + ): num_obs = 5 num_act = 1 - super(CartPoleSwingUpEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + super(CartPoleSwingUpEnv, self).__init__( + num_envs, + num_obs, + num_act, + episode_length, + MM_caching_frequency, + seed, + no_grad, + render, + device, + ) + + self.start_state = np.array(start_state) + assert self.start_state.shape[0] == 4, self.start_state self.stochastic_init = stochastic_init self.early_termination = early_termination - self.init_sim() # action parameters - self.action_strength = 1000. + self.action_strength = 1000.0 # loss related self.pole_angle_penalty = 1.0 @@ -55,11 +86,13 @@ def __init__(self, render=False, device='cuda:0', num_envs=1024, seed=0, episode self.cart_action_penalty = 0.0 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): - self.stage = Usd.Stage.CreateNew("outputs/" + "CartPoleSwingUp_" + str(self.num_envs) + ".usd") - + if self.visualize: + if stage_path is None: + stage_path = self.__class__.__name__ + filename = os.path.join("outputs", "{:}_{:}.usd".format(stage_path, self.num_envs)) + self.stage = Usd.Stage.CreateNew(filename) self.renderer = df.render.UsdRenderer(self.model, self.stage) self.renderer.draw_points = True self.renderer.draw_springs = True @@ -69,7 +102,7 @@ def __init__(self, render=False, device='cuda:0', num_envs=1024, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1. / 60. + self.dt = 1.0 / 60.0 self.sim_substeps = 4 self.sim_dt = self.dt @@ -81,19 +114,35 @@ def init_sim(self): self.num_joint_q = 2 self.num_joint_qd = 2 - asset_folder = os.path.join(os.path.dirname(__file__), 'assets') + asset_folder = os.path.join(os.path.dirname(__file__), "assets") + cartpole_filename = "cartpole.urdf" for i in range(self.num_environments): - lu.urdf_load(self.builder, - os.path.join(asset_folder, 'cartpole.urdf'), - df.transform((0.0, 2.5, 0.0 + self.env_dist * i), df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi*0.5)), - floating=False, - shape_kd=1e4, - limit_kd=1.) - self.builder.joint_q[i * self.num_joint_q + 1] = -math.pi - + lu.urdf_load( + self.builder, + os.path.join(asset_folder, cartpole_filename), + df.transform( + (0.0, 2.5, 0.0 + self.env_dist * i), + df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5), + ), + floating=False, + armature=0.1, + stiffness=0.0, + damping=0.0, + shape_ke=1e4, + shape_kd=1e4, + shape_kf=1e2, + shape_mu=0.5, + limit_ke=1e2, + limit_kd=1.0, + ) + self.builder.joint_q[i * self.num_joint_q] = self.start_state[0] + self.builder.joint_q[i * self.num_joint_q + 1] = self.start_state[1] + self.builder.joint_qd[i * self.num_joint_q] = self.start_state[2] + self.builder.joint_qd[i * self.num_joint_q + 1] = self.start_state[3] + self.model = self.builder.finalize(self.device) self.model.ground = False - self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype = torch.float, device = self.device) + self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype=torch.float, device=self.device) self.integrator = df.sim.SemiImplicitIntegrator() @@ -101,29 +150,35 @@ def init_sim(self): self.start_joint_q = self.state.joint_q.clone() self.start_joint_qd = self.state.joint_qd.clone() - def render(self, mode = 'human'): + def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.renderer.update(self.state, self.render_time) - if (self.num_frames == 40): + if self.num_frames == 40: try: self.stage.Save() except: - print('USD save error') + print("USD save error") self.num_frames -= 40 - + def step(self, actions): with df.ScopedTimer("simulate", active=False, detailed=False): actions = actions.view((self.num_envs, self.num_actions)) - - actions = torch.clip(actions, -1., 1.) + + actions = torch.clip(actions, -1.0, 1.0) self.actions = actions - + self.state.joint_act.view(self.num_envs, -1)[:, 0:1] = actions * self.action_strength - - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) + + self.state = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + ) self.sim_time += self.sim_dt - + self.reset_buf = torch.zeros_like(self.reset_buf) self.progress_buf += 1 @@ -135,25 +190,25 @@ def step(self, actions): if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + "obs_before_reset": self.obs_buf_before_reset, + "episode_end": self.termination_buf, + } env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) - #self.obs_buf_before_reset = self.obs_buf.clone() + # self.obs_buf_before_reset = self.obs_buf.clone() with df.ScopedTimer("reset", active=False, detailed=False): if len(env_ids) > 0: self.reset(env_ids) - + with df.ScopedTimer("render", active=False, detailed=False): self.render() - #self.extras = {'obs_before_reset': self.obs_buf_before_reset} - + # self.extras = {'obs_before_reset': self.obs_buf_before_reset} + return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: @@ -163,41 +218,47 @@ def reset(self, env_ids=None, force_reset=True): # fixed start state self.state.joint_q = self.state.joint_q.clone() self.state.joint_qd = self.state.joint_qd.clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = self.start_joint_q.view(-1, self.num_joint_q)[env_ids, :].clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = self.start_joint_qd.view(-1, self.num_joint_qd)[env_ids, :].clone() + self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = self.start_joint_q.view(-1, self.num_joint_q)[ + env_ids, : + ].clone() + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = self.start_joint_qd.view(-1, self.num_joint_qd)[ + env_ids, : + ].clone() if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = \ - self.state.joint_q.view(self.num_envs, -1)[env_ids, :] \ - + np.pi * (torch.rand(size=(len(env_ids), self.num_joint_q), device=self.device) - 0.5) - - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = \ - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] \ - + 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) - + self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = self.state.joint_q.view(self.num_envs, -1)[ + env_ids, : + ] + np.pi * (torch.rand(size=(len(env_ids), self.num_joint_q), device=self.device) - 0.5) + + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = self.state.joint_qd.view(self.num_envs, -1)[ + env_ids, : + ] + 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) + self.progress_buf[env_ids] = 0 self.calculateObservations() return self.obs_buf - ''' + """ cut off the gradient from the current state to previous states - ''' + """ + def clear_grad(self): - with torch.no_grad(): # TODO: check with Miles + with torch.no_grad(): # TODO: check with Miles current_joint_q = self.state.joint_q.clone() - current_joint_qd = self.state.joint_qd.clone() + current_joint_qd = self.state.joint_qd.clone() current_joint_act = self.state.joint_act.clone() self.state = self.model.state() self.state.joint_q = current_joint_q self.state.joint_qd = current_joint_qd self.state.joint_act = current_joint_act - ''' + """ This function starts collecting a new trajectory from the current states but cut off the computation graph to the previous states. It has to be called every time the algorithm starts an episode and return the observation vectors - ''' + """ + def initialize_trajectory(self): self.clear_grad() self.calculateObservations() @@ -205,24 +266,31 @@ def initialize_trajectory(self): def calculateObservations(self): x = self.state.joint_q.view(self.num_envs, -1)[:, 0:1] - theta = self.state.joint_q.view(self.num_envs, -1)[:, 1:2] + theta = self.state.joint_q.view(self.num_envs, -1)[:, 1:] xdot = self.state.joint_qd.view(self.num_envs, -1)[:, 0:1] - theta_dot = self.state.joint_qd.view(self.num_envs, -1)[:, 1:2] + theta_dot = self.state.joint_qd.view(self.num_envs, -1)[:, 1:] # observations: [x, xdot, sin(theta), cos(theta), theta_dot] - self.obs_buf = torch.cat([x, xdot, torch.sin(theta), torch.cos(theta), theta_dot], dim = -1) + self.obs_buf = torch.cat([x, xdot, torch.sin(theta), torch.cos(theta), theta_dot], dim=-1) def calculateReward(self): x = self.state.joint_q.view(self.num_envs, -1)[:, 0] theta = tu.normalize_angle(self.state.joint_q.view(self.num_envs, -1)[:, 1]) xdot = self.state.joint_qd.view(self.num_envs, -1)[:, 0] theta_dot = self.state.joint_qd.view(self.num_envs, -1)[:, 1] + pole_angle_penalty = -torch.pow(theta, 2.0) * self.pole_angle_penalty + + self.rew_buf = ( + pole_angle_penalty + - torch.pow(theta_dot, 2.0) * self.pole_velocity_penalty + - torch.pow(x, 2.0) * self.cart_position_penalty + - torch.pow(xdot, 2.0) * self.cart_velocity_penalty + - torch.sum(self.actions**2, dim=-1) * self.cart_action_penalty + ) - self.rew_buf = -torch.pow(theta, 2.) * self.pole_angle_penalty \ - - torch.pow(theta_dot, 2.) * self.pole_velocity_penalty \ - - torch.pow(x, 2.) * self.cart_position_penalty \ - - torch.pow(xdot, 2.) * self.cart_velocity_penalty \ - - torch.sum(self.actions ** 2, dim = -1) * self.cart_action_penalty - # reset agents - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) + self.reset_buf = torch.where( + self.progress_buf > self.episode_length - 1, + torch.ones_like(self.reset_buf), + self.reset_buf, + ) diff --git a/src/shac/envs/cartpole_swing_up_warp.py b/src/shac/envs/cartpole_swing_up_warp.py index 0188e145..4992f0cd 100644 --- a/src/shac/envs/cartpole_swing_up_warp.py +++ b/src/shac/envs/cartpole_swing_up_warp.py @@ -8,7 +8,6 @@ import math import os import sys - import torch from dmanip.envs import WarpEnv @@ -41,8 +40,8 @@ def __init__( no_grad=True, stochastic_init=False, early_termination=False, + inverted_pendulum=True, ): - num_obs = 5 num_act = 1 @@ -59,7 +58,7 @@ def __init__( ) self.early_termination = early_termination - + self.inverted_pendulum = inverted_pendulum self.init_sim() # action parameters @@ -90,21 +89,21 @@ def __init__( def init_sim(self): wp.init() self.dt = 1.0 / 60.0 - self.sim_substeps = 4 + self.sim_substeps = 32 self.sim_dt = self.dt - if self.visualize: - self.env_dist = 1.0 - else: - self.env_dist = 0.0 + self.env_dist = 1.0 - self.num_joint_q = 2 - self.num_joint_qd = 2 + self.num_joint_q = 2 + int(self.inverted_pendulum) + self.num_joint_qd = 2 + int(self.inverted_pendulum) asset_folder = os.path.join(os.path.dirname(__file__), "assets") + cartpole_filename = ( + "invertedcartpole.urdf" if self.inverted_pendulum else "cartpole.urdf" + ) self.articulation_builder = wp.sim.ModelBuilder() wp.sim.parse_urdf( - os.path.join(os.path.dirname(__file__), "assets/cartpole.urdf"), + os.path.join(asset_folder, cartpole_filename), self.articulation_builder, xform=wp.transform( np.array((0.0, 0.0, 0.0)), @@ -112,15 +111,15 @@ def init_sim(self): ), floating=False, density=0, - # armature=0.1, - # stiffness=0.0, - # damping=0.0, - # shape_ke=1.0e4, + armature=0.1, + stiffness=0.0, + damping=0.0, + shape_ke=1.0e4, shape_kd=1.0e4, - # shape_kf=1.0e4, - # shape_mu=1.0, - # limit_ke=100, - # limit_kd=1.0, + shape_kf=1.0e4, + shape_mu=1.0, + limit_ke=100, + limit_kd=1.0, ) self.builder = wp.sim.ModelBuilder() @@ -130,7 +129,7 @@ def init_sim(self): self.articulation_builder, xform=wp.transform( np.array((0.0, 2.5, self.env_dist * i)), - wp.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5), + wp.quat_from_axis_angle((1.0, 0.0, 0.0), 0), ), ) self.builder.joint_q[i * self.num_joint_q + 1] = -math.pi @@ -138,7 +137,9 @@ def init_sim(self): i * self.num_joint_q : (i + 1) * self.num_joint_q ] = [0.0, 0.0] - self.model = self.builder.finalize(str(self.device)) + self.model = self.builder.finalize( + str(self.device), requires_grad=self.requires_grad + ) self.model.ground = False self.model.joint_attach_ke = 10000.0 @@ -146,25 +147,29 @@ def init_sim(self): self.integrator = wp.sim.SemiImplicitIntegrator() - if not self.no_grad: - self.state = self.model.state(requires_grad=True) - self.model.joint_q.requires_grad = True - self.model.joint_qd.requires_grad = True - self.model.joint_act.requires_grad = True - else: - self.state = self.model.state(requires_grad=False) + self.state_0 = self.model.state(requires_grad=self.requires_grad) + self.model.joint_q.requires_grad = self.requires_grad + self.model.joint_qd.requires_grad = self.requires_grad + self.model.joint_act.requires_grad = self.requires_grad + + start_joint_q = wp.to_torch(self.model.joint_q).clone() + start_joint_qd = wp.to_torch(self.model.joint_qd).clone() + start_joint_act = wp.to_torch(self.model.joint_act).clone() - start_joint_q, start_joint_qd, start_joint_act = self.get_state(return_act=True) # only stores a single copy of the initial state - self.start_joint_q = start_joint_q.clone().view(self.num_envs, -1)[0] - self.start_joint_qd = start_joint_qd.clone().view(self.num_envs, -1)[0] - self.start_joint_act = start_joint_act.clone().view(self.num_envs, -1)[0] + self.start_joint_q = start_joint_q.view(self.num_envs, -1) + self.start_joint_qd = start_joint_qd.view(self.num_envs, -1) + self.start_joint_act = start_joint_act.view(self.num_envs, -1) + self.joint_q, self.joint_qd = start_joint_q, start_joint_qd + if self.requires_grad: + self.joint_q.requires_grad = True + self.joint_qd.requires_grad = True def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.stage.begin_frame(self.render_time) - self.stage.render(self.state) + self.stage.render(self.state_0) self.stage.end_frame() if self.num_frames == 40: self.stage.save() @@ -176,43 +181,40 @@ def step(self, actions): self.actions = actions.view(self.num_envs, -1) joint_act = self.action_strength * actions - requires_grad = not self.no_grad - if not self.no_grad: - body_q = wp.to_torch( - self.state.body_q - ) # does this cut off grad to prev timestep? - body_qd = wp.to_torch( - self.state.body_qd - ) # does this cut off grad to prev timestep? - body_q.requires_grad = requires_grad - body_qd.requires_grad = requires_grad + if self.requires_grad: + # does this cut off grad to prev timestep? + body_q = wp.to_torch(self.state_0.body_q) + body_qd = wp.to_torch(self.state_0.body_qd) + body_q.requires_grad = self.requires_grad + body_qd.requires_grad = self.requires_grad assert ( - self.model.body_q.requires_grad and self.state.body_q.requires_grad + self.model.body_q.requires_grad + and self.state_0.body_q.requires_grad ) - state_out = self.model.state(requires_grad=requires_grad) - self.joint_q, self.joint_qd, self.state = IntegratorSimulate.apply( + state_out = self.model.state(requires_grad=True) + self.joint_q, self.joint_qd, self.state_0 = IntegratorSimulate.apply( self.model, - self.state, + self.state_0, self.integrator, self.sim_dt, self.sim_substeps, - joint_act, + joint_act.flatten(), body_q, body_qd, state_out, ) else: for i in range(self.sim_substeps): - state_out = self.model.state(requires_grad=requires_grad) - self.state = self.integrator.simulate( + state_out = self.model.state(requires_grad=self.requires_grad) + self.state_0 = self.integrator.simulate( self.model, - self.state, + self.state_0, state_out, self.sim_dt / float(self.sim_substeps), ) joint_q = wp.zeros_like(self.model.joint_q) joint_qd = wp.zeros_like(self.model.joint_qd) - wp.sim.eval_ik(self.model, self.state, joint_q, joint_qd) + wp.sim.eval_ik(self.model, self.state_0, joint_q, joint_qd) self.joint_q, self.joint_qd = wp.to_torch(joint_q), wp.to_torch( joint_qd ) @@ -227,7 +229,7 @@ def step(self, actions): self.calculateObservations() self.calculateReward() - if self.no_grad == False: + if self.requires_grad: self.obs_buf_before_reset = self.obs_buf.clone() self.extras = { "obs_before_reset": self.obs_buf_before_reset, @@ -252,9 +254,7 @@ def get_stochastic_init(self, env_ids, joint_q, joint_qd): rand_init_qd = 0.5 * ( torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5 ) - joint_q[env_ids] += rand_init_q - joint_qd[env_ids] += rand_init_qd - return joint_q, joint_qd + return joint_q[env_ids] + rand_init_q, joint_qd[env_ids] + rand_init_qd def initialize_trajectory(self): """initialize_trajectory() starts collecting a new trajectory from the current states but cut off the computation graph to the previous states. @@ -264,17 +264,21 @@ def initialize_trajectory(self): self.calculateObservations() return self.obs_buf + def clear_grad(self, checkpoint=None): + super().clear_grad() + with torch.no_grad(): + self.actions = self.actions.clone() + if self.actions.grad is not None: + self.actions.grad.zero() + self.state_0 = self.model.state(requires_grad=self.requires_grad) + self.joint_q, self.joint_qd = self.joint_q.clone(), self.joint_qd.clone() + self.joint_q.requires_grad = self.requires_grad + self.joint_qd.requires_grad = self.requires_grad + # self.rew_buf.zero_() + if checkpoint is not None: + self.load_checkpoint(checkpoint) + def calculateObservations(self): - if self.joint_q is None: - self.joint_q, self.joint_qd = self.get_state() - if not self.no_grad: - self.joint_q.requires_grad = True - self.joint_qd.requires_grad = True - self.model.joint_act.requires_grad = True - self.model.body_q.requires_grad = True - self.model.body_qd.requires_grad = True - self.state.body_q.requires_grad = True - self.state.body_qd.requires_grad = True joint_q, joint_qd = self.joint_q.view(self.num_envs, -1), self.joint_qd.view( self.num_envs, -1 ) diff --git a/src/shac/envs/cheetah.py b/src/shac/envs/cheetah.py index c0e02b1d..c5da3087 100644 --- a/src/shac/envs/cheetah.py +++ b/src/shac/envs/cheetah.py @@ -13,11 +13,12 @@ from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -25,31 +26,57 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class CheetahEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode_length=1000, no_grad=True, stochastic_init=False, MM_caching_frequency = 1, early_termination = False): + def __init__( + self, + render=False, + device="cuda:0", + num_envs=4096, + seed=0, + episode_length=1000, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=16, + early_termination=True, + contact_termination=False, + jacobians=False, + ): num_obs = 17 num_act = 6 - - super(CheetahEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + + super(CheetahEnv, self).__init__( + num_envs, + num_obs, + num_act, + episode_length, + MM_caching_frequency, + seed, + no_grad, + render, + device, + ) self.stochastic_init = stochastic_init self.early_termination = early_termination + self.contact_termination = contact_termination + self.jacobians = jacobians self.init_sim() # other parameters self.action_strength = 200.0 - self.action_penalty = -0.1 + self.action_penalty = -1e-1 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): - self.stage = Usd.Stage.CreateNew("outputs/" + "Cheetah_" + str(self.num_envs) + ".usd") + if self.visualize: + self.stage = Usd.Stage.CreateNew( + "outputs/" + "Cheetah_" + str(self.num_envs) + ".usd" + ) self.renderer = df.render.UsdRenderer(self.model, self.stage) self.renderer.draw_points = True @@ -60,7 +87,7 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1.0/60.0 + self.dt = 1.0 / 60.0 self.sim_substeps = 16 self.sim_dt = self.dt @@ -69,75 +96,101 @@ def init_sim(self): self.num_joint_q = 9 self.num_joint_qd = 9 - self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.x_unit_tensor = tu.to_torch( + [1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.y_unit_tensor = tu.to_torch( + [0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.z_unit_tensor = tu.to_torch( + [0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) - self.start_rotation = torch.tensor([0.], device = self.device, requires_grad = False) + self.start_rotation = torch.tensor( + [0.0], device=self.device, requires_grad=False + ) # initialize some data used later on # todo - switch to z-up self.up_vec = self.y_unit_tensor.clone() - - self.potentials = tu.to_torch([0.], device=self.device, requires_grad=False).repeat(self.num_envs) - self.prev_potentials = self.potentials.clone() self.start_pos = [] - self.start_joint_q = [0., 0., 0., 0., 0., 0.] - self.start_joint_target = [0., 0., 0., 0., 0., 0.] + self.start_joint_q = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + self.start_joint_target = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] start_height = -0.2 - asset_folder = os.path.join(os.path.dirname(__file__), 'assets') + asset_folder = os.path.join(os.path.dirname(__file__), "assets") for i in range(self.num_environments): - link_start = len(self.builder.joint_type) - lu.parse_mjcf(os.path.join(asset_folder, "half_cheetah.xml"), self.builder, + lu.parse_mjcf( + os.path.join(asset_folder, "half_cheetah.xml"), + self.builder, density=1000.0, stiffness=0.0, damping=1.0, - contact_ke=2.e+4, - contact_kd=1.e+3, - contact_kf=1.e+3, - contact_mu=1., - limit_ke=1.e+3, - limit_kd=1.e+1, + contact_ke=2e4, + contact_kd=1e3, + contact_kf=1e3, + contact_mu=1.0, + limit_ke=1e3, + limit_kd=1e1, armature=0.1, - radians=True, load_stiffness=True) + radians=True, + load_stiffness=True, + ) - self.builder.joint_X_pj[link_start] = df.transform((0.0, 1.0, 0.0), df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi*0.5)) + self.builder.joint_X_pj[link_start] = df.transform( + (0.0, 1.0, 0.0), + df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5), + ) # base transform self.start_pos.append([0.0, start_height]) # set joint targets to rest pose in mjcf - self.builder.joint_q[i*self.num_joint_q + 3:i*self.num_joint_q + 9] = [0., 0., 0., 0., 0., 0.] - self.builder.joint_target[i*self.num_joint_q + 3:i*self.num_joint_q + 9] = [0., 0., 0., 0., 0., 0.] - + self.builder.joint_q[ + i * self.num_joint_q + 3 : i * self.num_joint_q + 9 + ] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + self.builder.joint_target[ + i * self.num_joint_q + 3 : i * self.num_joint_q + 9 + ] = [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ] + self.start_pos = tu.to_torch(self.start_pos, device=self.device) self.start_joint_q = tu.to_torch(self.start_joint_q, device=self.device) - self.start_joint_target = tu.to_torch(self.start_joint_target, device=self.device) + self.start_joint_target = tu.to_torch( + self.start_joint_target, device=self.device + ) # finalize model self.model = self.builder.finalize(self.device) self.model.ground = self.ground - self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype=torch.float32, device=self.device) + self.model.gravity = torch.tensor( + (0.0, -9.81, 0.0), dtype=torch.float32, device=self.device + ) self.integrator = df.sim.SemiImplicitIntegrator() self.state = self.model.state() - if (self.model.ground): + if self.model.ground: self.model.collide(self.state) - def render(self, mode = 'human'): + def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.renderer.update(self.state, self.render_time) render_interval = 1 - if (self.num_frames == render_interval): + if self.num_frames == render_interval: try: self.stage.Save() except: @@ -145,46 +198,100 @@ def render(self, mode = 'human'): self.num_frames -= render_interval - def step(self, actions): + def step(self, actions, play=False): actions = actions.view((self.num_envs, self.num_actions)) - actions = torch.clip(actions, -1., 1.) - + actions = torch.clip(actions, -1.0, 1.0) + unscaled_actions = actions * self.action_strength self.actions = actions.clone() - self.state.joint_act.view(self.num_envs, -1)[:, 3:] = actions * self.action_strength - - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) + self.state.joint_act.view(self.num_envs, -1)[:, 3:] = unscaled_actions + + next_state = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + ) + + # TODO this should be done conditionally + contacts_changed = next_state.body_f_s.clone().any( + dim=1 + ) != self.state.body_f_s.clone().any(dim=1) + contacts_changed = contacts_changed.view(self.num_envs, -1).any(dim=1) + body_f_s = next_state.body_f_s.clone().view(self.num_envs, self.num_joint_q, -1) + num_contacts = (body_f_s.abs() > 1e-1).any(dim=-1).any(dim=-1) + + # compute dynamics jacobians if requested + if self.jacobians and not play: + inputs = torch.cat((self.obs_buf.clone(), unscaled_actions.clone()), dim=1) + inputs.requires_grad_(True) + last_obs = inputs[:, : self.num_obs] + act = inputs[:, self.num_obs :] + self.setStateAct(last_obs, act) + output = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + False, + ) + outputs = torch.cat( + [ + output.joint_q.view(self.num_envs, -1)[:, 1:], + output.joint_qd.view(self.num_envs, -1), + ], + dim=-1, + ) + # TODO why are there no jacobians for indices 11..17 ? + jac = tu.jacobian2(outputs, inputs, max_out_dim=11) + + self.state = next_state self.sim_time += self.sim_dt - self.reset_buf = torch.zeros_like(self.reset_buf) - self.progress_buf += 1 self.num_frames += 1 - self.calculateObservations() self.calculateReward() + self.calculateObservations() - env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) + # Reset environments if exseeded horizon + # NOTE: this is truncation + truncation = self.progress_buf > self.episode_length - 1 + + # Reset environments if agent has ended in a bad state based on heuristics + # NOTE: this is termination + termination = torch.zeros_like(truncation) if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + "obs_before_reset": self.obs_buf_before_reset, + "episode_end": self.termination_buf, + "contacts_changed": contacts_changed, + } + + if self.jacobians and not play: + self.extras.update({"jacobian": jac.cpu().numpy()}) + # reset all environments which have been terminated + self.reset_buf = termination | truncation + env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) if len(env_ids) > 0: - self.reset(env_ids) + self.reset(env_ids) self.render() - return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - - def reset(self, env_ids = None, force_reset = True): + return self.obs_buf, self.rew_buf, termination, truncation, self.extras + + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: - env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) + env_ids = torch.arange( + self.num_envs, dtype=torch.long, device=self.device + ) if env_ids is not None: # clone the state to avoid gradient error @@ -192,52 +299,81 @@ def reset(self, env_ids = None, force_reset = True): self.state.joint_qd = self.state.joint_qd.clone() # fixed start state - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.start_pos[env_ids, :].clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = self.start_rotation.clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = self.start_joint_q.clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0. + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.start_pos[ + env_ids, : + ].clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 2 + ] = self.start_rotation.clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 3: + ] = self.start_joint_q.clone() + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.0 # randomization if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] + 0.1 * (torch.rand(size=(len(env_ids), 2), device=self.device) - 0.5) * 2. - self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = (torch.rand(len(env_ids), device = self.device) - 0.5) * 0.2 - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] + 0.1 * (torch.rand(size=(len(env_ids), self.num_joint_q - 3), device = self.device) - 0.5) * 2. - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] + + 0.1 + * (torch.rand(size=(len(env_ids), 2), device=self.device) - 0.5) + * 2.0 + ) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = ( + torch.rand(len(env_ids), device=self.device) - 0.5 + ) * 0.2 + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] + + 0.1 + * ( + torch.rand( + size=(len(env_ids), self.num_joint_q - 3), + device=self.device, + ) + - 0.5 + ) + * 2.0 + ) + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * ( + torch.rand( + size=(len(env_ids), self.num_joint_qd), device=self.device + ) + - 0.5 + ) # clear action self.actions = self.actions.clone() - self.actions[env_ids, :] = torch.zeros((len(env_ids), self.num_actions), device = self.device, dtype = torch.float) + self.actions[env_ids, :] = torch.zeros( + (len(env_ids), self.num_actions), device=self.device, dtype=torch.float + ) self.progress_buf[env_ids] = 0 - + self.calculateObservations() - + return self.obs_buf - - ''' - cut off the gradient from the current state to previous states - ''' - def clear_grad(self, checkpoint = None): + + def clear_grad(self, checkpoint=None): + """cut off the gradient from the current state to previous states""" + with torch.no_grad(): if checkpoint is None: checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() - current_joint_q = checkpoint['joint_q'].clone() - current_joint_qd = checkpoint['joint_qd'].clone() self.state = self.model.state() - self.state.joint_q = current_joint_q - self.state.joint_qd = current_joint_qd - self.actions = checkpoint['actions'].clone() - self.progress_buf = checkpoint['progress_buf'].clone() + self.state.joint_q = checkpoint["joint_q"] + self.state.joint_qd = checkpoint["joint_qd"] + self.actions = checkpoint["actions"] + self.progress_buf = checkpoint["progress_buf"] - ''' + """ This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. It has to be called every time the algorithm starts an episode and it returns the observation vectors - ''' + """ + def initialize_trajectory(self): self.clear_grad() self.calculateObservations() @@ -246,20 +382,31 @@ def initialize_trajectory(self): def get_checkpoint(self): checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() return checkpoint + def setStateAct(self, obs, act): + # self.state.joint_q.view(self.num_envs, -1)[:, 0:2] = TODO Don't need + self.state.joint_q.view(self.num_envs, -1)[:, 1:] = obs[:, :8] + self.state.joint_qd.view(self.num_envs, -1)[:, :] = obs[:, 8:] + self.state.joint_act.view(self.num_envs, -1)[:, 3:] = act + def calculateObservations(self): - self.obs_buf = torch.cat([self.state.joint_q.view(self.num_envs, -1)[:, 1:], self.state.joint_qd.view(self.num_envs, -1)], dim = -1) + self.obs_buf = torch.cat( + [ + self.state.joint_q.view(self.num_envs, -1)[:, 1:], + self.state.joint_qd.view(self.num_envs, -1), + ], + dim=-1, + ) def calculateReward(self): progress_reward = self.obs_buf[:, 8] - self.rew_buf = progress_reward + torch.sum(self.actions ** 2, dim = -1) * self.action_penalty - - # reset agents - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) \ No newline at end of file + self.rew_buf = ( + progress_reward + torch.sum(self.actions**2, dim=-1) * self.action_penalty + ) diff --git a/src/shac/envs/double_pendulum.py b/src/shac/envs/double_pendulum.py new file mode 100644 index 00000000..440d3ecb --- /dev/null +++ b/src/shac/envs/double_pendulum.py @@ -0,0 +1,278 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + +import math +import os +import sys + +import torch + +from .dflex_env import DFlexEnv + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + +import dflex as df + +import numpy as np + +np.set_printoptions(precision=5, linewidth=256, suppress=True) + +try: + from pxr import Usd +except ModuleNotFoundError: + print("No pxr package") + +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu + + +class DoublePendulumEnv(DFlexEnv): + def __init__( + self, + render=False, + device="cuda:0", + num_envs=1024, + seed=0, + episode_length=240, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=1, + early_termination=False, + ): + num_obs = 5 + 3 + num_act = 1 + + super(DoublePendulumEnv, self).__init__( + num_envs, + num_obs, + num_act, + episode_length, + MM_caching_frequency, + seed, + no_grad, + render, + device, + ) + + self.stochastic_init = stochastic_init + self.early_termination = early_termination + self.init_sim() + + # action parameters + self.action_strength = 1000.0 + + # loss related + self.pole_angle_penalty = 1.0 + self.pole_velocity_penalty = 0.1 + + self.cart_position_penalty = 0.05 + self.cart_velocity_penalty = 0.1 + + self.cart_action_penalty = 0.0 + + # ----------------------- + # set up Usd renderer + if self.visualize: + filename = os.path.join("outputs", "{:}_{:}.usd".format(self.__class__.__name__, self.num_envs)) + self.stage = Usd.Stage.CreateNew(filename) + self.renderer = df.render.UsdRenderer(self.model, self.stage) + self.renderer.draw_points = True + self.renderer.draw_springs = True + self.renderer.draw_shapes = True + self.render_time = 0.0 + + def init_sim(self): + self.builder = df.sim.ModelBuilder() + + self.dt = 1.0 / 60.0 + self.sim_substeps = 4 + self.sim_dt = self.dt + + if self.visualize: + self.env_dist = 1.0 + else: + self.env_dist = 0.0 + + self.num_joint_q = 3 + self.num_joint_qd = 3 + + asset_folder = os.path.join(os.path.dirname(__file__), "assets") + filename = "invertedcartpole.urdf" + for i in range(self.num_environments): + lu.urdf_load( + self.builder, + os.path.join(asset_folder, filename), + df.transform( + (0.0, 2.5, 0.0 + self.env_dist * i), + df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5), + ), + floating=False, + shape_kd=1e4, + limit_kd=1.0, + ) + + # set starting state + self.builder.joint_q[i * self.num_joint_q] = math.pi + self.builder.joint_q[i * self.num_joint_q + 1] = 0.0 + self.builder.joint_qd[i * self.num_joint_q] = 5.0 + self.builder.joint_qd[i * self.num_joint_q + 1] = -5.0 + + self.model = self.builder.finalize(self.device) + self.model.ground = False + self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype=torch.float, device=self.device) + + self.integrator = df.sim.SemiImplicitIntegrator() + + self.state = self.model.state() + self.start_joint_q = self.state.joint_q.clone() + self.start_joint_qd = self.state.joint_qd.clone() + + def render(self, mode="human"): + if self.visualize: + self.render_time += self.dt + self.renderer.update(self.state, self.render_time) + if self.num_frames == 40: + try: + self.stage.Save() + except: + print("USD save error") + self.num_frames -= 40 + + def step(self, actions): + with df.ScopedTimer("simulate", active=False, detailed=False): + actions = actions.view((self.num_envs, self.num_actions)) + + actions = torch.clip(actions, -1.0, 1.0) + self.actions = actions + + self.state.joint_act.view(self.num_envs, -1)[:, 0:1] = actions * self.action_strength + + self.state = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + ) + self.sim_time += self.sim_dt + + self.reset_buf = torch.zeros_like(self.reset_buf) + + self.progress_buf += 1 + self.num_frames += 1 + + self.calculateObservations() + self.calculateReward() + + if self.no_grad == False: + self.obs_buf_before_reset = self.obs_buf.clone() + self.extras = { + "obs_before_reset": self.obs_buf_before_reset, + "episode_end": self.termination_buf, + } + + env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) + + # self.obs_buf_before_reset = self.obs_buf.clone() + + with df.ScopedTimer("reset", active=False, detailed=False): + if len(env_ids) > 0: + self.reset(env_ids) + + with df.ScopedTimer("render", active=False, detailed=False): + self.render() + + # self.extras = {'obs_before_reset': self.obs_buf_before_reset} + + return self.obs_buf, self.rew_buf, self.reset_buf, self.extras + + def reset(self, env_ids=None, force_reset=True): + if env_ids is None: + if force_reset == True: + env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) + + if env_ids is not None: + # fixed start state + self.state.joint_q = self.state.joint_q.clone() + self.state.joint_qd = self.state.joint_qd.clone() + self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = self.start_joint_q.view(-1, self.num_joint_q)[ + env_ids, : + ].clone() + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = self.start_joint_qd.view(-1, self.num_joint_qd)[ + env_ids, : + ].clone() + + if self.stochastic_init: + self.state.joint_q.view(self.num_envs, -1)[env_ids, :] = self.state.joint_q.view(self.num_envs, -1)[ + env_ids, : + ] + np.pi * (torch.rand(size=(len(env_ids), self.num_joint_q), device=self.device) - 0.5) + + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = self.state.joint_qd.view(self.num_envs, -1)[ + env_ids, : + ] + 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) + + self.progress_buf[env_ids] = 0 + + self.calculateObservations() + + return self.obs_buf + + """ + cut off the gradient from the current state to previous states + """ + + def clear_grad(self): + with torch.no_grad(): # TODO: check with Miles + current_joint_q = self.state.joint_q.clone() + current_joint_qd = self.state.joint_qd.clone() + current_joint_act = self.state.joint_act.clone() + self.state = self.model.state() + self.state.joint_q = current_joint_q + self.state.joint_qd = current_joint_qd + self.state.joint_act = current_joint_act + + """ + This function starts collecting a new trajectory from the current states but cut off the computation graph to the previous states. + It has to be called every time the algorithm starts an episode and return the observation vectors + """ + + def initialize_trajectory(self): + self.clear_grad() + self.calculateObservations() + return self.obs_buf + + def calculateObservations(self): + x = self.state.joint_q.view(self.num_envs, -1)[:, 0:1] + theta = self.state.joint_q.view(self.num_envs, -1)[:, 1:] + xdot = self.state.joint_qd.view(self.num_envs, -1)[:, 0:1] + theta_dot = self.state.joint_qd.view(self.num_envs, -1)[:, 1:] + + # observations: [x, xdot, sin(theta), cos(theta), theta_dot] + self.obs_buf = torch.cat([x, xdot, torch.sin(theta), torch.cos(theta), theta_dot], dim=-1) + + def calculateReward(self): + x = self.state.joint_q.view(self.num_envs, -1)[:, 0] + theta = tu.normalize_angle(self.state.joint_q.view(self.num_envs, -1)[:, 1]) + xdot = self.state.joint_qd.view(self.num_envs, -1)[:, 0] + theta_dot = self.state.joint_qd.view(self.num_envs, -1)[:, 1] + tip_pos = self.state.body_X_sc.view(self.num_envs, -1, 7)[:, 3, 1] + pole_angle_penalty = -torch.pow(tip_pos - 2.5, 2.0) * self.pole_angle_penalty + + self.rew_buf = ( + pole_angle_penalty + - torch.pow(theta_dot, 2.0) * self.pole_velocity_penalty + - torch.pow(x, 2.0) * self.cart_position_penalty + - torch.pow(xdot, 2.0) * self.cart_velocity_penalty + - torch.sum(self.actions**2, dim=-1) * self.cart_action_penalty + ) + + # reset agents + self.reset_buf = torch.where( + self.progress_buf > self.episode_length - 1, + torch.ones_like(self.reset_buf), + self.reset_buf, + ) diff --git a/src/shac/envs/hopper.py b/src/shac/envs/hopper.py index 325f80fb..ea598b31 100644 --- a/src/shac/envs/hopper.py +++ b/src/shac/envs/hopper.py @@ -11,14 +11,14 @@ import torch -# from numpy.lib.function_base import angle from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -26,36 +26,62 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class HopperEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode_length=1000, no_grad=True, stochastic_init=False, MM_caching_frequency = 1, early_termination = True): + def __init__( + self, + render=False, + device="cuda:0", + num_envs=4096, + seed=0, + episode_length=1000, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=16, + early_termination=True, + contact_termination=False, + jacobians=False, + ): num_obs = 11 num_act = 3 - - super(HopperEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + + super(HopperEnv, self).__init__( + num_envs, + num_obs, + num_act, + episode_length, + MM_caching_frequency, + seed, + no_grad, + render, + device, + ) self.stochastic_init = stochastic_init self.early_termination = early_termination + self.contact_termination = contact_termination + self.jacobians = jacobians self.init_sim() # other parameters self.termination_height = -0.45 - self.termination_angle = np.pi / 6. + self.termination_angle = np.pi / 6.0 self.termination_height_tolerance = 0.15 self.termination_angle_tolerance = 0.05 self.height_rew_scale = 1.0 self.action_strength = 200.0 self.action_penalty = -1e-1 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): - self.stage = Usd.Stage.CreateNew("outputs/" + "Hopper_" + str(self.num_envs) + ".usd") + if self.visualize: + self.stage = Usd.Stage.CreateNew( + "outputs/" + "Hopper_" + str(self.num_envs) + ".usd" + ) self.renderer = df.render.UsdRenderer(self.model, self.stage) self.renderer.draw_points = True @@ -66,7 +92,7 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1.0/60.0 + self.dt = 1.0 / 60.0 self.sim_substeps = 16 self.sim_dt = self.dt @@ -75,72 +101,94 @@ def init_sim(self): self.num_joint_q = 6 self.num_joint_qd = 6 - self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.x_unit_tensor = tu.to_torch( + [1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.y_unit_tensor = tu.to_torch( + [0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) + self.z_unit_tensor = tu.to_torch( + [0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) - self.start_rotation = torch.tensor([0.], device = self.device, requires_grad = False) + self.start_rotation = torch.tensor( + [0.0], device=self.device, requires_grad=False + ) # initialize some data used later on # todo - switch to z-up self.up_vec = self.y_unit_tensor.clone() self.start_pos = [] - self.start_joint_q = [0., 0., 0.] - self.start_joint_target = [0., 0., 0.] + self.start_joint_q = [0.0, 0.0, 0.0] + self.start_joint_target = [0.0, 0.0, 0.0] start_height = 0.0 - asset_folder = os.path.join(os.path.dirname(__file__), 'assets') + asset_folder = os.path.join(os.path.dirname(__file__), "assets") for i in range(self.num_environments): - link_start = len(self.builder.joint_type) - lu.parse_mjcf(os.path.join(asset_folder, "hopper.xml"), self.builder, - density=1000.0, - stiffness=0.0, - damping=2.0, - contact_ke=2.e+4, - contact_kd=1.e+3, - contact_kf=1.e+3, + lu.parse_mjcf( + os.path.join(asset_folder, "hopper.xml"), + self.builder, + density=1000.0, # the way you calculate the mass of a body + stiffness=0.0, # don't do anything + damping=2.0, # don't do anything + contact_ke=2e4, # the higher the more stiff; proportional to kd and kf + contact_kd=1e3, + contact_kf=1e3, contact_mu=0.9, - limit_ke=1.e+3, - limit_kd=1.e+1, - armature=1.0, - radians=True, load_stiffness=True) - - self.builder.joint_X_pj[link_start] = df.transform((0.0, 0.0, 0.0), df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi*0.5)) + limit_ke=1e3, + limit_kd=1e1, + armature=1.0, # TODO; loosely related to how tight the joints are stuck together; don't touch + radians=True, + load_stiffness=True, # TODO + ) + + self.builder.joint_X_pj[link_start] = df.transform( + (0.0, 0.0, 0.0), + df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5), + ) # base transform self.start_pos.append([0.0, start_height]) # set joint targets to rest pose in mjcf - self.builder.joint_q[i*self.num_joint_q + 3:i*self.num_joint_q + 6] = [0., 0., 0.] - self.builder.joint_target[i*self.num_joint_q + 3:i*self.num_joint_q + 6] = [0., 0., 0., 0.] - + self.builder.joint_q[ + i * self.num_joint_q + 3 : i * self.num_joint_q + 6 + ] = [0.0, 0.0, 0.0] + self.builder.joint_target[ + i * self.num_joint_q + 3 : i * self.num_joint_q + 6 + ] = [0.0, 0.0, 0.0, 0.0] + self.start_pos = tu.to_torch(self.start_pos, device=self.device) self.start_joint_q = tu.to_torch(self.start_joint_q, device=self.device) - self.start_joint_target = tu.to_torch(self.start_joint_target, device=self.device) + self.start_joint_target = tu.to_torch( + self.start_joint_target, device=self.device + ) # finalize model self.model = self.builder.finalize(self.device) self.model.ground = self.ground - self.model.gravity = torch.tensor((0.0, -9.81, 0.0), dtype=torch.float32, device=self.device) + self.model.gravity = torch.tensor( + (0.0, -9.81, 0.0), dtype=torch.float32, device=self.device + ) self.integrator = df.sim.SemiImplicitIntegrator() self.state = self.model.state() - if (self.model.ground): + if self.model.ground: self.model.collide(self.state) - def render(self, mode = 'human'): + def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.renderer.update(self.state, self.render_time) render_interval = 1 - if (self.num_frames == render_interval): + if self.num_frames == render_interval: try: self.stage.Save() except: @@ -148,46 +196,101 @@ def render(self, mode = 'human'): self.num_frames -= render_interval - def step(self, actions): + def step(self, actions, play=False): actions = actions.view((self.num_envs, self.num_actions)) - actions = torch.clip(actions, -1., 1.) - + actions = torch.clip(actions, -1.0, 1.0) + unscaled_actions = actions * self.action_strength self.actions = actions.clone() - self.state.joint_act.view(self.num_envs, -1)[:, 3:] = actions * self.action_strength - - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) + self.state.joint_act.view(self.num_envs, -1)[:, 3:] = unscaled_actions + + next_state = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + ) + + # TODO this should be done conditionally + contacts_changed = next_state.body_f_s.clone().any( + dim=1 + ) != self.state.body_f_s.clone().any(dim=1) + contacts_changed = contacts_changed.view(self.num_envs, -1).any(dim=1) + body_f_s = next_state.body_f_s.clone().view(self.num_envs, self.num_joint_q, -1) + num_contacts = (body_f_s.abs() > 1e-1).any(dim=-1).any(dim=-1) + + # compute dynamics jacobians if requested + if self.jacobians and not play: + inputs = torch.cat((self.obs_buf.clone(), unscaled_actions.clone()), dim=1) + inputs.requires_grad_(True) + last_obs = inputs[:, : self.num_obs] + act = inputs[:, self.num_obs :] + self.setStateAct(last_obs, act) + output = self.integrator.forward( + self.model, + self.state, + self.sim_dt, + self.sim_substeps, + self.MM_caching_frequency, + False, + ) + outputs = torch.cat( + [ + output.joint_q.view(self.num_envs, -1)[:, 1:], + output.joint_qd.view(self.num_envs, -1), + ], + dim=-1, + ) + jac = tu.jacobian2(outputs, inputs) + + self.state = next_state self.sim_time += self.sim_dt - self.reset_buf = torch.zeros_like(self.reset_buf) - self.progress_buf += 1 self.num_frames += 1 - self.calculateObservations() self.calculateReward() + self.calculateObservations() - env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) + # Reset environments if exseeded horizon + # NOTE: this is truncation + truncation = self.progress_buf > self.episode_length - 1 + + # Reset environments if agent has ended in a bad state based on heuristics + # NOTE: this is termination + termination = torch.zeros_like(truncation) + if self.early_termination: + termination = self.obs_buf[:, 0] < self.termination_height if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + "obs_before_reset": self.obs_buf_before_reset, + "episode_end": self.termination_buf, + "contacts_changed": contacts_changed, + } + + if self.jacobians and not play: + self.extras.update({"jacobian": jac.cpu().numpy()}) + # reset all environments which have been terminated + self.reset_buf = termination | truncation + env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) if len(env_ids) > 0: - self.reset(env_ids) + self.reset(env_ids) self.render() - return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - - def reset(self, env_ids = None, force_reset = True): + return self.obs_buf, self.rew_buf, termination, truncation, self.extras + + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: - env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) + env_ids = torch.arange( + self.num_envs, dtype=torch.long, device=self.device + ) if env_ids is not None: # clone the state to avoid gradient error @@ -195,52 +298,117 @@ def reset(self, env_ids = None, force_reset = True): self.state.joint_qd = self.state.joint_qd.clone() # fixed start state - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.start_pos[env_ids, :].clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = self.start_rotation.clone() - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = self.start_joint_q.clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0. + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.start_pos[ + env_ids, : + ].clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 2 + ] = self.start_rotation.clone() + self.state.joint_q.view(self.num_envs, -1)[ + env_ids, 3: + ] = self.start_joint_q.clone() + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.0 # randomization if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] + 0.05 * (torch.rand(size=(len(env_ids), 2), device=self.device) - 0.5) * 2. - self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = (torch.rand(len(env_ids), device = self.device) - 0.5) * 0.1 - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] + 0.05 * (torch.rand(size=(len(env_ids), self.num_joint_q - 3), device = self.device) - 0.5) * 2. - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.05 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) * 2. + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:2] + + 0.05 + * (torch.rand(size=(len(env_ids), 2), device=self.device) - 0.5) + * 2.0 + ) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 2] = ( + torch.rand(len(env_ids), device=self.device) - 0.5 + ) * 0.1 + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:] + + 0.05 + * ( + torch.rand( + size=(len(env_ids), self.num_joint_q - 3), + device=self.device, + ) + - 0.5 + ) + * 2.0 + ) + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = ( + 0.05 + * ( + torch.rand( + size=(len(env_ids), self.num_joint_qd), device=self.device + ) + - 0.5 + ) + * 2.0 + ) # clear action self.actions = self.actions.clone() - self.actions[env_ids, :] = torch.zeros((len(env_ids), self.num_actions), device = self.device, dtype = torch.float) + self.actions[env_ids, :] = torch.zeros( + (len(env_ids), self.num_actions), device=self.device, dtype=torch.float + ) self.progress_buf[env_ids] = 0 - + self.calculateObservations() - + return self.obs_buf - - ''' - cut off the gradient from the current state to previous states - ''' - def clear_grad(self, checkpoint = None): + + def clear_grad(self, checkpoint=None): + """cut off the gradient from the current state to previous states""" + with torch.no_grad(): if checkpoint is None: checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() - current_joint_q = checkpoint['joint_q'].clone() - current_joint_qd = checkpoint['joint_qd'].clone() self.state = self.model.state() - self.state.joint_q = current_joint_q - self.state.joint_qd = current_joint_qd - self.actions = checkpoint['actions'].clone() - self.progress_buf = checkpoint['progress_buf'].clone() + self.state.joint_q = checkpoint["joint_q"] + self.state.joint_qd = checkpoint["joint_qd"] + self.actions = checkpoint["actions"] + self.progress_buf = checkpoint["progress_buf"] + + def clear_grad_ids(self, ids): + if len(ids) == 0: + return + + # need to preserve theg grads of non-cut trajectories + # init_joint_q = self.state.joint_q.clone() + # init_joint_qd = self.state.joint_qd.clone() + + # with torch.no_grad(): + # clone the state to avoid gradient error + # self.state = self.model.state() + + # self.state.joint_q = init_joint_q + # self.state.joint_qd = init_joint_qd - ''' + # clone the state to avoid gradient error + self.state.joint_q = self.state.joint_q.clone() + self.state.joint_qd = self.state.joint_qd.clone() + + with torch.no_grad(): + self.state.joint_q.view(self.num_envs, -1)[ids] = self.state.joint_q.view( + self.num_envs, -1 + )[ids].clone() + self.state.joint_qd.view(self.num_envs, -1)[ids] = self.state.joint_qd.view( + self.num_envs, -1 + )[ids].clone() + # self.actions[ids] = self.actions[ids].clone() + self.progress_buf[ids] = self.progress_buf[ids].clone() + + # recalculate observations so that grads don't propgate wrongly + self.calculateObservations() + + """ This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. It has to be called every time the algorithm starts an episode and it returns the observation vectors - ''' + """ + def initialize_trajectory(self): self.clear_grad() self.calculateObservations() @@ -249,29 +417,49 @@ def initialize_trajectory(self): def get_checkpoint(self): checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() return checkpoint + def setStateAct(self, obs, act): + # self.state.joint_q.view(self.num_envs, -1)[:, 0:2] = TODO Don't need + self.state.joint_q.view(self.num_envs, -1)[:, 1:] = obs[:, :5] + self.state.joint_qd.view(self.num_envs, -1)[:, :] = obs[:, 5:] + self.state.joint_act.view(self.num_envs, -1)[:, 3:] = act + def calculateObservations(self): - self.obs_buf = torch.cat([self.state.joint_q.view(self.num_envs, -1)[:, 1:], self.state.joint_qd.view(self.num_envs, -1)], dim = -1) + self.obs_buf = torch.cat( + [ + self.state.joint_q.view(self.num_envs, -1)[:, 1:], + self.state.joint_qd.view(self.num_envs, -1), + ], + dim=-1, + ) def calculateReward(self): - height_diff = self.obs_buf[:, 0] - (self.termination_height + self.termination_height_tolerance) + height_diff = self.obs_buf[:, 0] - ( + self.termination_height + self.termination_height_tolerance + ) height_reward = torch.clip(height_diff, -1.0, 0.3) - height_reward = torch.where(height_reward < 0.0, -200.0 * height_reward * height_reward, height_reward) - height_reward = torch.where(height_reward > 0.0, self.height_rew_scale * height_reward, height_reward) - - angle_reward = 1. * (-self.obs_buf[:, 1] ** 2 / (self.termination_angle ** 2) + 1.) + height_reward = torch.where( + height_reward < 0.0, -200.0 * height_reward * height_reward, height_reward + ) + height_reward = torch.where( + height_reward > 0.0, self.height_rew_scale * height_reward, height_reward + ) + + angle_reward = 1.0 * ( + -self.obs_buf[:, 1] ** 2 / (self.termination_angle**2) + 1.0 + ) progress_reward = self.obs_buf[:, 5] - self.rew_buf = progress_reward + height_reward + angle_reward + torch.sum(self.actions ** 2, dim = -1) * self.action_penalty - - # reset agents - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) - if self.early_termination: - self.reset_buf = torch.where(self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf) \ No newline at end of file + self.rew_buf = ( + progress_reward + + height_reward + + angle_reward + + torch.sum(self.actions**2, dim=-1) * self.action_penalty + ) diff --git a/src/shac/envs/humanoid.py b/src/shac/envs/humanoid.py index 36766758..54fc2b76 100644 --- a/src/shac/envs/humanoid.py +++ b/src/shac/envs/humanoid.py @@ -13,11 +13,12 @@ from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -25,17 +26,28 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class HumanoidEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode_length=1000, no_grad=True, stochastic_init=False, MM_caching_frequency = 1): + def __init__( + self, + render=False, + device="cuda:0", + num_envs=4096, + seed=0, + episode_length=1000, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=1, + ): num_obs = 76 num_act = 21 - super(HumanoidEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + super(HumanoidEnv, self).__init__( + num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device + ) self.stochastic_init = stochastic_init @@ -44,40 +56,43 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode # other parameters self.termination_height = 0.74 self.motor_strengths = [ - 200, - 200, - 200, - 200, - 200, - 600, - 400, - 100, - 100, - 200, - 200, - 600, - 400, - 100, + 200, + 200, + 200, + 200, + 200, + 600, + 400, + 100, + 100, + 200, + 200, + 600, + 400, + 100, + 100, 100, - 100, - 100, - 200, - 100, - 100, - 200] + 100, + 200, + 100, + 100, + 200, + ] self.motor_scale = 0.35 - self.motor_strengths = tu.to_torch(self.motor_strengths, dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.motor_strengths = tu.to_torch( + self.motor_strengths, dtype=torch.float, device=self.device, requires_grad=False + ).repeat((self.num_envs, 1)) self.action_penalty = -0.002 self.joint_vel_obs_scaling = 0.1 self.termination_tolerance = 0.1 self.height_rew_scale = 10.0 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): + if self.visualize: self.stage = Usd.Stage.CreateNew("outputs/" + "Humanoid_" + str(self.num_envs) + ".usd") self.renderer = df.render.UsdRenderer(self.model, self.stage) @@ -89,7 +104,7 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1.0/60.0 + self.dt = 1.0 / 60.0 self.sim_substeps = 48 self.sim_dt = self.dt @@ -98,11 +113,17 @@ def init_sim(self): self.num_joint_q = 28 self.num_joint_qd = 27 - self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - - self.start_rot = df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi*0.5) + self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + + self.start_rot = df.quat_from_axis_angle((1.0, 0.0, 0.0), -math.pi * 0.5) self.start_rotation = tu.to_torch(self.start_rot, device=self.device, requires_grad=False) # initialize some data used later on @@ -114,41 +135,46 @@ def init_sim(self): self.basis_vec0 = self.heading_vec.clone() self.basis_vec1 = self.up_vec.clone() - self.targets = tu.to_torch([200.0, 0.0, 0.0], device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.targets = tu.to_torch([200.0, 0.0, 0.0], device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) self.start_pos = [] if self.visualize: self.env_dist = 2.5 else: - self.env_dist = 0. # set to zero for training for numerical consistency + self.env_dist = 0.0 # set to zero for training for numerical consistency start_height = 1.35 - asset_folder = os.path.join(os.path.dirname(__file__), 'assets') + asset_folder = os.path.join(os.path.dirname(__file__), "assets") for i in range(self.num_environments): - lu.parse_mjcf(os.path.join(asset_folder, "humanoid.xml"), self.builder, + lu.parse_mjcf( + os.path.join(asset_folder, "humanoid.xml"), + self.builder, stiffness=5.0, damping=0.1, - contact_ke=2.e+4, - contact_kd=5.e+3, - contact_kf=1.e+3, + contact_ke=2.0e4, + contact_kd=5.0e3, + contact_kf=1.0e3, contact_mu=0.75, - limit_ke=1.e+3, - limit_kd=1.e+1, + limit_ke=1.0e3, + limit_kd=1.0e1, armature=0.007, load_stiffness=True, - load_armature=True) + load_armature=True, + ) # base transform - start_pos_z = i*self.env_dist + start_pos_z = i * self.env_dist self.start_pos.append([0.0, start_height, start_pos_z]) - self.builder.joint_q[i*self.num_joint_q:i*self.num_joint_q + 3] = self.start_pos[-1] - self.builder.joint_q[i*self.num_joint_q + 3:i*self.num_joint_q + 7] = self.start_rot + self.builder.joint_q[i * self.num_joint_q : i * self.num_joint_q + 3] = self.start_pos[-1] + self.builder.joint_q[i * self.num_joint_q + 3 : i * self.num_joint_q + 7] = self.start_rot - num_q = int(len(self.builder.joint_q)/self.num_environments) - num_qd = int(len(self.builder.joint_qd)/self.num_environments) + num_q = int(len(self.builder.joint_q) / self.num_environments) + num_qd = int(len(self.builder.joint_qd) / self.num_environments) print(num_q, num_qd) print("Start joint_q: ", self.builder.joint_q[0:num_q]) @@ -170,17 +196,17 @@ def init_sim(self): self.state = self.model.state() num_act = int(len(self.state.joint_act) / self.num_environments) - 6 - print('num_act = ', num_act) + print("num_act = ", num_act) - if (self.model.ground): + if self.model.ground: self.model.collide(self.state) - def render(self, mode = 'human'): + def render(self, mode="human"): if self.visualize: self.render_time += self.dt self.renderer.update(self.state, self.render_time) - if (self.num_frames == 1): + if self.num_frames == 1: try: self.stage.Save() except: @@ -192,14 +218,15 @@ def step(self, actions): actions = actions.view((self.num_envs, self.num_actions)) # todo - make clip range a parameter - actions = torch.clip(actions, -1., 1.) + actions = torch.clip(actions, -1.0, 1.0) ##### an ugly fix for simulation nan values #### # reference: https://github.com/pytorch/pytorch/issues/15131 def create_hook(): def hook(grad): - torch.nan_to_num(grad, 0.0, 0.0, 0.0, out = grad) + torch.nan_to_num(grad, 0.0, 0.0, 0.0, out=grad) + return hook - + if self.state.joint_q.requires_grad: self.state.joint_q.register_hook(create_hook()) if self.state.joint_qd.requires_grad: @@ -209,10 +236,12 @@ def hook(grad): ################################################# self.actions = actions.clone() - + self.state.joint_act.view(self.num_envs, -1)[:, 6:] = actions * self.motor_scale * self.motor_strengths - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) - + self.state = self.integrator.forward( + self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency + ) + self.sim_time += self.sim_dt self.reset_buf = torch.zeros_like(self.reset_buf) @@ -227,19 +256,16 @@ def hook(grad): if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() - self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + self.extras = {"obs_before_reset": self.obs_buf_before_reset, "episode_end": self.termination_buf} if len(env_ids) > 0: - self.reset(env_ids) + self.reset(env_ids) self.render() return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - - def reset(self, env_ids = None, force_reset = True): + + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) @@ -253,51 +279,65 @@ def reset(self, env_ids = None, force_reset = True): self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.start_pos[env_ids, :].clone() self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = self.start_rotation.clone() self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = self.start_joint_q.clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0. + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.0 # randomization if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + 0.1 * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) * 2. - angle = (torch.rand(len(env_ids), device = self.device) - 0.5) * np.pi / 12. - axis = torch.nn.functional.normalize(torch.rand((len(env_ids), 3), device = self.device) - 0.5) - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul(self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], tu.quat_from_angle_axis(angle, axis)) - self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] + 0.2 * (torch.rand(size=(len(env_ids), self.num_joint_q - 7), device = self.device) - 0.5) * 2. - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + + 0.1 * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) * 2.0 + ) + angle = (torch.rand(len(env_ids), device=self.device) - 0.5) * np.pi / 12.0 + axis = torch.nn.functional.normalize(torch.rand((len(env_ids), 3), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], tu.quat_from_angle_axis(angle, axis) + ) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] + + 0.2 * (torch.rand(size=(len(env_ids), self.num_joint_q - 7), device=self.device) - 0.5) * 2.0 + ) + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * ( + torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5 + ) # clear action self.actions = self.actions.clone() - self.actions[env_ids, :] = torch.zeros((len(env_ids), self.num_actions), device = self.device, dtype = torch.float) + self.actions[env_ids, :] = torch.zeros( + (len(env_ids), self.num_actions), device=self.device, dtype=torch.float + ) self.progress_buf[env_ids] = 0 self.calculateObservations() return self.obs_buf - - ''' + + """ cut off the gradient from the current state to previous states - ''' - def clear_grad(self, checkpoint = None): + """ + + def clear_grad(self, checkpoint=None): with torch.no_grad(): if checkpoint is None: checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() - current_joint_q = checkpoint['joint_q'].clone() - current_joint_qd = checkpoint['joint_qd'].clone() + current_joint_q = checkpoint["joint_q"].clone() + current_joint_qd = checkpoint["joint_qd"].clone() self.state = self.model.state() self.state.joint_q = current_joint_q self.state.joint_qd = current_joint_qd - self.actions = checkpoint['actions'].clone() - self.progress_buf = checkpoint['progress_buf'].clone() + self.actions = checkpoint["actions"].clone() + self.progress_buf = checkpoint["progress_buf"].clone() - ''' + """ This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. It has to be called every time the algorithm starts an episode and it returns the observation vectors - ''' + """ + def initialize_trajectory(self): self.clear_grad() self.calculateObservations() @@ -306,10 +346,10 @@ def initialize_trajectory(self): def get_checkpoint(self): checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() return checkpoint @@ -320,27 +360,31 @@ def calculateObservations(self): ang_vel = self.state.joint_qd.view(self.num_envs, -1)[:, 0:3] # convert the linear velocity of the torso from twist representation to the velocity of the center of mass in world frame - lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim = -1) + lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim=-1) to_target = self.targets + self.start_pos - torso_pos to_target[:, 1] = 0.0 - + target_dirs = tu.normalize(to_target) torso_quat = tu.quat_mul(torso_rot, self.inv_start_rot) up_vec = tu.quat_rotate(torso_quat, self.basis_vec1) heading_vec = tu.quat_rotate(torso_quat, self.basis_vec0) - self.obs_buf = torch.cat([torso_pos[:, 1:2], # 0 - torso_rot, # 1:5 - lin_vel, # 5:8 - ang_vel, # 8:11 - self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:32 - self.joint_vel_obs_scaling * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 32:53 - up_vec[:, 1:2], # 53:54 - (heading_vec * target_dirs).sum(dim = -1).unsqueeze(-1), # 54:55 - self.actions.clone()], # 55:76 - dim = -1) + self.obs_buf = torch.cat( + [ + torso_pos[:, 1:2], # 0 + torso_rot, # 1:5 + lin_vel, # 5:8 + ang_vel, # 8:11 + self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:32 + self.joint_vel_obs_scaling * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 32:53 + up_vec[:, 1:2], # 53:54 + (heading_vec * target_dirs).sum(dim=-1).unsqueeze(-1), # 54:55 + self.actions.clone(), + ], # 55:76 + dim=-1, + ) def calculateReward(self): up_reward = 0.1 * self.obs_buf[:, 53] @@ -350,22 +394,46 @@ def calculateReward(self): height_reward = torch.clip(height_diff, -1.0, self.termination_tolerance) height_reward = torch.where(height_reward < 0.0, -200.0 * height_reward * height_reward, height_reward) height_reward = torch.where(height_reward > 0.0, self.height_rew_scale * height_reward, height_reward) - + progress_reward = self.obs_buf[:, 5] - self.rew_buf = progress_reward + up_reward + heading_reward + height_reward + torch.sum(self.actions ** 2, dim = -1) * self.action_penalty + self.rew_buf = ( + progress_reward + + up_reward + + heading_reward + + height_reward + + torch.sum(self.actions**2, dim=-1) * self.action_penalty + ) # reset agents - self.reset_buf = torch.where(self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf) - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) + self.reset_buf = torch.where( + self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf + ) + self.reset_buf = torch.where( + self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf + ) # an ugly fix for simulation nan values - nan_masks = torch.logical_or(torch.isnan(self.obs_buf).sum(-1) > 0, torch.logical_or(torch.isnan(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, torch.isnan(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0)) - inf_masks = torch.logical_or(torch.isinf(self.obs_buf).sum(-1) > 0, torch.logical_or(torch.isinf(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, torch.isinf(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0)) - invalid_value_masks = torch.logical_or((torch.abs(self.state.joint_q.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, - (torch.abs(self.state.joint_qd.view(self.num_environments, -1)) > 1e6).sum(-1) > 0) + nan_masks = torch.logical_or( + torch.isnan(self.obs_buf).sum(-1) > 0, + torch.logical_or( + torch.isnan(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, + torch.isnan(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0, + ), + ) + inf_masks = torch.logical_or( + torch.isinf(self.obs_buf).sum(-1) > 0, + torch.logical_or( + torch.isinf(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, + torch.isinf(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0, + ), + ) + invalid_value_masks = torch.logical_or( + (torch.abs(self.state.joint_q.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, + (torch.abs(self.state.joint_qd.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, + ) invalid_masks = torch.logical_or(invalid_value_masks, torch.logical_or(nan_masks, inf_masks)) - + self.reset_buf = torch.where(invalid_masks, torch.ones_like(self.reset_buf), self.reset_buf) - - self.rew_buf[invalid_masks] = 0. \ No newline at end of file + + self.rew_buf[invalid_masks] = 0.0 diff --git a/src/shac/envs/snu_humanoid.py b/src/shac/envs/snu_humanoid.py index 264213cd..2d894a0e 100644 --- a/src/shac/envs/snu_humanoid.py +++ b/src/shac/envs/snu_humanoid.py @@ -13,11 +13,12 @@ from .dflex_env import DFlexEnv -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import dflex as df import numpy as np + np.set_printoptions(precision=5, linewidth=256, suppress=True) try: @@ -25,20 +26,40 @@ except ModuleNotFoundError: print("No pxr package") -from utils import load_utils as lu -from utils import torch_utils as tu +from shac.utils import load_utils as lu +from shac.utils import torch_utils as tu class SNUHumanoidEnv(DFlexEnv): - - def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode_length=1000, no_grad=True, stochastic_init=False, MM_caching_frequency = 1): - - self.filter = { "Pelvis", "FemurR", "TibiaR", "TalusR", "FootThumbR", "FootPinkyR", "FemurL", "TibiaL", "TalusL", "FootThumbL", "FootPinkyL"} + def __init__( + self, + render=False, + device="cuda:0", + num_envs=4096, + seed=0, + episode_length=1000, + no_grad=True, + stochastic_init=False, + MM_caching_frequency=1, + ): + self.filter = { + "Pelvis", + "FemurR", + "TibiaR", + "TalusR", + "FootThumbR", + "FootPinkyR", + "FemurL", + "TibiaL", + "TalusL", + "FootThumbL", + "FootPinkyL", + } self.skeletons = [] self.muscle_strengths = [] - self.mtu_actuations = True + self.mtu_actuations = True self.inv_control_freq = 1 @@ -46,21 +67,23 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode self.num_joint_q = 29 self.num_joint_qd = 24 - self.num_dof = self.num_joint_q - 7 # 22 + self.num_dof = self.num_joint_q - 7 # 22 self.num_muscles = 152 self.str_scale = 0.6 - num_act = self.num_joint_qd - 6 # 18 - num_obs = 71 # 13 + 22 + 18 + 18 + num_act = self.num_joint_qd - 6 # 18 + num_obs = 71 # 13 + 22 + 18 + 18 if self.mtu_actuations: - num_obs = 53 # 71 - 18 + num_obs = 53 # 71 - 18 if self.mtu_actuations: num_act = self.num_muscles - - super(SNUHumanoidEnv, self).__init__(num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device) + + super(SNUHumanoidEnv, self).__init__( + num_envs, num_obs, num_act, episode_length, MM_caching_frequency, seed, no_grad, render, device + ) self.stochastic_init = stochastic_init @@ -74,9 +97,9 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode self.action_penalty = -0.001 self.joint_vel_obs_scaling = 0.1 - #----------------------- + # ----------------------- # set up Usd renderer - if (self.visualize): + if self.visualize: self.stage = Usd.Stage.CreateNew("outputs/" + self.name + "HumanoidSNU_Low_" + str(self.num_envs) + ".usd") self.renderer = df.render.UsdRenderer(self.model, self.stage) @@ -88,18 +111,24 @@ def __init__(self, render=False, device='cuda:0', num_envs=4096, seed=0, episode def init_sim(self): self.builder = df.sim.ModelBuilder() - self.dt = 1.0/60.0 + self.dt = 1.0 / 60.0 self.sim_substeps = 48 self.sim_dt = self.dt self.ground = True - self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat((self.num_envs, 1)) - - self.start_rot = df.quat_from_axis_angle((0.0, 1.0, 0.0), math.pi*0.5) + self.x_unit_tensor = tu.to_torch([1, 0, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + self.y_unit_tensor = tu.to_torch([0, 1, 0], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + self.z_unit_tensor = tu.to_torch([0, 0, 1], dtype=torch.float, device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) + + self.start_rot = df.quat_from_axis_angle((0.0, 1.0, 0.0), math.pi * 0.5) self.start_rotation = tu.to_torch(self.start_rot, device=self.device, requires_grad=False) # initialize some data used later on @@ -111,59 +140,76 @@ def init_sim(self): self.basis_vec0 = self.heading_vec.clone() self.basis_vec1 = self.up_vec.clone() - self.targets = tu.to_torch([10000.0, 0.0, 0.0], device=self.device, requires_grad=False).repeat((self.num_envs, 1)) + self.targets = tu.to_torch([10000.0, 0.0, 0.0], device=self.device, requires_grad=False).repeat( + (self.num_envs, 1) + ) self.start_pos = [] if self.visualize: self.env_dist = 2.0 else: - self.env_dist = 0. # set to zero for training for numerical consistency + self.env_dist = 0.0 # set to zero for training for numerical consistency start_height = 1.0 - self.asset_folder = os.path.join(os.path.dirname(__file__), 'assets/snu') + self.asset_folder = os.path.join(os.path.dirname(__file__), "assets/snu") asset_path = os.path.join(self.asset_folder, "human.xml") muscle_path = os.path.join(self.asset_folder, "muscle284.xml") for i in range(self.num_environments): - if self.mtu_actuations: - skeleton = lu.Skeleton(asset_path, muscle_path, self.builder, self.filter, - stiffness=5.0, - damping=2.0, - contact_ke=5e3, - contact_kd=2e3, - contact_kf=1e3, - contact_mu=0.5, - limit_ke=1e3, - limit_kd=1e1, - armature=0.05) + skeleton = lu.Skeleton( + asset_path, + muscle_path, + self.builder, + self.filter, + stiffness=5.0, + damping=2.0, + contact_ke=5e3, + contact_kd=2e3, + contact_kf=1e3, + contact_mu=0.5, + limit_ke=1e3, + limit_kd=1e1, + armature=0.05, + ) else: - skeleton = lu.Skeleton(asset_path, None, self.builder, self.filter, - stiffness=5.0, - damping=2.0, - contact_ke=5e3, - contact_kd=2e3, - contact_kf=1e3, - contact_mu=0.5, - limit_ke=1e3, - limit_kd=1e1, - armature=0.05) + skeleton = lu.Skeleton( + asset_path, + None, + self.builder, + self.filter, + stiffness=5.0, + damping=2.0, + contact_ke=5e3, + contact_kd=2e3, + contact_kf=1e3, + contact_mu=0.5, + limit_ke=1e3, + limit_kd=1e1, + armature=0.05, + ) # set initial position 1m off the ground self.builder.joint_q[skeleton.coord_start + 2] = i * self.env_dist self.builder.joint_q[skeleton.coord_start + 1] = start_height - self.builder.joint_q[skeleton.coord_start + 3:skeleton.coord_start + 7] = self.start_rot + self.builder.joint_q[skeleton.coord_start + 3 : skeleton.coord_start + 7] = self.start_rot - self.start_pos.append([self.builder.joint_q[skeleton.coord_start], start_height, self.builder.joint_q[skeleton.coord_start + 2]]) + self.start_pos.append( + [ + self.builder.joint_q[skeleton.coord_start], + start_height, + self.builder.joint_q[skeleton.coord_start + 2], + ] + ) self.skeletons.append(skeleton) num_muscles = len(self.skeletons[0].muscles) - num_q = int(len(self.builder.joint_q)/self.num_environments) - num_qd = int(len(self.builder.joint_qd)/self.num_environments) + num_q = int(len(self.builder.joint_q) / self.num_environments) + num_qd = int(len(self.builder.joint_qd) / self.num_environments) print(num_q, num_qd) print("Start joint_q: ", self.builder.joint_q[0:num_q]) @@ -171,7 +217,7 @@ def init_sim(self): self.start_joint_q = self.builder.joint_q[7:num_q].copy() self.start_joint_target = self.start_joint_q.copy() - + for m in self.skeletons[0].muscles: self.muscle_strengths.append(self.str_scale * m.muscle_strength) @@ -179,7 +225,7 @@ def init_sim(self): self.muscle_strengths[mi] = self.str_scale * self.muscle_strengths[mi] self.muscle_strengths = tu.to_torch(self.muscle_strengths, device=self.device).repeat(self.num_envs) - + self.start_pos = tu.to_torch(self.start_pos, device=self.device) self.start_joint_q = tu.to_torch(self.start_joint_q, device=self.device) self.start_joint_target = tu.to_torch(self.start_joint_target, device=self.device) @@ -193,20 +239,17 @@ def init_sim(self): self.state = self.model.state() - if (self.model.ground): + if self.model.ground: self.model.collide(self.state) - def render(self, mode = 'human'): - + def render(self, mode="human"): if self.visualize: with torch.no_grad(): - muscle_start = 0 skel_index = 0 for s in self.skeletons: for mesh, link in s.mesh_map.items(): - if link != -1: X_sc = df.transform_expand(self.state.body_X_sc[link].tolist()) @@ -215,30 +258,34 @@ def render(self, mode = 'human'): self.renderer.add_mesh(mesh, mesh_path, X_sc, 1.0, self.render_time) for m in range(len(s.muscles)): - start = self.model.muscle_start[muscle_start + m].item() end = self.model.muscle_start[muscle_start + m + 1].item() points = [] for w in range(start, end): - link = self.model.muscle_links[w].item() point = self.model.muscle_points[w].cpu().numpy() X_sc = df.transform_expand(self.state.body_X_sc[link].cpu().tolist()) points.append(Gf.Vec3f(df.transform_point(X_sc, point).tolist())) - - self.renderer.add_line_strip(points, name=s.muscles[m].name + str(skel_index), radius=0.0075, color=(self.model.muscle_activation[muscle_start + m]/self.muscle_strengths[m], 0.2, 0.5), time=self.render_time) - + + self.renderer.add_line_strip( + points, + name=s.muscles[m].name + str(skel_index), + radius=0.0075, + color=(self.model.muscle_activation[muscle_start + m] / self.muscle_strengths[m], 0.2, 0.5), + time=self.render_time, + ) + muscle_start += len(s.muscles) skel_index += 1 self.render_time += self.dt * self.inv_control_freq self.renderer.update(self.state, self.render_time) - if (self.num_frames == 1): + if self.num_frames == 1: try: self.stage.Save() except: @@ -249,15 +296,16 @@ def render(self, mode = 'human'): def step(self, actions): actions = actions.view((self.num_envs, self.num_actions)) - actions = torch.clip(actions, -1., 1.) + actions = torch.clip(actions, -1.0, 1.0) actions = actions * 0.5 + 0.5 - + ##### an ugly fix for simulation nan values #### # reference: https://github.com/pytorch/pytorch/issues/15131 def create_hook(): def hook(grad): - torch.nan_to_num(grad, 0.0, 0.0, 0.0, out = grad) + torch.nan_to_num(grad, 0.0, 0.0, 0.0, out=grad) + return hook - + if self.state.joint_q.requires_grad: self.state.joint_q.register_hook(create_hook()) if self.state.joint_qd.requires_grad: @@ -273,8 +321,10 @@ def hook(grad): self.model.muscle_activation = actions.view(-1) * self.muscle_strengths else: self.state.joint_act.view(self.num_envs, -1)[:, 6:] = actions * self.action_strength - - self.state = self.integrator.forward(self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency) + + self.state = self.integrator.forward( + self.model, self.state, self.sim_dt, self.sim_substeps, self.MM_caching_frequency + ) self.sim_time += self.sim_dt self.reset_buf = torch.zeros_like(self.reset_buf) @@ -289,20 +339,17 @@ def hook(grad): if self.no_grad == False: self.obs_buf_before_reset = self.obs_buf.clone() - self.extras = { - 'obs_before_reset': self.obs_buf_before_reset, - 'episode_end': self.termination_buf - } + self.extras = {"obs_before_reset": self.obs_buf_before_reset, "episode_end": self.termination_buf} if len(env_ids) > 0: - self.reset(env_ids) + self.reset(env_ids) with df.ScopedTimer("render", False): self.render() return self.obs_buf, self.rew_buf, self.reset_buf, self.extras - - def reset(self, env_ids = None, force_reset = True): + + def reset(self, env_ids=None, force_reset=True): if env_ids is None: if force_reset == True: env_ids = torch.arange(self.num_envs, dtype=torch.long, device=self.device) @@ -316,50 +363,61 @@ def reset(self, env_ids = None, force_reset = True): self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.start_pos[env_ids, :].clone() self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = self.start_rotation.clone() self.state.joint_q.view(self.num_envs, -1)[env_ids, 7:] = self.start_joint_q.clone() - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0. + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.0 # randomization if self.stochastic_init: - self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + 0.1 * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) * 2. - angle = (torch.rand(len(env_ids), device = self.device) - 0.5) * np.pi / 12. - axis = torch.nn.functional.normalize(torch.rand((len(env_ids), 3), device = self.device) - 0.5) - self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul(self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], tu.quat_from_angle_axis(angle, axis)) - self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * (torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] = ( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 0:3] + + 0.1 * (torch.rand(size=(len(env_ids), 3), device=self.device) - 0.5) * 2.0 + ) + angle = (torch.rand(len(env_ids), device=self.device) - 0.5) * np.pi / 12.0 + axis = torch.nn.functional.normalize(torch.rand((len(env_ids), 3), device=self.device) - 0.5) + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7] = tu.quat_mul( + self.state.joint_q.view(self.num_envs, -1)[env_ids, 3:7], tu.quat_from_angle_axis(angle, axis) + ) + self.state.joint_qd.view(self.num_envs, -1)[env_ids, :] = 0.5 * ( + torch.rand(size=(len(env_ids), self.num_joint_qd), device=self.device) - 0.5 + ) # clear action self.actions = self.actions.clone() - self.actions[env_ids, :] = torch.zeros((len(env_ids), self.num_actions), device = self.device, dtype = torch.float) + self.actions[env_ids, :] = torch.zeros( + (len(env_ids), self.num_actions), device=self.device, dtype=torch.float + ) self.progress_buf[env_ids] = 0 self.calculateObservations() - + return self.obs_buf - - ''' + + """ cut off the gradient from the current state to previous states - ''' - def clear_grad(self, checkpoint = None): + """ + + def clear_grad(self, checkpoint=None): with torch.no_grad(): if checkpoint is None: - checkpoint = {} # NOTE: any other things to restore? - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() - - current_joint_q = checkpoint['joint_q'].clone() - current_joint_qd = checkpoint['joint_qd'].clone() + checkpoint = {} # NOTE: any other things to restore? + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() + + current_joint_q = checkpoint["joint_q"].clone() + current_joint_qd = checkpoint["joint_qd"].clone() self.state = self.model.state() self.state.joint_q = current_joint_q self.state.joint_qd = current_joint_qd - self.actions = checkpoint['actions'].clone() - self.progress_buf = checkpoint['progress_buf'].clone() + self.actions = checkpoint["actions"].clone() + self.progress_buf = checkpoint["progress_buf"].clone() - ''' + """ This function starts collecting a new trajectory from the current states but cuts off the computation graph to the previous states. It has to be called every time the algorithm starts an episode and it returns the observation vectors - ''' + """ + def initialize_trajectory(self): self.clear_grad() self.calculateObservations() @@ -368,10 +426,10 @@ def initialize_trajectory(self): def get_checkpoint(self): checkpoint = {} - checkpoint['joint_q'] = self.state.joint_q.clone() - checkpoint['joint_qd'] = self.state.joint_qd.clone() - checkpoint['actions'] = self.actions.clone() - checkpoint['progress_buf'] = self.progress_buf.clone() + checkpoint["joint_q"] = self.state.joint_q.clone() + checkpoint["joint_qd"] = self.state.joint_qd.clone() + checkpoint["actions"] = self.actions.clone() + checkpoint["progress_buf"] = self.progress_buf.clone() return checkpoint @@ -382,26 +440,30 @@ def calculateObservations(self): ang_vel = self.state.joint_qd.view(self.num_envs, -1)[:, 0:3] # convert the linear velocity of the torso from twist representation to the velocity of the center of mass in world frame - lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim = -1) + lin_vel = lin_vel - torch.cross(torso_pos, ang_vel, dim=-1) to_target = self.targets + self.start_pos - torso_pos to_target[:, 1] = 0.0 - + target_dirs = tu.normalize(to_target) torso_quat = tu.quat_mul(torso_rot, self.inv_start_rot) up_vec = tu.quat_rotate(torso_quat, self.basis_vec1) heading_vec = tu.quat_rotate(torso_quat, self.basis_vec0) - - self.obs_buf = torch.cat([torso_pos[:, 1:2], # 0 - torso_rot, # 1:5 - lin_vel, # 5:8 - ang_vel, # 8:11 - self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:33 - self.joint_vel_obs_scaling * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 33:51 - up_vec[:, 1:2], # 51 - (heading_vec * target_dirs).sum(dim = -1).unsqueeze(-1)], # 52 - dim = -1) + + self.obs_buf = torch.cat( + [ + torso_pos[:, 1:2], # 0 + torso_rot, # 1:5 + lin_vel, # 5:8 + ang_vel, # 8:11 + self.state.joint_q.view(self.num_envs, -1)[:, 7:], # 11:33 + self.joint_vel_obs_scaling * self.state.joint_qd.view(self.num_envs, -1)[:, 6:], # 33:51 + up_vec[:, 1:2], # 51 + (heading_vec * target_dirs).sum(dim=-1).unsqueeze(-1), + ], # 52 + dim=-1, + ) def calculateReward(self): up_reward = 0.1 * self.obs_buf[:, 51] @@ -409,27 +471,48 @@ def calculateReward(self): height_diff = self.obs_buf[:, 0] - (self.termination_height + self.termination_tolerance) height_reward = torch.clip(height_diff, -1.0, self.termination_tolerance) - height_reward = torch.where(height_reward < 0.0, -200.0 * height_reward * height_reward, height_reward) # JIE: not smooth + height_reward = torch.where( + height_reward < 0.0, -200.0 * height_reward * height_reward, height_reward + ) # JIE: not smooth height_reward = torch.where(height_reward > 0.0, self.height_rew_scale * height_reward, height_reward) - - act_penalty = torch.sum(torch.abs(self.actions), dim = -1) * self.action_penalty #torch.sum(self.actions ** 2, dim = -1) * self.action_penalty + + act_penalty = ( + torch.sum(torch.abs(self.actions), dim=-1) * self.action_penalty + ) # torch.sum(self.actions ** 2, dim = -1) * self.action_penalty progress_reward = self.obs_buf[:, 5] self.rew_buf = progress_reward + up_reward + heading_reward + act_penalty - + # reset agents - self.reset_buf = torch.where(self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf) - self.reset_buf = torch.where(self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf) + self.reset_buf = torch.where( + self.obs_buf[:, 0] < self.termination_height, torch.ones_like(self.reset_buf), self.reset_buf + ) + self.reset_buf = torch.where( + self.progress_buf > self.episode_length - 1, torch.ones_like(self.reset_buf), self.reset_buf + ) # an ugly fix for simulation nan values - nan_masks = torch.logical_or(torch.isnan(self.obs_buf).sum(-1) > 0, torch.logical_or(torch.isnan(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, torch.isnan(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0)) - inf_masks = torch.logical_or(torch.isinf(self.obs_buf).sum(-1) > 0, torch.logical_or(torch.isinf(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, torch.isinf(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0)) - invalid_value_masks = torch.logical_or((torch.abs(self.state.joint_q.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, - (torch.abs(self.state.joint_qd.view(self.num_environments, -1)) > 1e6).sum(-1) > 0) + nan_masks = torch.logical_or( + torch.isnan(self.obs_buf).sum(-1) > 0, + torch.logical_or( + torch.isnan(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, + torch.isnan(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0, + ), + ) + inf_masks = torch.logical_or( + torch.isinf(self.obs_buf).sum(-1) > 0, + torch.logical_or( + torch.isinf(self.state.joint_q.view(self.num_environments, -1)).sum(-1) > 0, + torch.isinf(self.state.joint_qd.view(self.num_environments, -1)).sum(-1) > 0, + ), + ) + invalid_value_masks = torch.logical_or( + (torch.abs(self.state.joint_q.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, + (torch.abs(self.state.joint_qd.view(self.num_environments, -1)) > 1e6).sum(-1) > 0, + ) invalid_masks = torch.logical_or(invalid_value_masks, torch.logical_or(nan_masks, inf_masks)) self.reset_buf = torch.where(invalid_masks, torch.ones_like(self.reset_buf), self.reset_buf) - self.rew_buf[invalid_masks] = 0. - \ No newline at end of file + self.rew_buf[invalid_masks] = 0.0 diff --git a/src/shac/models/actor.py b/src/shac/models/actor.py index a4929cda..93ea3000 100644 --- a/src/shac/models/actor.py +++ b/src/shac/models/actor.py @@ -21,19 +21,13 @@ def __init__(self, obs_dim, action_dim, cfg_network, device="cuda:0"): self.layer_dims = [obs_dim] + cfg_network["actor_mlp"]["units"] + [action_dim] - init_ = lambda m: model_utils.init( - m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2) - ) + init_ = lambda m: model_utils.init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2)) modules = [] for i in range(len(self.layer_dims) - 1): modules.append(init_(nn.Linear(self.layer_dims[i], self.layer_dims[i + 1]))) if i < len(self.layer_dims) - 2: - modules.append( - model_utils.get_activation_func( - cfg_network["actor_mlp"]["activation"] - ) - ) + modules.append(model_utils.get_activation_func(cfg_network["actor_mlp"]["activation"])) modules.append(torch.nn.LayerNorm(self.layer_dims[i + 1])) self.actor = nn.Sequential(*modules).to(device) @@ -59,19 +53,13 @@ def __init__(self, obs_dim, action_dim, cfg_network, device="cuda:0"): self.layer_dims = [obs_dim] + cfg_network["actor_mlp"]["units"] + [action_dim] - init_ = lambda m: model_utils.init( - m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2) - ) + init_ = lambda m: model_utils.init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2)) modules = [] for i in range(len(self.layer_dims) - 1): modules.append(nn.Linear(self.layer_dims[i], self.layer_dims[i + 1])) if i < len(self.layer_dims) - 2: - modules.append( - model_utils.get_activation_func( - cfg_network["actor_mlp"]["activation"] - ) - ) + modules.append(model_utils.get_activation_func(cfg_network["actor_mlp"]["activation"])) modules.append(torch.nn.LayerNorm(self.layer_dims[i + 1])) else: modules.append(model_utils.get_activation_func("identity")) @@ -80,9 +68,7 @@ def __init__(self, obs_dim, action_dim, cfg_network, device="cuda:0"): logstd = cfg_network.get("actor_logstd_init", -1.0) - self.logstd = torch.nn.Parameter( - torch.ones(action_dim, dtype=torch.float32, device=device) * logstd - ) + self.logstd = torch.nn.Parameter(torch.ones(action_dim, dtype=torch.float32, device=device) * logstd) self.action_dim = action_dim self.obs_dim = obs_dim diff --git a/src/shac/models/critic.py b/src/shac/models/critic.py index bbd76c80..231cfc0d 100644 --- a/src/shac/models/critic.py +++ b/src/shac/models/critic.py @@ -20,19 +20,13 @@ def __init__(self, obs_dim, cfg_network, device="cuda:0"): self.layer_dims = [obs_dim] + cfg_network["critic_mlp"]["units"] + [1] - init_ = lambda m: model_utils.init( - m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2) - ) + init_ = lambda m: model_utils.init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2)) modules = [] for i in range(len(self.layer_dims) - 1): modules.append(init_(nn.Linear(self.layer_dims[i], self.layer_dims[i + 1]))) if i < len(self.layer_dims) - 2: - modules.append( - model_utils.get_activation_func( - cfg_network["critic_mlp"]["activation"] - ) - ) + modules.append(model_utils.get_activation_func(cfg_network["critic_mlp"]["activation"])) modules.append(torch.nn.LayerNorm(self.layer_dims[i + 1])) self.critic = nn.Sequential(*modules).to(device) @@ -43,3 +37,31 @@ def __init__(self, obs_dim, cfg_network, device="cuda:0"): def forward(self, observations): return self.critic(observations) + + +class QCriticMLP(nn.Module): + def __init__(self, obs_dim, action_dim, cfg_network, device="cuda:0"): + super(QCriticMLP, self).__init__() + + self.device = device + + self.layer_dims = [obs_dim + action_dim] + cfg_network["critic_mlp"]["units"] + [1] + + init_ = lambda m: model_utils.init(m, nn.init.orthogonal_, lambda x: nn.init.constant_(x, 0), np.sqrt(2)) + + modules = [] + for i in range(len(self.layer_dims) - 1): + modules.append(init_(nn.Linear(self.layer_dims[i], self.layer_dims[i + 1]))) + if i < len(self.layer_dims) - 2: + modules.append(model_utils.get_activation_func(cfg_network["critic_mlp"]["activation"])) + modules.append(torch.nn.LayerNorm(self.layer_dims[i + 1])) + + self.q_function = nn.Sequential(*modules).to(device) + + self.obs_dim = obs_dim + self.action_dim = action_dim + + print(self.q_function) + + def forward(self, observations, actions): + return self.q_function(torch.cat([observations, actions], dim=-1)) diff --git a/src/shac/utils/dataset.py b/src/shac/utils/dataset.py index 970b81e1..b54e2ee4 100644 --- a/src/shac/utils/dataset.py +++ b/src/shac/utils/dataset.py @@ -6,22 +6,37 @@ # license agreement from NVIDIA CORPORATION is strictly prohibited. import numpy as np +import torch class CriticDataset: - def __init__(self, batch_size, obs, target_values, shuffle = False, drop_last = False): + def __init__(self, batch_size, obs, target_values, shuffle=False, drop_last=False): self.obs = obs.view(-1, obs.shape[-1]) self.target_values = target_values.view(-1) self.batch_size = batch_size + # filter nans + not_nan_idx = (self.obs == self.obs).all(dim=-1).nonzero(as_tuple=False) + + # for debugging below + # print("detected {:} nans".format(len(self.obs) - len(not_nan_idx))) + # if len(self.obs) - len(not_nan_idx) > 0: + # print(self.obs.shape) + # print(not_nan_idx.shape) + # print(not_nan_idx) + # exit(1) + + self.obs = self.obs[not_nan_idx] + self.target_values = self.target_values[not_nan_idx] + if shuffle: self.shuffle() - + if drop_last: self.length = self.obs.shape[0] // self.batch_size else: self.length = ((self.obs.shape[0] - 1) // self.batch_size) + 1 - + def shuffle(self): index = np.random.permutation(self.obs.shape[0]) self.obs = self.obs[index, :] @@ -29,8 +44,47 @@ def shuffle(self): def __len__(self): return self.length - + + def __getitem__(self, index): + start_idx = index * self.batch_size + end_idx = min((index + 1) * self.batch_size, self.obs.shape[0]) + return { + "obs": self.obs[start_idx:end_idx, :], + "target_values": self.target_values[start_idx:end_idx], + } + + +class QCriticDataset: + def __init__( + self, batch_size, obs, act, target_values, shuffle=False, drop_last=False + ): + self.obs = obs.view(-1, obs.shape[-1]) + self.act = act.view(-1, act.shape[-1]) + self.target_values = target_values.view(-1) + self.batch_size = batch_size + + if shuffle: + self.shuffle() + + if drop_last: + self.length = self.obs.shape[0] // self.batch_size + else: + self.length = ((self.obs.shape[0] - 1) // self.batch_size) + 1 + + def shuffle(self): + index = np.random.permutation(self.obs.shape[0]) + self.obs = self.obs[index, :] + self.act = self.act[index, :] + self.target_values = self.target_values[index] + + def __len__(self): + return self.length + def __getitem__(self, index): start_idx = index * self.batch_size end_idx = min((index + 1) * self.batch_size, self.obs.shape[0]) - return {'obs': self.obs[start_idx:end_idx, :], 'target_values': self.target_values[start_idx:end_idx]} + return { + "obs": self.obs[start_idx:end_idx, :], + "act": self.act[start_idx:end_idx, :], + "target_values": self.target_values[start_idx:end_idx], + } diff --git a/src/shac/utils/hydra_utils.py b/src/shac/utils/hydra_utils.py new file mode 100644 index 00000000..504457ff --- /dev/null +++ b/src/shac/utils/hydra_utils.py @@ -0,0 +1,18 @@ +import numpy as np +from omegaconf import OmegaConf, DictConfig +from typing import Dict + +OmegaConf.register_new_resolver("resolve_default", lambda default, arg: default if arg in ["", None] else arg) + +OmegaConf.register_new_resolver("eval", eval) + + +def omegaconf_to_dict(d: DictConfig) -> Dict: + """Converts an omegaconf DictConfig to a python Dict, respecting variable interpolation.""" + ret = {} + for k, v in d.items(): + if isinstance(v, DictConfig): + ret[k] = omegaconf_to_dict(v) + else: + ret[k] = v + return ret diff --git a/src/shac/utils/load_utils.py b/src/shac/utils/load_utils.py index 92eb3e8d..2384c212 100644 --- a/src/shac/utils/load_utils.py +++ b/src/shac/utils/load_utils.py @@ -18,9 +18,17 @@ def set_np_formatting(): - np.set_printoptions(edgeitems=30, infstr='inf', - linewidth=4000, nanstr='nan', precision=2, - suppress=False, threshold=10000, formatter=None) + np.set_printoptions( + edgeitems=30, + infstr="inf", + linewidth=4000, + nanstr="nan", + precision=2, + suppress=False, + threshold=10000, + formatter=None, + ) + def set_seed(seed, torch_deterministic=False): if seed == -1 and torch_deterministic: @@ -32,13 +40,13 @@ def set_seed(seed, torch_deterministic=False): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) - os.environ['PYTHONHASHSEED'] = str(seed) + os.environ["PYTHONHASHSEED"] = str(seed) torch.cuda.manual_seed(seed) torch.cuda.manual_seed_all(seed) if torch_deterministic: # refer to https://docs.nvidia.com/cuda/cublas/index.html#cublasApi_reproducibility - os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' + os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" torch.backends.cudnn.benchmark = False torch.backends.cudnn.deterministic = True torch.use_deterministic_algorithms(True) @@ -48,11 +56,12 @@ def set_seed(seed, torch_deterministic=False): return seed -def urdf_add_collision(builder, link, collisions, shape_ke, shape_kd, shape_kf, shape_mu): - + +def urdf_add_collision( + builder, link, collisions, shape_ke, shape_kd, shape_kf, shape_mu +): # add geometry for collision in collisions: - origin = urdfpy.matrix_to_xyz_rpy(collision.origin) pos = origin[0:3] @@ -60,62 +69,63 @@ def urdf_add_collision(builder, link, collisions, shape_ke, shape_kd, shape_kf, geo = collision.geometry - if (geo.box): + if geo.box: builder.add_shape_box( link, - pos, - rot, - geo.box.size[0]*0.5, - geo.box.size[1]*0.5, - geo.box.size[2]*0.5, + pos, + rot, + geo.box.size[0] * 0.5, + geo.box.size[1] * 0.5, + geo.box.size[2] * 0.5, ke=shape_ke, kd=shape_kd, kf=shape_kf, - mu=shape_mu) - - if (geo.sphere): + mu=shape_mu, + ) + + if geo.sphere: builder.add_shape_sphere( - link, - pos, - rot, + link, + pos, + rot, geo.sphere.radius, ke=shape_ke, kd=shape_kd, kf=shape_kf, - mu=shape_mu) - - if (geo.cylinder): - + mu=shape_mu, + ) + + if geo.cylinder: # cylinders in URDF are aligned with z-axis, while dFlex uses x-axis - r = df.quat_from_axis_angle((0.0, 1.0, 0.0), math.pi*0.5) + r = df.quat_from_axis_angle((0.0, 1.0, 0.0), math.pi * 0.5) builder.add_shape_capsule( - link, - pos, - df.quat_multiply(rot, r), - geo.cylinder.radius, - geo.cylinder.length*0.5, + link, + pos, + df.quat_multiply(rot, r), + geo.cylinder.radius, + geo.cylinder.length * 0.5, ke=shape_ke, kd=shape_kd, kf=shape_kf, - mu=shape_mu) - - if (geo.mesh): + mu=shape_mu, + ) + if geo.mesh: for m in geo.mesh.meshes: faces = [] vertices = [] for v in m.vertices: vertices.append(np.array(v)) - + for f in m.faces: faces.append(int(f[0])) faces.append(int(f[1])) faces.append(int(f[2])) - + mesh = df.Mesh(vertices, faces) - + builder.add_shape_mesh( link, pos, @@ -124,21 +134,25 @@ def urdf_add_collision(builder, link, collisions, shape_ke, shape_kd, shape_kf, ke=shape_ke, kd=shape_kd, kf=shape_kf, - mu=shape_mu) - + mu=shape_mu, + ) + + def urdf_load( - builder, - filename, - xform, - floating=False, - armature=0.0, - shape_ke=1.e+4, - shape_kd=1.e+4, - shape_kf=1.e+2, + builder, + filename, + xform, + floating=False, + stiffness=100.0, + damping=10.0, + armature=0.0, + shape_ke=1.0e4, + shape_kd=1.0e4, + shape_kf=1.0e2, shape_mu=0.25, limit_ke=100.0, - limit_kd=1.0): - + limit_kd=1.0, +): robot = urdfpy.URDF.load(filename) # maps from link name -> link index @@ -147,8 +161,10 @@ def urdf_load( builder.add_articulation() # add base - if (floating): - root = builder.add_link(-1, df.transform_identity(), (0,0,0), df.JOINT_FREE) + if floating: + root = builder.add_link( + -1, df.transform_identity(), (0, 0, 0), df.JOINT_FREE, armature=armature + ) # set dofs to transform start = builder.joint_q_start[root] @@ -161,29 +177,30 @@ def urdf_load( builder.joint_q[start + 4] = xform[1][1] builder.joint_q[start + 5] = xform[1][2] builder.joint_q[start + 6] = xform[1][3] - else: - root = builder.add_link(-1, xform, (0,0,0), df.JOINT_FIXED) + else: + root = builder.add_link(-1, xform, (0, 0, 0), df.JOINT_FIXED) - urdf_add_collision(builder, root, robot.links[0].collisions, shape_ke, shape_kd, shape_kf, shape_mu) + urdf_add_collision( + builder, root, robot.links[0].collisions, shape_ke, shape_kd, shape_kf, shape_mu + ) link_index[robot.links[0].name] = root # add children for joint in robot.joints: - type = None axis = (0.0, 0.0, 0.0) - if (joint.joint_type == "revolute" or joint.joint_type == "continuous"): + if joint.joint_type == "revolute" or joint.joint_type == "continuous": type = df.JOINT_REVOLUTE axis = joint.axis - if (joint.joint_type == "prismatic"): + if joint.joint_type == "prismatic": type = df.JOINT_PRISMATIC axis = joint.axis - if (joint.joint_type == "fixed"): + if joint.joint_type == "fixed": type = df.JOINT_FIXED - if (joint.joint_type == "floating"): + if joint.joint_type == "floating": type = df.JOINT_FREE - + parent = -1 if joint.parent in link_index: @@ -194,104 +211,114 @@ def urdf_load( pos = origin[0:3] rot = df.rpy2quat(*origin[3:6]) - lower = -1.e+3 - upper = 1.e+3 - damping = 0.0 + lower = -1.0e3 + upper = 1.0e3 # limits - if (joint.limit): - - if (joint.limit.lower != None): + if joint.limit: + if joint.limit.lower != None: lower = joint.limit.lower - if (joint.limit.upper != None): + if joint.limit.upper != None: upper = joint.limit.upper # damping - if (joint.dynamics): - if (joint.dynamics.damping): + if joint.dynamics: + if joint.dynamics.damping: damping = joint.dynamics.damping # add link link = builder.add_link( - parent=parent, - X_pj=df.transform(pos, rot), - axis=axis, + parent=parent, + X_pj=df.transform(pos, rot), + axis=axis, type=type, limit_lower=lower, limit_upper=upper, limit_ke=limit_ke, limit_kd=limit_kd, - damping=damping) + damping=damping, + armature=armature, + stiffness=stiffness, + ) # add collisions - urdf_add_collision(builder, link, robot.link_map[joint.child].collisions, shape_ke, shape_kd, shape_kf, shape_mu) + urdf_add_collision( + builder, + link, + robot.link_map[joint.child].collisions, + shape_ke, + shape_kd, + shape_kf, + shape_mu, + ) # add ourselves to the index link_index[joint.child] = link + # build an articulated tree def build_tree( - builder, + builder, angle, - max_depth, + max_depth, width=0.05, length=0.25, density=1000.0, joint_stiffness=0.0, joint_damping=0.0, - shape_ke = 1.e+4, - shape_kd = 1.e+3, - shape_kf = 1.e+2, - shape_mu = 0.5, - floating=False): - + shape_ke=1.0e4, + shape_kd=1.0e3, + shape_kf=1.0e2, + shape_mu=0.5, + floating=False, +): def build_recursive(parent, depth): - - if (depth >= max_depth): + if depth >= max_depth: return - X_pj = df.transform((length * 2.0, 0.0, 0.0), df.quat_from_axis_angle((0.0, 0.0, 1.0), angle)) + X_pj = df.transform( + (length * 2.0, 0.0, 0.0), df.quat_from_axis_angle((0.0, 0.0, 1.0), angle) + ) type = df.JOINT_REVOLUTE axis = (0.0, 0.0, 1.0) - if (depth == 0 and floating == True): + if depth == 0 and floating == True: X_pj = df.transform((0.0, 0.0, 0.0), df.quat_identity()) type = df.JOINT_FREE link = builder.add_link( - parent, - X_pj, - axis, - type, - stiffness=joint_stiffness, - damping=joint_damping) - + parent, X_pj, axis, type, stiffness=joint_stiffness, damping=joint_damping + ) + # capsule shape = builder.add_shape_capsule( - link, - pos=(length, 0.0, 0.0), - radius=width, - half_width=length, + link, + pos=(length, 0.0, 0.0), + radius=width, + half_width=length, ke=shape_ke, kd=shape_kd, kf=shape_kf, - mu=shape_mu) + mu=shape_mu, + ) # recurse - #build_tree_recursive(builder, link, angle, width, depth + 1, max_depth, shape_ke, shape_kd, shape_kf, shape_mu, floating) + # build_tree_recursive(builder, link, angle, width, depth + 1, max_depth, shape_ke, shape_kd, shape_kf, shape_mu, floating) build_recursive(link, depth + 1) - # + # build_recursive(-1, 0) + # Mujoco file format parser + def parse_mjcf( - filename, - builder, - density=1000.0, - stiffness=0.0, - damping=1.0, + filename, + builder, + density=1000.0, + stiffness=0.0, + damping=1.0, contact_ke=1e4, contact_kd=1e4, contact_kf=1e3, @@ -301,19 +328,19 @@ def parse_mjcf( armature=0.01, radians=False, load_stiffness=False, - load_armature=False): - + load_armature=False, +): file = ET.parse(filename) root = file.getroot() - type_map = { - "ball": df.JOINT_BALL, - "hinge": df.JOINT_REVOLUTE, - "slide": df.JOINT_PRISMATIC, - "free": df.JOINT_FREE, - "fixed": df.JOINT_FIXED + type_map = { + "ball": df.JOINT_BALL, + "hinge": df.JOINT_REVOLUTE, + "slide": df.JOINT_PRISMATIC, + "free": df.JOINT_FREE, + "fixed": df.JOINT_FIXED, } - + def parse_float(node, key, default): if key in node.attrib: return float(node.attrib[key]) @@ -322,7 +349,6 @@ def parse_float(node, key, default): def parse_bool(node, key, default): if key in node.attrib: - if node.attrib[key] == "true": return True else: @@ -338,35 +364,35 @@ def parse_vec(node, key, default): return np.array(default) def parse_body(body, parent, last_joint_pos): - body_name = body.attrib["name"] body_pos = np.fromstring(body.attrib["pos"], sep=" ") # last_joint_pos = np.zeros(3) - #----------------- + # ----------------- # add body for each joint, we assume the joints attached to one body have the same joint_pos for i, joint in enumerate(body.findall("joint")): - joint_name = joint.attrib["name"] - joint_type = type_map[joint.attrib.get("type", 'hinge')] + joint_type = type_map[joint.attrib.get("type", "hinge")] joint_axis = parse_vec(joint, "axis", (0.0, 0.0, 0.0)) joint_pos = parse_vec(joint, "pos", (0.0, 0.0, 0.0)) joint_limited = parse_bool(joint, "limited", True) if joint_limited: if radians: - joint_range = parse_vec(joint, "range", (np.deg2rad(-170.), np.deg2rad(170.))) + joint_range = parse_vec( + joint, "range", (np.deg2rad(-170.0), np.deg2rad(170.0)) + ) else: joint_range = np.deg2rad(parse_vec(joint, "range", (-170.0, 170.0))) else: - joint_range = np.array([-1.e+6, 1.e+6]) + joint_range = np.array([-1.0e6, 1.0e6]) if load_stiffness: - joint_stiffness = parse_float(joint, 'stiffness', stiffness) + joint_stiffness = parse_float(joint, "stiffness", stiffness) else: joint_stiffness = stiffness - joint_damping = parse_float(joint, 'damping', damping) + joint_damping = parse_float(joint, "damping", damping) if load_armature: joint_armature = parse_float(joint, "armature", armature) @@ -374,16 +400,18 @@ def parse_body(body, parent, last_joint_pos): joint_armature = armature joint_axis = df.normalize(joint_axis) - - if (parent == -1): + + if parent == -1: body_pos = np.array((0.0, 0.0, 0.0)) - - #----------------- + + # ----------------- # add body link = builder.add_link( - parent, - X_pj=df.transform(body_pos + joint_pos - last_joint_pos, df.quat_identity()), - axis=joint_axis, + parent, + X_pj=df.transform( + body_pos + joint_pos - last_joint_pos, df.quat_identity() + ), + axis=joint_axis, type=joint_type, limit_lower=joint_range[0], limit_upper=joint_range[1], @@ -391,71 +419,78 @@ def parse_body(body, parent, last_joint_pos): limit_kd=limit_kd, stiffness=joint_stiffness, damping=joint_damping, - armature=joint_armature) + armature=joint_armature, + ) # assume that each joint is one body in simulation - parent = link - body_pos = [0.0, 0.0, 0.0] + parent = link + body_pos = [0.0, 0.0, 0.0] last_joint_pos = joint_pos - #----------------- + # ----------------- # add shapes to the last joint in the body for geom in body.findall("geom"): geom_name = geom.attrib["name"] geom_type = geom.attrib["type"] - geom_size = parse_vec(geom, "size", [1.0]) - geom_pos = parse_vec(geom, "pos", (0.0, 0.0, 0.0)) + geom_size = parse_vec(geom, "size", [1.0]) + geom_pos = parse_vec(geom, "pos", (0.0, 0.0, 0.0)) geom_rot = parse_vec(geom, "quat", (0.0, 0.0, 0.0, 1.0)) - if (geom_type == "sphere"): - + if geom_type == "sphere": builder.add_shape_sphere( - link, - pos=geom_pos - last_joint_pos, # position relative to the parent frame + link, + pos=geom_pos + - last_joint_pos, # position relative to the parent frame rot=geom_rot, radius=geom_size[0], density=density, ke=contact_ke, kd=contact_kd, kf=contact_kf, - mu=contact_mu) - - elif (geom_type == "capsule"): + mu=contact_mu, + ) - if ("fromto" in geom.attrib): - geom_fromto = parse_vec(geom, "fromto", (0.0, 0.0, 0.0, 1.0, 0.0, 0.0)) + elif geom_type == "capsule": + if "fromto" in geom.attrib: + geom_fromto = parse_vec( + geom, "fromto", (0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + ) start = geom_fromto[0:3] end = geom_fromto[3:6] - # compute rotation to align dflex capsule (along x-axis), with mjcf fromto direction - axis = df.normalize(end-start) + # compute rotation to align dflex capsule (along x-axis), with mjcf fromto direction + axis = df.normalize(end - start) angle = math.acos(np.dot(axis, (1.0, 0.0, 0.0))) axis = df.normalize(np.cross(axis, (1.0, 0.0, 0.0))) - geom_pos = (start + end)*0.5 + geom_pos = (start + end) * 0.5 geom_rot = df.quat_from_axis_angle(axis, -angle) geom_radius = geom_size[0] - geom_width = np.linalg.norm(end-start)*0.5 + geom_width = np.linalg.norm(end - start) * 0.5 else: - geom_radius = geom_size[0] geom_width = geom_size[1] geom_pos = parse_vec(geom, "pos", (0.0, 0.0, 0.0)) - - if ("axisangle" in geom.attrib): + + if "axisangle" in geom.attrib: axis_angle = parse_vec(geom, "axisangle", (0.0, 1.0, 0.0, 0.0)) - geom_rot = df.quat_from_axis_angle(axis_angle[0:3], axis_angle[3]) + geom_rot = df.quat_from_axis_angle( + axis_angle[0:3], axis_angle[3] + ) - if ("quat" in geom.attrib): + if "quat" in geom.attrib: q = parse_vec(geom, "quat", df.quat_identity()) geom_rot = q - geom_rot = df.quat_multiply(geom_rot, df.quat_from_axis_angle((0.0, 1.0, 0.0), -math.pi*0.5)) + geom_rot = df.quat_multiply( + geom_rot, + df.quat_from_axis_angle((0.0, 1.0, 0.0), -math.pi * 0.5), + ) builder.add_shape_capsule( link, @@ -467,18 +502,19 @@ def parse_body(body, parent, last_joint_pos): ke=contact_ke, kd=contact_kd, kf=contact_kf, - mu=contact_mu) + mu=contact_mu, + ) else: - print("Type: " + geom_type + " unsupported") + print("Type: " + geom_type + " unsupported") - #----------------- + # ----------------- # recurse for child in body.findall("body"): parse_body(child, link, last_joint_pos) - #----------------- + # ----------------- # start articulation builder.add_articulation() @@ -490,30 +526,33 @@ def parse_body(body, parent, last_joint_pos): # SNU file format parser -class MuscleUnit: +class MuscleUnit: def __init__(self): - self.name = "" self.bones = [] self.points = [] self.muscle_strength = 0.0 -class Skeleton: - def __init__(self, skeleton_file, muscle_file, builder, - filter={}, - visualize_shapes=True, - stiffness=5.0, - damping=2.0, +class Skeleton: + def __init__( + self, + skeleton_file, + muscle_file, + builder, + filter={}, + visualize_shapes=True, + stiffness=5.0, + damping=2.0, contact_ke=5000.0, contact_kd=2000.0, contact_kf=1000.0, contact_mu=0.5, limit_ke=1000.0, limit_kd=10.0, - armature = 0.05): - + armature=0.05, + ): self.armature = armature self.stiffness = stiffness self.damping = damping @@ -537,28 +576,26 @@ def __init__(self, skeleton_file, muscle_file, builder, def parse_skeleton(self, filename, builder, filter): file = ET.parse(filename) root = file.getroot() - - self.node_map = {} # map node names to link indices - self.xform_map = {} # map node names to parent transforms - self.mesh_map = {} # map mesh names to link indices objects + + self.node_map = {} # map node names to link indices + self.xform_map = {} # map node names to parent transforms + self.mesh_map = {} # map mesh names to link indices objects self.coord_start = len(builder.joint_q) self.dof_start = len(builder.joint_qd) - type_map = { - "Ball": df.JOINT_BALL, - "Revolute": df.JOINT_REVOLUTE, - "Prismatic": df.JOINT_PRISMATIC, - "Free": df.JOINT_FREE, - "Fixed": df.JOINT_FIXED + type_map = { + "Ball": df.JOINT_BALL, + "Revolute": df.JOINT_REVOLUTE, + "Prismatic": df.JOINT_PRISMATIC, + "Free": df.JOINT_FREE, + "Fixed": df.JOINT_FIXED, } builder.add_articulation() for child in root: - - if (child.tag == "Node"): - + if child.tag == "Node": body = child.find("Body") joint = child.find("Joint") @@ -580,35 +617,39 @@ def parse_skeleton(self, filename, builder, filter): body_type = body.attrib["type"] body_mass = float(body.attrib["mass"]) - x=body_size[0] - y=body_size[1] - z=body_size[2] - density = body_mass / (x*y*z) + x = body_size[0] + y = body_size[1] + z = body_size[2] + density = body_mass / (x * y * z) max_body_mass = 15.0 mass_scale = body_mass / max_body_mass - body_R_s = np.fromstring(body_xform.attrib["linear"], sep=" ").reshape((3,3)) + body_R_s = np.fromstring(body_xform.attrib["linear"], sep=" ").reshape( + (3, 3) + ) body_t_s = np.fromstring(body_xform.attrib["translation"], sep=" ") - joint_R_s = np.fromstring(joint_xform.attrib["linear"], sep=" ").reshape((3,3)) + joint_R_s = np.fromstring( + joint_xform.attrib["linear"], sep=" " + ).reshape((3, 3)) joint_t_s = np.fromstring(joint_xform.attrib["translation"], sep=" ") - + joint_type = type_map[joint.attrib["type"]] - joint_lower = -1.e+3 - joint_upper = 1.e+3 - - if (joint_type == type_map["Revolute"]): - if ("lower" in joint.attrib): + joint_lower = -1.0e3 + joint_upper = 1.0e3 + + if joint_type == type_map["Revolute"]: + if "lower" in joint.attrib: joint_lower = np.fromstring(joint.attrib["lower"], sep=" ")[0] - if ("upper" in joint.attrib): + if "upper" in joint.attrib: joint_upper = np.fromstring(joint.attrib["upper"], sep=" ")[0] - + # print(joint_type, joint_lower, joint_upper) - if ("axis" in joint.attrib): + if "axis" in joint.attrib: joint_axis = np.fromstring(joint.attrib["axis"], sep=" ") else: joint_axis = np.array((0.0, 0.0, 0.0)) @@ -622,16 +663,19 @@ def parse_skeleton(self, filename, builder, filter): link = -1 if len(filter) == 0 or name in filter: - - joint_X_p = df.transform_multiply(df.transform_inverse(parent_X_s), joint_X_s) - body_X_c = df.transform_multiply(df.transform_inverse(joint_X_s), body_X_s) - - if (parent_link == -1): + joint_X_p = df.transform_multiply( + df.transform_inverse(parent_X_s), joint_X_s + ) + body_X_c = df.transform_multiply( + df.transform_inverse(joint_X_s), body_X_s + ) + + if parent_link == -1: joint_X_p = df.transform_identity() # add link link = builder.add_link( - parent=parent_link, + parent=parent_link, X_pj=joint_X_p, axis=joint_axis, type=joint_type, @@ -641,22 +685,24 @@ def parse_skeleton(self, filename, builder, filter): limit_kd=self.limit_kd * mass_scale, damping=self.damping, stiffness=self.stiffness * math.sqrt(mass_scale), - armature=self.armature) - # armature=self.armature * math.sqrt(mass_scale)) + armature=self.armature, + ) + # armature=self.armature * math.sqrt(mass_scale)) # add shape shape = builder.add_shape_box( - body=link, + body=link, pos=body_X_c[0], rot=body_X_c[1], - hx=x*0.5, - hy=y*0.5, - hz=z*0.5, + hx=x * 0.5, + hy=y * 0.5, + hz=z * 0.5, density=density, ke=self.contact_ke, kd=self.contact_kd, kf=self.contact_kf, - mu=self.contact_mu) + mu=self.contact_mu, + ) # add lookup in name->link map # save parent transform @@ -665,7 +711,6 @@ def parse_skeleton(self, filename, builder, filter): self.mesh_map[mesh_base] = link def parse_muscles(self, filename, builder): - # list of MuscleUnits muscles = [] @@ -675,9 +720,7 @@ def parse_muscles(self, filename, builder): self.muscle_start = len(builder.muscle_activation) for child in root: - - if (child.tag == "Unit"): - + if child.tag == "Unit": unit_name = child.attrib["name"] unit_f0 = float(child.attrib["f0"]) unit_lm = float(child.attrib["lm"]) @@ -693,29 +736,36 @@ def parse_muscles(self, filename, builder): incomplete = False for waypoint in child.iter("Waypoint"): - way_bone = waypoint.attrib["body"] way_link = self.node_map[way_bone] - way_loc = np.fromstring(waypoint.attrib["p"], sep=" ", dtype=np.float32) + way_loc = np.fromstring( + waypoint.attrib["p"], sep=" ", dtype=np.float32 + ) - if (way_link == -1): + if way_link == -1: incomplete = True break # transform loc to joint local space joint_X_s = self.xform_map[way_bone] - way_loc = df.transform_point(df.transform_inverse(joint_X_s), way_loc) + way_loc = df.transform_point( + df.transform_inverse(joint_X_s), way_loc + ) m.bones.append(way_link) m.points.append(way_loc) if not incomplete: - muscles.append(m) - builder.add_muscle(m.bones, m.points, f0=unit_f0, lm=unit_lm, lt=unit_lt, lmax=unit_lmax, pen=unit_pen) + builder.add_muscle( + m.bones, + m.points, + f0=unit_f0, + lm=unit_lm, + lt=unit_lt, + lmax=unit_lmax, + pen=unit_pen, + ) self.muscles = muscles - - - diff --git a/src/shac/utils/torch_utils.py b/src/shac/utils/torch_utils.py index 89c74947..b15a6025 100644 --- a/src/shac/utils/torch_utils.py +++ b/src/shac/utils/torch_utils.py @@ -11,20 +11,25 @@ import gc import torch import cProfile +from torchviz import make_dot +from time import sleep log_output = "" + def log(s): print(s) global log_output log_output = log_output + s + "\n" + # short hands # torch quat/vector utils -def to_torch(x, dtype=torch.float, device='cuda:0', requires_grad=False): + +def to_torch(x, dtype=torch.float, device="cuda:0", requires_grad=False): return torch.tensor(x, dtype=dtype, device=device, requires_grad=requires_grad) @@ -72,11 +77,13 @@ def quat_rotate(q, v): shape = q.shape q_w = q[:, -1] q_vec = q[:, :3] - a = v * (2.0 * q_w ** 2 - 1.0).unsqueeze(-1) + a = v * (2.0 * q_w**2 - 1.0).unsqueeze(-1) b = torch.cross(q_vec, v, dim=-1) * q_w.unsqueeze(-1) * 2.0 - c = q_vec * \ - torch.bmm(q_vec.view(shape[0], 1, 3), v.view( - shape[0], 3, 1)).squeeze(-1) * 2.0 + c = ( + q_vec + * torch.bmm(q_vec.view(shape[0], 1, 3), v.view(shape[0], 3, 1)).squeeze(-1) + * 2.0 + ) return a + b + c @@ -85,11 +92,13 @@ def quat_rotate_inverse(q, v): shape = q.shape q_w = q[:, -1] q_vec = q[:, :3] - a = v * (2.0 * q_w ** 2 - 1.0).unsqueeze(-1) + a = v * (2.0 * q_w**2 - 1.0).unsqueeze(-1) b = torch.cross(q_vec, v, dim=-1) * q_w.unsqueeze(-1) * 2.0 - c = q_vec * \ - torch.bmm(q_vec.view(shape[0], 1, 3), v.view( - shape[0], 3, 1)).squeeze(-1) * 2.0 + c = ( + q_vec + * torch.bmm(q_vec.view(shape[0], 1, 3), v.view(shape[0], 3, 1)).squeeze(-1) + * 2.0 + ) return a - b + c @@ -153,17 +162,17 @@ def get_basis_vector(q, v): def mem_report(): - '''Report the memory usage of the tensor.storage in pytorch - Both on CPUs and GPUs are reported''' + """Report the memory usage of the tensor.storage in pytorch + Both on CPUs and GPUs are reported""" def _mem_report(tensors, mem_type): - '''Print the selected tensors of type + """Print the selected tensors of type There are two major storage types in our major concern: - GPU: tensors transferred to CUDA devices - CPU: tensors remaining on the system memory (usually unimportant) Args: - tensors: the tensors of specified type - - mem_type: 'CPU' or 'GPU' in current implementation ''' + - mem_type: 'CPU' or 'GPU' in current implementation""" total_numel = 0 total_mem = 0 visited_data = [] @@ -179,7 +188,7 @@ def _mem_report(tensors, mem_type): numel = tensor.storage().size() total_numel += numel element_size = tensor.storage().element_size() - mem = numel*element_size /1024/1024 # 32bit=4Byte, MByte + mem = numel * element_size / 1024 / 1024 # 32bit=4Byte, MByte total_mem += mem element_type = type(tensor).__name__ size = tuple(tensor.size()) @@ -188,34 +197,39 @@ def _mem_report(tensors, mem_type): # element_type, # size, # mem) ) - print('Type: %s Total Tensors: %d \tUsed Memory Space: %.2f MBytes' % (mem_type, total_numel, total_mem) ) + print( + "Type: %s Total Tensors: %d \tUsed Memory Space: %.2f MBytes" + % (mem_type, total_numel, total_mem) + ) gc.collect() LEN = 65 objects = gc.get_objects() - #print('%s\t%s\t\t\t%s' %('Element type', 'Size', 'Used MEM(MBytes)') ) + # print('%s\t%s\t\t\t%s' %('Element type', 'Size', 'Used MEM(MBytes)') ) tensors = [obj for obj in objects if torch.is_tensor(obj)] cuda_tensors = [t for t in tensors if t.is_cuda] host_tensors = [t for t in tensors if not t.is_cuda] - _mem_report(cuda_tensors, 'GPU') - _mem_report(host_tensors, 'CPU') - print('='*LEN) + _mem_report(cuda_tensors, "GPU") + _mem_report(host_tensors, "CPU") + print("=" * LEN) + def grad_norm(params): - grad_norm = 0. + grad_norm = 0.0 for p in params: if p.grad is not None: - grad_norm += torch.sum(p.grad ** 2) + grad_norm += torch.sum(p.grad**2) return torch.sqrt(grad_norm) + def print_leaf_nodes(grad_fn, id_set): if grad_fn is None: return - if hasattr(grad_fn, 'variable'): + if hasattr(grad_fn, "variable"): mem_id = id(grad_fn.variable) - if not(mem_id in id_set): - print('is leaf:', grad_fn.variable.is_leaf) + if not (mem_id in id_set): + print("is leaf:", grad_fn.variable.is_leaf) print(grad_fn.variable) id_set.add(mem_id) @@ -223,10 +237,54 @@ def print_leaf_nodes(grad_fn, id_set): for i in range(len(grad_fn.next_functions)): print_leaf_nodes(grad_fn.next_functions[i][0], id_set) + def policy_kl(p0_mu, p0_sigma, p1_mu, p1_sigma): - c1 = torch.log(p1_sigma/p0_sigma + 1e-5) - c2 = (p0_sigma**2 + (p1_mu - p0_mu)**2)/(2.0 * (p1_sigma**2 + 1e-5)) + c1 = torch.log(p1_sigma / p0_sigma + 1e-5) + c2 = (p0_sigma**2 + (p1_mu - p0_mu) ** 2) / (2.0 * (p1_sigma**2 + 1e-5)) c3 = -1.0 / 2.0 kl = c1 + c2 + c3 - kl = kl.sum(dim=-1) # returning mean between all steps of sum between all actions - return kl.mean() \ No newline at end of file + kl = kl.sum(dim=-1) # returning mean between all steps of sum between all actions + return kl.mean() + + +def jacobian(f, input): + """Computes the jacobian of function f with respect to the input""" + num_envs, input_dim = input.shape + output = f(input) + # outputs should be of shape [] + output_dim = output.shape[1] + jacobians = torch.empty((num_envs, output_dim, input_dim), dtype=torch.float32) + for out_idx in range(output_dim): + select_index = torch.zeros(output_dim) + select_index[out_idx] = 1.0 + e = torch.tile(select_index, (num_envs, 1)).cuda() + output.backward(e, retain_graph=True) + vector_jacobian = input.grad + jacobians[:, out_idx, :] = vector_jacobian.view(num_envs, input_dim) + input.grad.zero_() + + return jacobians + + +def jacobian2(output, input, max_out_dim=None): + """Computes the jacobian of output tensor with respect to the input""" + num_envs, input_dim = input.shape + output_dim = output.shape[1] + if max_out_dim: + output_dim = min(output_dim, max_out_dim) + jacobians = torch.zeros((num_envs, output_dim, input_dim), dtype=torch.float32) + for out_idx in range(output_dim): + select_index = torch.zeros(output.shape[1]) + select_index[out_idx] = 1.0 + e = torch.tile(select_index, (num_envs, 1)).cuda() + # retain = out_idx != output_dim - 1 # NOTE: experimental + try: + (grad,) = torch.autograd.grad( + outputs=output, inputs=input, grad_outputs=e, retain_graph=True + ) + jacobians[:, out_idx, :] = grad.view(num_envs, input_dim) + except RuntimeError as err: + print(f"WARN: Couldn't compute jacobian for {out_idx} index") + print(err) + + return jacobians diff --git a/src/shac/utils/warp_utils.py b/src/shac/utils/warp_utils.py index e6931fa7..7feaae01 100644 --- a/src/shac/utils/warp_utils.py +++ b/src/shac/utils/warp_utils.py @@ -30,12 +30,12 @@ def float_assign(a, b): @wp.kernel def assign_act_kernel( - b: wp.array2d(dtype=float), + b: wp.array(dtype=float), # outputs a: wp.array(dtype=float), ): tid = wp.tid() - a[2 * tid] = b[tid, 0] + a[2 * tid] = b[tid] a[2 * tid + 1] = 0.0 @@ -226,19 +226,15 @@ def forward( ctx.body_q.requires_grad = True ctx.body_qd.requires_grad = True - ctx.model.shape_materials.ke.requires_grad = True - ctx.model.shape_materials.kd.requires_grad = True - ctx.model.shape_materials.kf.requires_grad = True - ctx.model.shape_materials.mu.requires_grad = True - ctx.model.shape_materials.restitution.requires_grad = True - with ctx.tape: float_assign_joint_act(ctx.model.joint_act, ctx.act) # transform_assign(ctx.state_in.body_q, ctx.body_q) # spatial_assign(ctx.state_in.body_qd, ctx.body_qd) # eval_FK and eval_IK together break body integration due to small errors in # revolute joints, therefore DO NOT call in forward pass - # wp.sim.eval_fk(ctx.model, ctx.model.joint_q, ctx.model.joint_qd, None, state_in) + # wp.sim.eval_fk( + # ctx.model, ctx.model.joint_q, ctx.model.joint_qd, None, state_in + # ) for _ in range(substeps - 1): state_in.clear_forces() state_temp = model.state(requires_grad=True) @@ -250,7 +246,6 @@ def forward( requires_grad=True, ) state_in = state_temp - state_in.clear_forces() # updates joint_q joint_qd ctx.state_out = integrator.simulate( ctx.model, state_in, state_out, dt / float(substeps), requires_grad=True @@ -258,37 +253,37 @@ def forward( # TODO: Check if calling collide after running substeps is correct if ctx.model.ground: wp.sim.collide(ctx.model, ctx.state_out) - # wp.sim.eval_ik(ctx.model, ctx.state_out, ctx.joint_q_end, ctx.joint_qd_end) - - wp.launch( - kernel=get_joint_q, - dim=model.joint_count, - device=model.device, - inputs=[ - ctx.state_out.body_q, - model.joint_type, - model.joint_parent, - model.joint_X_p, - model.joint_axis, - 0.0, - ], - outputs=[ctx.joint_q_end], - ) - wp.launch( - kernel=get_joint_qd, - dim=model.joint_count, - device=model.device, - inputs=[ - ctx.state_out.body_q, - ctx.state_out.body_qd, - model.joint_qd_start, - model.joint_type, - model.joint_parent, - model.joint_X_p, - model.joint_axis, - ], - outputs=[ctx.joint_qd_end], - ) + wp.sim.eval_ik(ctx.model, ctx.state_out, ctx.joint_q_end, ctx.joint_qd_end) + + # wp.launch( + # kernel=get_joint_q, + # dim=model.joint_count, + # device=model.device, + # inputs=[ + # ctx.state_out.body_q, + # model.joint_type, + # model.joint_parent, + # model.joint_X_p, + # model.joint_axis, + # 0.0, + # ], + # outputs=[ctx.joint_q_end], + # ) + # wp.launch( + # kernel=get_joint_qd, + # dim=model.joint_count, + # device=model.device, + # inputs=[ + # ctx.state_out.body_q, + # ctx.state_out.body_qd, + # model.joint_qd_start, + # model.joint_type, + # model.joint_parent, + # model.joint_X_p, + # model.joint_axis, + # ], + # outputs=[ctx.joint_qd_end], + # ) joint_q_end = wp.to_torch(ctx.joint_q_end) joint_qd_end = wp.to_torch(ctx.joint_qd_end) return ( @@ -299,7 +294,6 @@ def forward( @staticmethod def backward(ctx, adj_joint_q, adj_joint_qd, _a): - # map incoming Torch grads to our output variables ctx.joint_q_end.grad = wp.from_torch(adj_joint_q) ctx.joint_qd_end.grad = wp.from_torch(adj_joint_qd) @@ -309,8 +303,8 @@ def backward(ctx, adj_joint_q, adj_joint_qd, _a): # Unnecessary copying of grads, grads should already be recorded by context body_q_grad = wp.to_torch(ctx.tape.gradients[ctx.state_in.body_q]).clone() body_qd_grad = wp.to_torch(ctx.tape.gradients[ctx.state_in.body_qd]).clone() - ctx.body_q.grad = wp.from_torch(body_q_grad) - ctx.body_qd.grad = wp.from_torch(body_qd_grad) + # ctx.body_q.grad = wp.from_torch(body_q_grad) + # ctx.body_qd.grad = wp.from_torch(body_qd_grad) ctx.tape.zero() # return adjoint w.r.t. inputs