diff --git a/docs/source/how_to/how_to_change_plotting_backend.ipynb b/docs/source/how_to/how_to_change_plotting_backend.ipynb new file mode 100644 index 000000000..0e04d2cf8 --- /dev/null +++ b/docs/source/how_to/how_to_change_plotting_backend.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# How to change the plotting backend" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "optimagic supports various visualization libraries as plotting backends, which can be\n", + "selected using the `backend` argument." + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "::::{tab-set}\n", + "\n", + ":::{tab-item} Plotly\n", + "\n", + "The default plotting library. To select the Plotly backend explicitly, set `backend=\"plotly\"`.\n", + "\n", + "The returned figure object is a [`plotly.graph_objects.Figure`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html).\n", + "\n", + ":::\n", + "\n", + ":::{tab-item} Matplotlib\n", + "\n", + "To select the Matplotlib backend, set `backend=\"matplotlib\"`.\n", + "\n", + "The returned figure object is a [`matplotlib.axes.Axes`](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html).\n", + "\n", + ":::\n", + "\n", + "::::" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "In the following guide, we showcase the criterion plot visualized using different\n", + "backends." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "import optimagic as om\n", + "\n", + "\n", + "def sphere(x):\n", + " return x @ x\n", + "\n", + "\n", + "results = {}\n", + "for algo in [\"scipy_lbfgsb\", \"scipy_neldermead\"]:\n", + " results[algo] = om.minimize(sphere, params=np.arange(5), algorithm=algo)" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Plotly" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + ":::{note}\n", + "\n", + "**Choose the Plotly renderer according to your environment:**\n", + "\n", + "- Use `plotly.io.renderers.default = \"notebook_connected\"` in Jupyter notebooks for interactive plots.\n", + "- Use `plotly.io.renderers.default = \"browser\"` to open plots in your default web browser when running as a script.\n", + "\n", + "Refer to the [Plotly documentation](https://plotly.com/python/renderers/) for more details.\n", + "\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "\n", + "pio.renderers.default = \"notebook_connected\"\n", + "\n", + "fig = om.criterion_plot(results, backend=\"plotly\") # Also the default\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Matplotlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "ax = om.criterion_plot(results, backend=\"matplotlib\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/how_to/how_to_visualize_histories.ipynb b/docs/source/how_to/how_to_visualize_histories.ipynb index 144a35dbf..e25d2a367 100644 --- a/docs/source/how_to/how_to_visualize_histories.ipynb +++ b/docs/source/how_to/how_to_visualize_histories.ipynb @@ -77,6 +77,18 @@ "cell_type": "markdown", "id": "6", "metadata": {}, + "source": [ + ":::{note}\n", + "\n", + "For details on using other plotting backends, see [How to change the plotting backend](how_to_change_plotting_backend.ipynb).\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, "source": [ "## Compare two optimizations in a criterion plot" ] @@ -84,7 +96,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -94,16 +106,16 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ - "## Use some advanced options of criterion_plot" + "## Use some advanced options of criterion plot" ] }, { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "11", "metadata": {}, "source": [ "## Make a params plot" @@ -128,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "13", "metadata": {}, "source": [ "## Use advanced options of params plot" @@ -147,7 +159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -163,16 +175,16 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "15", "metadata": {}, "source": [ - "## criterion_plot with multistart optimization" + "## Criterion plot with multistart optimization" ] }, { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -192,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -203,7 +215,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -217,7 +229,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.10.17" } }, "nbformat": 4, diff --git a/docs/source/how_to/index.md b/docs/source/how_to/index.md index 911762e43..b8fbe8bbf 100644 --- a/docs/source/how_to/index.md +++ b/docs/source/how_to/index.md @@ -19,6 +19,7 @@ how_to_constraints how_to_globalization how_to_multistart how_to_visualize_histories +how_to_change_plotting_backend how_to_scaling how_to_logging how_to_errors_during_optimization diff --git a/src/optimagic/visualization/backends.py b/src/optimagic/visualization/backends.py index 541a39b30..402e1768e 100644 --- a/src/optimagic/visualization/backends.py +++ b/src/optimagic/visualization/backends.py @@ -1,4 +1,4 @@ -from typing import Any, Literal, Protocol, runtime_checkable +from typing import TYPE_CHECKING, Any, Literal, Protocol, runtime_checkable import plotly.graph_objects as go @@ -6,7 +6,7 @@ from optimagic.exceptions import InvalidPlottingBackendError, NotInstalledError from optimagic.visualization.plotting_utilities import LineData -if IS_MATPLOTLIB_INSTALLED: +if TYPE_CHECKING: import matplotlib.pyplot as plt @@ -79,6 +79,16 @@ def _line_plot_matplotlib( width: int | None, legend_properties: dict[str, Any] | None, ) -> "plt.Axes": + import matplotlib.pyplot as plt + + # In interactive environments (like Jupyter), explicitly enable matplotlib's + # interactive mode. If it is not enabled, matplotlib's context manager will + # revert to non-interactive mode after creating the first figure, causing + # subsequent figures to not display inline. + # See: https://github.com/matplotlib/matplotlib/issues/26716 + if plt.get_backend() == "module://matplotlib_inline.backend_inline": + plt.ion() + if template is None: template = "default"