diff --git a/.constraints/py3.6.txt b/.constraints/py3.6.txt
index 5e71e1899..4075796db 100644
--- a/.constraints/py3.6.txt
+++ b/.constraints/py3.6.txt
@@ -14,15 +14,17 @@ async-generator==1.10
attrs==20.3.0
babel==2.9.1
backcall==0.2.0
+backports.entry-points-selectable==1.1.0
beautifulsoup4==4.9.3
black==21.6b0
bleach==3.3.0
certifi==2021.5.30
cffi==1.14.6
cfgv==3.3.0
-chardet==4.0.0
+charset-normalizer==2.0.1
click==8.0.1
colorama==0.4.4
+commonmark==0.9.1
contextvars==2.4
coverage==5.5
cycler==0.10.0
@@ -48,10 +50,10 @@ future==0.18.2
gitdb==4.0.7
gitpython==3.1.18
gprof2dot==2021.2.21
-graphviz==0.16
+graphviz==0.17
hepunits==2.1.1
identify==2.2.11
-idna==2.10
+idna==3.2
imagesize==1.2.0
immutables==0.15
importlib-metadata==4.6.1
@@ -114,6 +116,7 @@ pexpect==4.8.0
pickleshare==0.7.5
pillow==8.3.1
pip-tools==6.2.0
+platformdirs==2.0.2
pluggy==0.13.1
pre-commit==2.13.0
prometheus-client==0.11.0
@@ -138,16 +141,17 @@ pytest-notebook==0.6.1
pytest-profiling==1.7.0
pytest-xdist==2.3.0
python-constraint==1.4.0
-python-dateutil==2.8.1
+python-dateutil==2.8.2
pytz==2021.1
pyyaml==5.4.1
pyzmq==22.1.0
-qrules==0.9.0
+qrules==0.9.1
radon==5.0.1
regex==2021.7.6
-requests==2.25.1
+requests==2.26.0
requests-unixsocket==0.2.0
restructuredtext-lint==1.3.2
+rich==10.6.0
send2trash==1.7.1
six==1.16.0
smmap==4.0.0
@@ -182,7 +186,7 @@ typed-ast==1.4.3
types-docutils==0.1.7
typing-extensions==3.10.0.0 ; python_version < "3.8.0"
urllib3==1.26.6
-virtualenv==20.4.7
+virtualenv==20.5.0
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.1.0
diff --git a/.constraints/py3.7.txt b/.constraints/py3.7.txt
index 4a4a21a97..8aa541fe5 100644
--- a/.constraints/py3.7.txt
+++ b/.constraints/py3.7.txt
@@ -14,15 +14,17 @@ async-generator==1.10
attrs==20.3.0
babel==2.9.1
backcall==0.2.0
+backports.entry-points-selectable==1.1.0
beautifulsoup4==4.9.3
black==21.6b0
bleach==3.3.0
certifi==2021.5.30
cffi==1.14.6
cfgv==3.3.0
-chardet==4.0.0
+charset-normalizer==2.0.1
click==8.0.1
colorama==0.4.4
+commonmark==0.9.1
coverage==5.5
cycler==0.10.0
debugpy==1.3.0
@@ -47,10 +49,10 @@ future==0.18.2
gitdb==4.0.7
gitpython==3.1.18
gprof2dot==2021.2.21
-graphviz==0.16
+graphviz==0.17
hepunits==2.1.1
identify==2.2.11
-idna==2.10
+idna==3.2
imagesize==1.2.0
importlib-metadata==3.10.1
importlib-resources==5.2.0
@@ -113,6 +115,7 @@ pexpect==4.8.0
pickleshare==0.7.5
pillow==8.3.1
pip-tools==6.2.0
+platformdirs==2.0.2
pluggy==0.13.1
pre-commit==2.13.0
prometheus-client==0.11.0
@@ -137,16 +140,17 @@ pytest-notebook==0.6.1
pytest-profiling==1.7.0
pytest-xdist==2.3.0
python-constraint==1.4.0
-python-dateutil==2.8.1
+python-dateutil==2.8.2
pytz==2021.1
pyyaml==5.4.1
pyzmq==22.1.0
-qrules==0.9.0
+qrules==0.9.1
radon==5.0.1
regex==2021.7.6
-requests==2.25.1
+requests==2.26.0
requests-unixsocket==0.2.0
restructuredtext-lint==1.3.2
+rich==10.6.0
send2trash==1.7.1
six==1.16.0
smmap==4.0.0
@@ -181,7 +185,7 @@ typed-ast==1.4.3
types-docutils==0.1.7
typing-extensions==3.10.0.0 ; python_version < "3.8.0"
urllib3==1.26.6
-virtualenv==20.4.7
+virtualenv==20.5.0
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.1.0
diff --git a/.constraints/py3.8.txt b/.constraints/py3.8.txt
index 56f97bffd..9942f2e43 100644
--- a/.constraints/py3.8.txt
+++ b/.constraints/py3.8.txt
@@ -14,15 +14,17 @@ async-generator==1.10
attrs==20.3.0
babel==2.9.1
backcall==0.2.0
+backports.entry-points-selectable==1.1.0
beautifulsoup4==4.9.3
black==21.6b0
bleach==3.3.0
certifi==2021.5.30
cffi==1.14.6
cfgv==3.3.0
-chardet==4.0.0
+charset-normalizer==2.0.1
click==8.0.1
colorama==0.4.4
+commonmark==0.9.1
coverage==5.5
cycler==0.10.0
debugpy==1.3.0
@@ -47,10 +49,10 @@ future==0.18.2
gitdb==4.0.7
gitpython==3.1.18
gprof2dot==2021.2.21
-graphviz==0.16
+graphviz==0.17
hepunits==2.1.1
identify==2.2.11
-idna==2.10
+idna==3.2
imagesize==1.2.0
importlib-metadata==4.6.1
importlib-resources==5.2.0
@@ -113,6 +115,7 @@ pexpect==4.8.0
pickleshare==0.7.5
pillow==8.3.1
pip-tools==6.2.0
+platformdirs==2.0.2
pluggy==0.13.1
pre-commit==2.13.0
prometheus-client==0.11.0
@@ -137,16 +140,17 @@ pytest-notebook==0.6.1
pytest-profiling==1.7.0
pytest-xdist==2.3.0
python-constraint==1.4.0
-python-dateutil==2.8.1
+python-dateutil==2.8.2
pytz==2021.1
pyyaml==5.4.1
pyzmq==22.1.0
-qrules==0.9.0
+qrules==0.9.1
radon==5.0.1
regex==2021.7.6
-requests==2.25.1
+requests==2.26.0
requests-unixsocket==0.2.0
restructuredtext-lint==1.3.2
+rich==10.6.0
send2trash==1.7.1
six==1.16.0
smmap==4.0.0
@@ -180,7 +184,7 @@ traitlets==5.0.5
types-docutils==0.1.7
typing-extensions==3.10.0.0
urllib3==1.26.6
-virtualenv==20.4.7
+virtualenv==20.5.0
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.1.0
diff --git a/.constraints/py3.9.txt b/.constraints/py3.9.txt
index b5712b61c..69bede48e 100644
--- a/.constraints/py3.9.txt
+++ b/.constraints/py3.9.txt
@@ -14,15 +14,17 @@ async-generator==1.10
attrs==20.3.0
babel==2.9.1
backcall==0.2.0
+backports.entry-points-selectable==1.1.0
beautifulsoup4==4.9.3
black==21.6b0
bleach==3.3.0
certifi==2021.5.30
cffi==1.14.6
cfgv==3.3.0
-chardet==4.0.0
+charset-normalizer==2.0.1
click==8.0.1
colorama==0.4.4
+commonmark==0.9.1
coverage==5.5
cycler==0.10.0
debugpy==1.3.0
@@ -47,10 +49,10 @@ future==0.18.2
gitdb==4.0.7
gitpython==3.1.18
gprof2dot==2021.2.21
-graphviz==0.16
+graphviz==0.17
hepunits==2.1.1
identify==2.2.11
-idna==2.10
+idna==3.2
imagesize==1.2.0
importlib-metadata==4.6.1
importlib-resources==5.2.0
@@ -113,6 +115,7 @@ pexpect==4.8.0
pickleshare==0.7.5
pillow==8.3.1
pip-tools==6.2.0
+platformdirs==2.0.2
pluggy==0.13.1
pre-commit==2.13.0
prometheus-client==0.11.0
@@ -137,16 +140,17 @@ pytest-notebook==0.6.1
pytest-profiling==1.7.0
pytest-xdist==2.3.0
python-constraint==1.4.0
-python-dateutil==2.8.1
+python-dateutil==2.8.2
pytz==2021.1
pyyaml==5.4.1
pyzmq==22.1.0
-qrules==0.9.0
+qrules==0.9.1
radon==5.0.1
regex==2021.7.6
-requests==2.25.1
+requests==2.26.0
requests-unixsocket==0.2.0
restructuredtext-lint==1.3.2
+rich==10.6.0
send2trash==1.7.1
six==1.16.0
smmap==4.0.0
@@ -180,7 +184,7 @@ traitlets==5.0.5
types-docutils==0.1.7
typing-extensions==3.10.0.0
urllib3==1.26.6
-virtualenv==20.4.7
+virtualenv==20.5.0
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.1.0
diff --git a/.cspell.json b/.cspell.json
index ec0cb1894..3239f96cb 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -156,6 +156,7 @@
"heurisch",
"imag",
"iplt",
+ "ipykernel",
"ipyplot",
"ipywidgets",
"isfunction",
@@ -166,6 +167,7 @@
"katex",
"kernelspec",
"kmatrix",
+ "kutschke",
"kwargs",
"linestyle",
"linewidth",
@@ -185,6 +187,7 @@
"nbsphinx",
"ndarray",
"noqa",
+ "nowrap",
"nrows",
"nsimplify",
"numpycode",
@@ -202,6 +205,7 @@
"pyproject",
"pyright",
"pytestconfig",
+ "richman",
"rightarrow",
"risch",
"rtfd",
diff --git a/docs/bibliography.bib b/docs/bibliography.bib
index 7827059c3..0eca8a83d 100644
--- a/docs/bibliography.bib
+++ b/docs/bibliography.bib
@@ -47,6 +47,16 @@ @article{chungPartialWaveAnalysis1995
number = {5}
}
+@techreport{chungSpinFormalismsUpdated2014,
+ title = {Spin {{Formalisms}} ({{Updated Version}})},
+ author = {Chung, Suh-Urk},
+ year = {2014},
+ month = jul,
+ pages = {BNL--76975-2006-IR, 890945},
+ institution = {{Brookhaven National Laboratory}},
+ url = {https://suchung.web.cern.ch/spinfm1.pdf}
+}
+
@article{flatteCoupledchannelAnalysisPi1976,
title = {Coupled-Channel Analysis of the Πη and {{KK̄}} Systems near {{KK̄}} Threshold},
author = {Flatté, S.M.},
@@ -61,6 +71,14 @@ @article{flatteCoupledchannelAnalysisPi1976
number = {2}
}
+@misc{kutschkeAngularDistributionCookbook1996,
+ title = {An {{Angular Distribution Cookbook}}},
+ author = {Kutschke, Rob},
+ year = {1996},
+ month = jan,
+ url = {https://home.fnal.gov/~kutschke/Angdist/angdist.ps}
+}
+
@misc{meyerMatrixTutorial2008,
title = {A 𝐾-{{Matrix Tutorial}}},
author = {Meyer, Curtis A.},
@@ -79,4 +97,12 @@ @phdthesis{pychyGekoppeltePartialwellenanalyseAnnihilationen2016
school = {Ruhr-Universität Bochum}
}
+@misc{richmanExperimenterGuideHelicity1984,
+ title = {An {{Experimenter}}'s {{Guide}} to the {{Helicity Formalism}}},
+ author = {Richman, Jeffrey D.},
+ year = {1984},
+ month = jun,
+ url = {https://inspirehep.net/literature/202987}
+}
+
diff --git a/docs/usage.md b/docs/usage.md
index cab5e433f..38ef3d220 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -11,5 +11,6 @@ maxdepth: 2
usage/amplitude
usage/modify
usage/interactive
+usage/formalism
usage/dynamics
```
diff --git a/docs/usage/formalism.ipynb b/docs/usage/formalism.ipynb
new file mode 100644
index 000000000..2d437e5d6
--- /dev/null
+++ b/docs/usage/formalism.ipynb
@@ -0,0 +1,358 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "hideCode": true,
+ "hideOutput": true,
+ "hidePrompt": true,
+ "jupyter": {
+ "source_hidden": true
+ },
+ "slideshow": {
+ "slide_type": "skip"
+ },
+ "tags": [
+ "remove-cell"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "%config Completer.use_jedi = False\n",
+ "%config InlineBackend.figure_formats = ['svg']\n",
+ "import os\n",
+ "\n",
+ "STATIC_WEB_PAGE = {\"EXECUTE_NB\", \"READTHEDOCS\"}.intersection(os.environ)\n",
+ "\n",
+ "# Install on Google Colab\n",
+ "import subprocess\n",
+ "import sys\n",
+ "\n",
+ "from IPython import get_ipython\n",
+ "\n",
+ "install_packages = \"google.colab\" in str(get_ipython())\n",
+ "if install_packages:\n",
+ " for package in [\"ampform[doc]\", \"graphviz\"]:\n",
+ " subprocess.check_call(\n",
+ " [sys.executable, \"-m\", \"pip\", \"install\", package]\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Helicity versus canonical"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-cell"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import logging\n",
+ "\n",
+ "import graphviz\n",
+ "import qrules\n",
+ "import sympy as sp\n",
+ "from IPython.display import HTML, Math\n",
+ "from rich.table import Table\n",
+ "\n",
+ "import ampform\n",
+ "\n",
+ "LOGGER = logging.getLogger()\n",
+ "LOGGER.setLevel(logging.ERROR)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this notebook, we have a look at the decay\n",
+ "\n",
+ "$$D_1(2420)^0 \\to a_1(1260)^+ K^- \\to (K^+K^0)K^-$$\n",
+ "\n",
+ "in order to see the difference between a {class}`.HelicityModel` formulated in the **canonical** basis and one formulated in the **helicity** basis. To simplify things, we only look at spin projection $+1$ for $D_1(2420)^0$, because the intensities for each of the spin projections of $D_1(2420)^0$ are incoherent, no matter which spin formalism we choose.\n",
+ "\n",
+ ":::{tip}\n",
+ "\n",
+ "For more information about the helicity formalism, see {cite}`chungSpinFormalismsUpdated2014`, {cite}`richmanExperimenterGuideHelicity1984`, and {cite}`kutschkeAngularDistributionCookbook1996`.\n",
+ "\n",
+ ":::\n",
+ "\n",
+ "First, we use {func}`qrules.generate_transitions` to generate a {class}`~qrules.transition.ReactionInfo` instance for both formalisms:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "def generate_transitions(formalism: str):\n",
+ " reaction = qrules.generate_transitions(\n",
+ " initial_state=(\"D(1)(2420)0\", [+1]),\n",
+ " final_state=[\"K+\", \"K-\", \"K~0\"],\n",
+ " allowed_intermediate_particles=[\"a(1)(1260)+\"],\n",
+ " formalism=formalism,\n",
+ " )\n",
+ " builder = ampform.get_builder(reaction)\n",
+ " return builder.formulate()\n",
+ "\n",
+ "\n",
+ "cano_model = generate_transitions(\"canonical-helicity\")\n",
+ "heli_model = generate_transitions(\"helicity\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From {attr}`.components` and {attr}`.parameter_defaults`, we can see that the canonical formalism has a larger number of amplitudes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "table = Table(show_edge=False)\n",
+ "table.add_column(\"Formalism\")\n",
+ "table.add_column(\"Coefficients\", justify=\"right\")\n",
+ "table.add_column(\"Amplitudes\", justify=\"right\")\n",
+ "table.add_row(\n",
+ " \"Canonical\",\n",
+ " str(len(cano_model.parameter_defaults)),\n",
+ " str(len(cano_model.components) - 1),\n",
+ ")\n",
+ "table.add_row(\n",
+ " \"Helicity\",\n",
+ " str(len(heli_model.parameter_defaults)),\n",
+ " str(len(heli_model.components) - 1),\n",
+ ")\n",
+ "table"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The reason for this is that canonical basis distinguishes amplitudes over their $LS$-combinations. This becomes clear if we define $a$ to be the amplitude _without coefficient_ ($A = C a$), and consider what the full, coherent intensity looks like.\n",
+ "\n",
+ "If we write the full intensity as $I = \\left|\\sum_i A_i\\right|^2$, then we have, in the case of the **canonical** basis:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "def extract_amplitude_substitutions(model):\n",
+ " amplitude_to_symbol = {}\n",
+ " for name, expr in model.components.items():\n",
+ " if not name.startswith(\"A\"):\n",
+ " continue\n",
+ " for par in model.parameter_defaults:\n",
+ " if par in expr.args:\n",
+ " expr /= par\n",
+ " name = \"a\" + name[1:]\n",
+ " symbol = sp.Symbol(name)\n",
+ " amplitude_to_symbol[expr] = symbol\n",
+ " return amplitude_to_symbol\n",
+ "\n",
+ "\n",
+ "cano_amplitude_to_symbol = extract_amplitude_substitutions(cano_model)\n",
+ "heli_amplitude_to_symbol = extract_amplitude_substitutions(heli_model)\n",
+ "\n",
+ "\n",
+ "def render_amplitude_summation(model):\n",
+ " amplitude_to_symbol = extract_amplitude_substitutions(model)\n",
+ " collected_expr = sp.collect(\n",
+ " model.expression.subs(amplitude_to_symbol).args[0].args[0],\n",
+ " tuple(model.parameter_defaults),\n",
+ " )\n",
+ " terms = collected_expr.args\n",
+ " latex = \"\"\n",
+ " latex += R\"\\begin{align}\"\n",
+ " latex += fR\"\\sum_i A_i & = {sp.latex(terms[0])}\\\\\"\n",
+ " for term in terms[1:]:\n",
+ " latex += fR\"& + {sp.latex(term)} \\\\\"\n",
+ " latex += R\"\\end{align}\"\n",
+ " return Math(latex)\n",
+ "\n",
+ "\n",
+ "render_amplitude_summation(cano_model)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the **helicity** basis, the $LS$-combinations have been summed over already and we can only see an amplitude for each helicity:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "render_amplitude_summation(heli_model)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Amplitudes in the **canonical** basis are formulated with regard to their $LS$-couplings. As such, they contain additional [Clebsch-Gordan coefficients](https://en.wikipedia.org/wiki/Clebsch%E2%80%93Gordan_coefficients) that serve as _expansion coefficients_."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "def extract_amplitudes(model):\n",
+ " return {\n",
+ " expr: sp.Symbol(name)\n",
+ " for name, expr in model.components.items()\n",
+ " if name.startswith(\"A\")\n",
+ " }\n",
+ "\n",
+ "\n",
+ "cano_amplitudes = extract_amplitudes(cano_model)\n",
+ "heli_amplitudes = extract_amplitudes(heli_model)\n",
+ "\n",
+ "expression, symbol = next(iter(cano_amplitude_to_symbol.items()))\n",
+ "display(symbol, Math(fR\"\\quad = {sp.latex(expression)}\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the **helicity** basis, these Clebsch-Gordan coefficients and [Wigner-$D$ functions](https://en.wikipedia.org/wiki/Wigner_D-matrix) have been summed up, leaving only a Wigner-$D$ for each node in the decay chain (two in this case):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "expression, symbol = next(iter(heli_amplitude_to_symbol.items()))\n",
+ "display(symbol, Math(fR\"\\quad = {sp.latex(expression)}\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see this also from the original {class}`~qrules.transition.ReactionInfo` objects. Let's select only the {attr}`~qrules.transition.ReactionInfo.transitions` where the $a_1(1260)^+$ resonance has spin projection $-1$ (taken to be helicity $-1$ in the helicity formalism). We then see just one {class}`~qrules.transition.StateTransition` in the helicity basis and three transitions in the canonical basis:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "def render_selection(model):\n",
+ " transitions = model.adapter.reaction_info.transitions\n",
+ " selection = filter(\n",
+ " lambda s: s.states[3].spin_projection == -1, transitions\n",
+ " )\n",
+ " dot = qrules.io.asdot(\n",
+ " selection, render_node=True, render_final_state_id=False\n",
+ " )\n",
+ " return graphviz.Source(dot)\n",
+ "\n",
+ "\n",
+ "display(\n",
+ " HTML(\"Helicity basis:\"),\n",
+ " render_selection(heli_model),\n",
+ " HTML(\"Canonical basis:\"),\n",
+ " render_selection(cano_model),\n",
+ ")"
+ ]
+ }
+ ],
+ "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.8.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/setup.cfg b/setup.cfg
index d164bb2c2..706d778e8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -63,6 +63,7 @@ doc =
mdit-py-plugins < 0.2.7 # temporary fix
mpl_interactions
myst-nb >= 0.11 # myst_enable_extensions
+ rich
Sphinx >= 3
sphinx-book-theme
sphinx-copybutton