{ "cells": [ { "cell_type": "markdown", "id": "2fd419a9", "metadata": {}, "source": [ "# Gymnasium integration\n", "[Gymnasium](https://gymnasium.farama.org/) is an API standard for reinforcement learning with a diverse collection of reference environments. Stormvogel supports some integration with gymnasium. In particular, you can construct explicit models from the gymnasium environmnents under Gymnasium's [ToyText](https://gymnasium.farama.org/environments/toy_text/) (except Blackjack)." ] }, { "cell_type": "markdown", "id": "3c2d4d13", "metadata": {}, "source": [ "## FrozenLake\n", "Let us create one of these environments, called FrozenLake.\n", "Our agent wants to get to the present. Currently, it just chooses a random action." ] }, { "cell_type": "code", "execution_count": 1, "id": "449b20a9", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:23.313105Z", "iopub.status.busy": "2026-10-01T12:39:23.312813Z", "iopub.status.idle": "2026-10-01T12:39:24.040555Z", "shell.execute_reply": "2026-10-01T12:39:24.040012Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import gymnasium as gym\n", "from stormvogel.extensions.gym_grid import *\n", "from stormvogel.extensions.gifs import embed_gif\n", "from stormvogel import *\n", "\n", "env = gym.make(\n", " \"FrozenLake-v1\", render_mode=\"rgb_array\", is_slippery=False\n", ") # Set `is_slippery=True` for stochastic behavior\n", "filename = gymnasium_render_model_gif(env, filename=\"ice1\")\n", "embed_gif(filename)" ] }, { "cell_type": "markdown", "id": "d7145507", "metadata": {}, "source": [ "We can convert it into an explicit MDP as follows. Each state has a label that relates to the coordinates of the tile." ] }, { "cell_type": "code", "execution_count": 2, "id": "00ae1fed", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:24.042494Z", "iopub.status.busy": "2026-10-01T12:39:24.042259Z", "iopub.status.idle": "2026-10-01T12:39:24.234555Z", "shell.execute_reply": "2026-10-01T12:39:24.234023Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", " \n", " Network\n", " \n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sv_model = gymnasium_grid_to_stormvogel(env, GRID_ACTION_LABEL_MAP)\n", "# GRID_ACTION_LABEL_MAP = {0: \"←\", 1: \"↓\", 2: \"→\", 3: \"↑\", 4: \"pickup\", 5: \"dropoff\"} (map between gymnasium action ids and labels)\n", "show(sv_model)" ] }, { "cell_type": "markdown", "id": "14b17afe", "metadata": {}, "source": [ "Now, let's do some model checking to calculate a strategy to solve the puzzle. We will tell the model checker to maximize the probability of getting to the target state (the present)." ] }, { "cell_type": "code", "execution_count": 3, "id": "6919c0db", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:24.256120Z", "iopub.status.busy": "2026-10-01T12:39:24.255805Z", "iopub.status.idle": "2026-10-01T12:39:24.287125Z", "shell.execute_reply": "2026-10-01T12:39:24.286533Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", " \n", " Network\n", " \n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "res = model_checking(sv_model, 'Pmax=? [F \"target\"]')\n", "vis2 = show(sv_model, result=res)" ] }, { "cell_type": "markdown", "id": "e1a99f10", "metadata": {}, "source": [ "Let's highlight the path to see what the scheduler is doing." ] }, { "cell_type": "code", "execution_count": 4, "id": "716188a8", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:24.305518Z", "iopub.status.busy": "2026-10-01T12:39:24.305271Z", "iopub.status.idle": "2026-10-01T12:39:39.359322Z", "shell.execute_reply": "2026-10-01T12:39:39.358804Z" } }, "outputs": [], "source": [ "path = simulate_path(sv_model, scheduler=res.scheduler, steps=20)\n", "vis2.highlight_path(path, color=\"orange\")" ] }, { "cell_type": "markdown", "id": "7f1804f4", "metadata": {}, "source": [ "Alternatively, we can show what our scheduler does in the frozen lake environment itself." ] }, { "cell_type": "code", "execution_count": 5, "id": "a40f12dc", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.361193Z", "iopub.status.busy": "2026-10-01T12:39:39.361005Z", "iopub.status.idle": "2026-10-01T12:39:39.407471Z", "shell.execute_reply": "2026-10-01T12:39:39.406869Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from stormvogel.extensions.gym_grid import *\n", "\n", "gs = to_gymnasium_scheduler(sv_model, res.scheduler, GRID_ACTION_LABEL_MAP)\n", "filename = gymnasium_render_model_gif(env, gs, filename=\"ice2\")\n", "embed_gif(filename)" ] }, { "cell_type": "markdown", "id": "ea01920e", "metadata": {}, "source": [ "We can also define a function to act as the scheduler on the model and convert it to a gymnasium scheduler. This one just keeps going in a loop..." ] }, { "cell_type": "code", "execution_count": 6, "id": "a4c286d5", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.409460Z", "iopub.status.busy": "2026-10-01T12:39:39.409269Z", "iopub.status.idle": "2026-10-01T12:39:39.713124Z", "shell.execute_reply": "2026-10-01T12:39:39.712485Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from stormvogel.model import Action\n", "\n", "\n", "def my_scheduler(s: stormvogel.model.State):\n", " # \"←\" \"↓\" \"→\" \"↑\"\n", " if s.is_initial():\n", " return Action(\"→\")\n", " env_id = s.get_valuation(stormvogel.model.Variable(\"env_id\"))\n", " x, y = to_coordinate(env_id, env)\n", " if x < 2 and y == 0:\n", " return Action(\"→\")\n", " elif x == 2 and y < 2:\n", " return Action(\"↓\")\n", " elif x > 0 and y == 2:\n", " return Action(\"←\")\n", " else:\n", " return Action(\"↑\")\n", "\n", "\n", "gs = to_gymnasium_scheduler(sv_model, my_scheduler, GRID_ACTION_LABEL_MAP)\n", "filename = gymnasium_render_model_gif(env, gs, filename=\"ice3\")\n", "embed_gif(filename)" ] }, { "cell_type": "markdown", "id": "1c14dca9", "metadata": {}, "source": [ "## CliffWalking\n", "CliffWalking is a slightly more boring version of FrozenLake. You can apply the same principles that we just applied to FrozenLake." ] }, { "cell_type": "code", "execution_count": 7, "id": "66b8036e", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.715144Z", "iopub.status.busy": "2026-10-01T12:39:39.714954Z", "iopub.status.idle": "2026-10-01T12:39:39.720968Z", "shell.execute_reply": "2026-10-01T12:39:39.720359Z" } }, "outputs": [], "source": [ "import gymnasium as gym\n", "from stormvogel.extensions.gym_grid import *\n", "\n", "env = gym.make(\"CliffWalking-v1\", render_mode=\"rgb_array\")" ] }, { "cell_type": "code", "execution_count": 8, "id": "fb58a569", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.722692Z", "iopub.status.busy": "2026-10-01T12:39:39.722525Z", "iopub.status.idle": "2026-10-01T12:39:39.758574Z", "shell.execute_reply": "2026-10-01T12:39:39.757931Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", " \n", " Network\n", " \n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sv_model = gymnasium_grid_to_stormvogel(env, GRID_ACTION_LABEL_MAP)\n", "show(sv_model)" ] }, { "cell_type": "code", "execution_count": 9, "id": "3bf1f1ae", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.778224Z", "iopub.status.busy": "2026-10-01T12:39:39.777980Z", "iopub.status.idle": "2026-10-01T12:39:39.968377Z", "shell.execute_reply": "2026-10-01T12:39:39.967867Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from stormvogel.stormpy_utils.model_checking import model_checking\n", "\n", "res = model_checking(sv_model, 'Pmax=? [F \"target\"]')\n", "gs = to_gymnasium_scheduler(sv_model, res.scheduler, GRID_ACTION_LABEL_MAP)\n", "filename = gymnasium_render_model_gif(env, gs, filename=\"cliff\")\n", "embed_gif(filename)" ] }, { "cell_type": "markdown", "id": "9c2981af", "metadata": {}, "source": [ "## Taxi\n", "In the Taxi scenario, a taxi has to pick up passengers and transport them to the hotel. The position of the target, passenger and taxi are chosen at random." ] }, { "cell_type": "code", "execution_count": 10, "id": "dcb906c8", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:39.970204Z", "iopub.status.busy": "2026-10-01T12:39:39.970032Z", "iopub.status.idle": "2026-10-01T12:39:40.183509Z", "shell.execute_reply": "2026-10-01T12:39:40.183017Z" } }, "outputs": [ { "data": { "text/plain": [ "'ModelType.MDP model with 3529 states, 6049 choices, and 1005 distinct labels.'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import gymnasium as gym\n", "from stormvogel.extensions.gym_grid import *\n", "\n", "env = gym.make(\n", " \"Taxi-v4\", render_mode=\"rgb_array\"\n", ") # Set `is_slippery=True` for stochastic behavior\n", "sv_model = gymnasium_grid_to_stormvogel(env)\n", "# This model is so big that it is better not to display it.\n", "sv_model.summary()" ] }, { "cell_type": "code", "execution_count": 11, "id": "7df0ea8f", "metadata": { "execution": { "iopub.execute_input": "2026-10-01T12:39:40.185235Z", "iopub.status.busy": "2026-10-01T12:39:40.185068Z", "iopub.status.idle": "2026-10-01T12:39:41.111446Z", "shell.execute_reply": "2026-10-01T12:39:41.110790Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "target = get_target_state(env)\n", "res = model_checking(sv_model, \"Rmax=? [S]\")\n", "gs = to_gymnasium_scheduler(sv_model, res.scheduler, GRID_ACTION_LABEL_MAP)\n", "filename = gymnasium_render_model_gif(env, gs, filename=\"taxi\")\n", "embed_gif(filename)" ] } ], "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.13.15" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "0cab26ac5cec4bedbc390c6071fa6d72": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_3bce4987aa424c88a170a2b8721aad69", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "3bce4987aa424c88a170a2b8721aad69": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "49897e92b1bc4c10ba22176222191e4a": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "4a98148892124bc49e5348efb5ba4879": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "5670bcc3687f4d56a45483bee7472c8d": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_c2b303d8ddcb4f12aee989d380a05212", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "6b05ec6261ca4ec4a50a1c4f7774b306": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_dad1bbba35054589a7ea51ec913cd547", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "a8a1f4bea04d4f70bc4538bacadad0cc": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_49897e92b1bc4c10ba22176222191e4a", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "c2b303d8ddcb4f12aee989d380a05212": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "cdb2bb4d7190439893481d0c4a2f11d6": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "dad1bbba35054589a7ea51ec913cd547": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "e9983caa170843ec821b8248eb006d7c": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_4a98148892124bc49e5348efb5ba4879", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "eaa961c7765547529a293e3ea70e4723": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_cdb2bb4d7190439893481d0c4a2f11d6", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }