diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index f8f7b74..0c429df 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -24,6 +24,7 @@
* Added references and examples to the docstrings. Slight modification to the docstring format [(#47)](https://github.com/polyquantique/haarpy/pull/47).
* `get_conjugacy_class()` has been sped up [(#51)](https://github.com/polyquantique/haarpy/pull/51).
* `weingarten_...()` and `haar_integral_...()` have been sped up by building logic to simplify rational functions in `utils._simplify` [(#53)](https://github.com/polyquantique/haarpy/pull/53).
+* Added documentation [(#59)](https://github.com/polyquantique/haarpy/pull/59).
### Bug fixes
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 2e18ed8..eb768f2 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -25,7 +25,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v3
with:
- python-version: '3.9'
+ python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1d933dd..5f8f566 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -22,7 +22,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
- python-version: '3.9'
+ python-version: '3.10'
- name: Install dependencies
shell: bash
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..d51a0b8
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,21 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+ os: ubuntu-24.04
+ tools:
+ python: "3.12"
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: docs/conf.py
+
+# Set the requirements required to build your docs
+python:
+ install:
+ - requirements: docs/requirements.txt
\ No newline at end of file
diff --git a/README.md b/README.md
index 26617af..e270a59 100644
--- a/README.md
+++ b/README.md
@@ -156,7 +156,7 @@ pip install haarpy
## Compiling from source
Haarpy has the following dependencies:
-* [Python](https://www.python.org/) >= 3.9
+* [Python](https://www.python.org/) >= 3.10
* [SymPy](https://www.sympy.org) >= 1.14
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/code/circular_ensembles.rst b/docs/code/circular_ensembles.rst
new file mode 100644
index 0000000..a0426be
--- /dev/null
+++ b/docs/code/circular_ensembles.rst
@@ -0,0 +1,8 @@
+Circular ensembles
+==================
+
+This module provides tools for computations of integrals of polynomials on the circular ensembles.
+
+.. automodule:: haarpy.circular_ensembles
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/orthogonal.rst b/docs/code/orthogonal.rst
new file mode 100644
index 0000000..c2b9f45
--- /dev/null
+++ b/docs/code/orthogonal.rst
@@ -0,0 +1,8 @@
+Orthogonal group
+================
+
+This module provides tools for computations of integrals of polynomials on the orthogonal group.
+
+.. automodule:: haarpy.orthogonal
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/partition.rst b/docs/code/partition.rst
new file mode 100644
index 0000000..2d439b2
--- /dev/null
+++ b/docs/code/partition.rst
@@ -0,0 +1,8 @@
+Partition
+=========
+
+Description
+
+.. automodule:: haarpy.partition
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/permutation.rst b/docs/code/permutation.rst
new file mode 100644
index 0000000..d207761
--- /dev/null
+++ b/docs/code/permutation.rst
@@ -0,0 +1,8 @@
+Permutation matrices
+====================
+
+This module provides tools for computations of integrals of Haar random permutation matrices.
+
+.. automodule:: haarpy.permutation
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/quantum.rst b/docs/code/quantum.rst
new file mode 100644
index 0000000..482dde9
--- /dev/null
+++ b/docs/code/quantum.rst
@@ -0,0 +1,8 @@
+Quantum groups
+==============
+
+This module provides tools for computations of integrals of polynomials on the quantum groups.
+
+.. automodule:: haarpy.quantum
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/symmetric.rst b/docs/code/symmetric.rst
new file mode 100644
index 0000000..75f4b28
--- /dev/null
+++ b/docs/code/symmetric.rst
@@ -0,0 +1,8 @@
+Symmetric group
+===============
+
+This module provides tools to work with the symmetric group in the context of Weingarten calculus.
+
+.. automodule:: haarpy.symmetric
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/symplectic.rst b/docs/code/symplectic.rst
new file mode 100644
index 0000000..9530035
--- /dev/null
+++ b/docs/code/symplectic.rst
@@ -0,0 +1,8 @@
+Symplectic group
+================
+
+This module provides tools for computations of integrals of polynomials on the symplectic group.
+
+.. automodule:: haarpy.symplectic
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/code/unitary.rst b/docs/code/unitary.rst
new file mode 100644
index 0000000..bfd6ba6
--- /dev/null
+++ b/docs/code/unitary.rst
@@ -0,0 +1,8 @@
+Unitary group
+=============
+
+This module provides tools for computations of integrals of polynomials on the unitary group.
+
+.. automodule:: haarpy.unitary
+ :members:
+ :member-order: bysource
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..45baff2
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,45 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+import sys, os
+import haarpy
+
+sys.path.insert(0, os.path.abspath(".."))
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'Haarpy'
+copyright = '2026, Yanic Cardin, Hubert de Guise, Nicolás Quesada'
+author = 'Yanic Cardin, Hubert de Guise, Nicolás Quesada'
+release = haarpy.version()
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.mathjax",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.autosummary",
+ "sphinx_autodoc_typehints",
+ "myst_parser",
+ "nbsphinx",
+ "sphinx_copybutton",
+]
+
+bibtex_bibfiles = ["references.bib"]
+
+autosummary_generate = True
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'pydata_sphinx_theme'
+html_static_path = ['_static']
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..04ad083
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,40 @@
+.. Haarpy documentation master file, created by
+ sphinx-quickstart on Tue Jun 2 16:04:12 2026.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Haarpy Documentation
+====================
+
+A Python library for Weingarten calculus and integration of groups and ensembles equipped with the Haar measure.
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Getting started
+
+ tutorials/installation
+ tutorials/guide.ipynb
+
+..
+ toctree::
+ :maxdepth: 1
+ :caption: Background on Weingarten calculus
+
+ .. theory/weingarten
+ theory/compact
+ theory/circular
+ theorry/quantum
+ theory/permutation
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Haarpy API
+
+ code/symmetric
+ code/partition
+ code/permutation
+ code/unitary
+ code/orthogonal
+ code/symplectic
+ code/circular_ensembles
+ code/quantum
\ No newline at end of file
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..32bb245
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.https://www.sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/references.bib b/docs/references.bib
new file mode 100644
index 0000000..e69de29
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..b15d6d0
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,11 @@
+docutils
+sphinx
+sphinx_copybutton
+nbsphinx
+pydata-sphinx-theme
+myst-parser
+sphinx-autodoc-typehints
+sphinx-copybutton
+sphinx-autobuild
+sympy>=1.14
+ipykernel
\ No newline at end of file
diff --git a/docs/theory/unitary.rst b/docs/theory/unitary.rst
new file mode 100644
index 0000000..69fc394
--- /dev/null
+++ b/docs/theory/unitary.rst
@@ -0,0 +1,5 @@
+The Unitary group
+=================
+
+.. math::
+ \text{Wg}^U = \sum\delta,
diff --git a/docs/tutorials/guide.ipynb b/docs/tutorials/guide.ipynb
new file mode 100644
index 0000000..395bb89
--- /dev/null
+++ b/docs/tutorials/guide.ipynb
@@ -0,0 +1,180 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Quick Guide\n",
+ "To introduce the main functionality of Haarpy consider the following problem: imagine that you can generate unitary matrices at random (from the Haar measure); you would like to estimate the average of $|U_{i,j}|^2$ which we write mathematically as $\\int dU |U_{i,j}|^2 = \\int dU U_{i,j} U_{i,j}^*$. We could obtain this average by using the random matrix functionality of SciPy as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from scipy.stats import unitary_group"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([0.4964599 , 0.32742463, 0.25429793])"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "np.random.seed(137)\n",
+ "shots = 1000\n",
+ "\n",
+ "# For unitary matrices of size between 2 and 4 produce 1000 shots and calculate the average\n",
+ "# of the absolute value squared of the 0,1 entry\n",
+ "np.array(\n",
+ " [\n",
+ " np.mean([np.abs(unitary_group.rvs(dim=dim)[0, 1]) ** 2 for _ in range(shots)])\n",
+ " for dim in range(2, 5)\n",
+ " ]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Haarpy allows you to obtain this average (and many others!) analytically. We first recall that the expression we are trying to calculate is $\\int dU \\left|U_{i,j}\\right|^2 = \\int dU U_{i,j} U_{i,j}^*$. With this expression in mind we can use Sympy to create a symbolic variable $d$ for the dimension of the unitary and write"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sympy import Symbol\n",
+ "from haarpy import haar_integral_unitary"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "$\\displaystyle \\frac{1}{d}$"
+ ],
+ "text/plain": [
+ "1/d"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "d = Symbol(\"d\")\n",
+ "haar_integral_unitary((\"i\", \"j\", \"i\", \"j\"), d)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can also put integers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Fraction(1, 3)"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "haar_integral_unitary((\"i\", \"j\", \"i\", \"j\"), 3)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Notice the order of the indices! The first `\"i\"` and `\"j\"` are the indices of $U$ while the second pair of `\"i\"` and `\"j\"` are the indices of $U^*$.\n",
+ "\n",
+ "Imagine that now we want to calculate something like $\\int dU U_{i,m} U_{j,n} U_{k,o} U_{i,m}^* U_{j,n}^* U_{k,p}^*$ $= \\int dU \\left|U_{i,m} U_{j,n} U_{k,o}\\right|^2$ we simply do"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/latex": [
+ "$\\displaystyle \\frac{d^{2} - 2}{d \\left(d - 2\\right) \\left(d - 1\\right) \\left(d + 1\\right) \\left(d + 2\\right)}$"
+ ],
+ "text/plain": [
+ "(d**2 - 2)/(d*(d - 2)*(d - 1)*(d + 1)*(d + 2))"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "haar_integral_unitary((\"ijk\", \"mno\", \"ijk\", \"mno\"), d)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The averages we are calculating are obtained by using so-called [Weingarten calculus](https://doi.org/10.1155/S107379280320917X)."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "mtl",
+ "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.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/tutorials/installation.rst b/docs/tutorials/installation.rst
new file mode 100644
index 0000000..ebc07f1
--- /dev/null
+++ b/docs/tutorials/installation.rst
@@ -0,0 +1,26 @@
+
+Installation
+============
+
+Haarpy requires Python version 3.10 or later. Installation of Haarpy and its dependencies can be done using pip:
+
+.. code-block:: bash
+
+ $ pip install haarpy
+
+
+Compiling from source
+---------------------
+
+The Walrus has the following dependencies:
+
+* `Python `_ >= 3.10
+* `SymPy `_ >=1.14.0
+
+You can compile the latest development version by cloning the git repository, and installing using
+pip in development mode.
+
+.. code-block:: console
+
+ $ git clone https://github.com/polyquantique/haarpy.git
+ $ cd haarpy && python -m pip install -e .
\ No newline at end of file
diff --git a/haarpy/circular_ensembles.py b/haarpy/circular_ensembles.py
index be906bf..4ea1249 100644
--- a/haarpy/circular_ensembles.py
+++ b/haarpy/circular_ensembles.py
@@ -17,13 +17,12 @@
References
----------
[1] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
- symmetric spaces. arXiv preprint arXiv:1301.5401.
+ symmetric spaces. arXiv preprint arXiv:1301.5401.
"""
from math import prod
from fractions import Fraction
from functools import lru_cache
-from typing import Union
from collections import Counter
from sympy import Symbol, Expr
from sympy.combinatorics import Permutation, SymmetricGroup
@@ -39,38 +38,43 @@
@lru_cache
def weingarten_circular_orthogonal(
- permutation: Union[Permutation, tuple[int]],
+ permutation: Permutation | tuple[int, ...],
coe_dimension: Symbol,
) -> Expr:
"""Returns the circular orthogonal ensemble's Weingarten functions
Parameters
----------
- permutation (Permutation) : A permutation of S_2k or its coset-type
- coe_dimension (Symbol) : The dimension of the COE
+ permutation : Permutation) : | tuple[int, ...]
+ A permutation of the symmetric group :math:`S_{2p}` or its coset-type
+
+ coe_dimension : Symbol
+ The dimension of the circular orthogonal ensemble
Returns
-------
- Expr : The Weingarten function
+ Expr
+ The Weingarten function
Examples
--------
- >>> from sympy import Symbol
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import weingarten_circular_orthogonal
- >>> d = Symbol("d")
- >>> weingarten_circular_orthogonal(Permutation(3)(0,1), 4)
- Fraction(3, 70)
- >>> weingarten_circular_orthogonal(Permutation(3)(0,1), d)
- (d + 2)/(d*(d + 1)*(d + 3))
- >>> weingarten_circular_orthogonal((1,1), d)
- (d + 2)/(d*(d + 1)*(d + 3))
-
- Where (1,1) is the coset-type of Permutation(3)(0,1)
+ >>> from sympy import Symbol
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import weingarten_circular_orthogonal
+ >>> d = Symbol("d")
+ >>> weingarten_circular_orthogonal(Permutation(3)(0,1), 4)
+ Fraction(3, 70)
+ >>> weingarten_circular_orthogonal(Permutation(3)(0,1), d)
+ (d + 2)/(d*(d + 1)*(d + 3))
+ >>> weingarten_circular_orthogonal((1,1), d)
+ (d + 2)/(d*(d + 1)*(d + 3))
See Also
--------
- coset_type, weingarten_orthogonal
+ :func:`haarpy.symmetric.coset_type`
+ Returns the coset-type of a given permutation of the symmetric group
+ :func:`haarpy.orthogonal.weingarten_orthogonal`
+ Returns the orthogonal Weingarten function
"""
return weingarten_orthogonal(permutation, coe_dimension + 1)
@@ -81,27 +85,32 @@ def weingarten_circular_symplectic(permutation: Permutation, cse_dimension: Symb
Parameters
----------
- permutation (Permutation) : A permutation of the symmetric group S_2k
- cse_dimension (int) : The dimension of the CSE
+ permutation : Permutation
+ A permutation of the symmetric group :math:`S_{2p}
+
+ cse_dimension : int
+ The dimension of the circular symplectic ensemble
Returns
-------
- Expr : The Weingarten function
+ Expr
+ The Weingarten function
Examples
--------
- >>> from sympy import Symbol
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import weingarten_circular_symplectic
- >>> d = Symbol("d")
- >>> weingarten_circular_symplectic(Permutation(3)(0,1), 4)
- Fraction(-3, 140)
- >>> weingarten_circular_symplectic(Permutation(3)(0,1), d)
- (1 - d)/(d*(2*d - 3)*(2*d - 1))
+ >>> from sympy import Symbol
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import weingarten_circular_symplectic
+ >>> d = Symbol("d")
+ >>> weingarten_circular_symplectic(Permutation(3)(0,1), 4)
+ Fraction(-3, 140)
+ >>> weingarten_circular_symplectic(Permutation(3)(0,1), d)
+ (1 - d)/(d*(2*d - 3)*(2*d - 1))
See Also
--------
- weingarten_symplectic
+ :func:`haarpy.symplectic.weingarten_symplectic`
+ Returns the symplectic Weingarten function
"""
symplectic_dimension = (
(2 * cse_dimension - 1) / 2
@@ -113,39 +122,51 @@ def weingarten_circular_symplectic(permutation: Permutation, cse_dimension: Symb
@lru_cache
def haar_integral_circular_orthogonal(
- sequences: tuple[tuple[int]], group_dimension: Symbol
+ sequences: tuple[tuple[int, ...], ...], group_dimension: Symbol
) -> Expr:
- """Returns integral over circular orthogonal ensemble polynomial
+ """Returns the integral over the circular orthogonal ensemble of a monomial
sampled at random from the Haar measure
Parameters
----------
- sequences (tuple[tuple[int]]) : Indices of matrix elements
- group_dimension (Symbol) : Dimension of the orthogonal group
+ sequences : tuple[tuple[int, ...], ...]
+ Sequences of matrix elements
+
+ group_dimension : Symbol
+ The dimension of the circular orthogonal ensemble
Returns
-------
- Expr : Integral under the Haar measure
+ Expr
+ The integral under the Haar measure
- Raise
- -----
- ValueError : if sequences do not contain 2 tuples
- ValueError : if tuples i and j are of odd size
+ Raises
+ ------
+ ValueError
+ If ``sequences`` do not contain precisely two sequences
+ ValueError
+ If the sequences are of odd length
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_circular_orthogonal
- >>> d = Symbol("d")
- >>> seq_i, seq_j = (0, 0, 1, 2), (1, 0, 0, 2)
- >>> haar_integral_circular_orthogonal((seq_i, seq_j), 7)
- Fraction(-1, 280)
- >>> haar_integral_circular_orthogonal((seq_i, seq_j), d)
- -2/(d*(d + 1)*(d + 3))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_circular_orthogonal
+ >>> d = Symbol("d")
+ >>> seq_i, seq_j = (0, 0, 1, 2), (1, 0, 0, 2)
+ >>> haar_integral_circular_orthogonal((seq_i, seq_j), 7)
+ Fraction(-1, 280)
+ >>> haar_integral_circular_orthogonal((seq_i, seq_j), d)
+ -2/(d*(d + 1)*(d + 3))
See Also
--------
- coset_type, stabilizer_coset, weingarten_circular_orthogonal
+ :func:`haarpy.symmetric.coset_type`
+ Returns the coset-type of a given permutation of the symmetric group
+ :func:`haarpy.symmetric.stabilizer_coset`
+ Returns all permutations that sends the first sequences to the second. For a single input,
+ the function returns the stabilizer group with respect to the sequence
+ :func:`haarpy.circular_ensembles.weingarten_circular_orthogonal`
+ Returns the circular orthogonal ensemble's Weingarten functions
"""
if len(sequences) != 2:
raise ValueError("Wrong tuple format")
@@ -168,44 +189,58 @@ def haar_integral_circular_orthogonal(
@lru_cache
-def haar_integral_circular_symplectic(sequences: tuple[tuple[Expr]], half_dimension: Expr) -> Expr:
- """Returns integral over circular symplectic ensemble polynomial
+def haar_integral_circular_symplectic(
+ sequences: tuple[tuple[Expr, ...], ...], half_dimension: Expr
+) -> Expr:
+ """Returns the integral over the circular symplectic ensemble of a monomial
sampled at random from the Haar measure
Parameters
----------
- sequences (tuple[tuple[int]]) : Indices of matrix elements
- half_dimension (Symbol) : Half the dimension of the unitary group
+ sequences : tuple[tuple[Expr, ...], ...]
+ Indices of matrix elements
+
+ half_dimension : Expr
+ Half the dimension of the unitary group
Returns
-------
- Expr : Integral under the Haar measure
-
- Raise
- -----
- ValueError : if sequences do not contain 2 tuples
- ValueError : if tuples i and j are of odd size
- TypeError : if dimension is int and sequence is not
- TypeError : if the half_dimension is neither int nor Symbol
- ValueError : if all sequence indices are not between 0 and 2*dimension - 1
- TypeError : if sequence contains something other than Expr
- TypeError : if symbolic sequences have the wrong format
+ Expr
+ The integral under the Haar measure
+
+ Raises
+ ------
+ ValueError
+ If parameter ``sequences`` do not contain precisely two sequences
+ ValueError
+ If either sequence is of odd length
+ TypeError
+ If parameter ``half_dimension`` is of type ``int`` while the sequences contain symbols
+ TypeError
+ If the parameter ``half_dimension`` is neither ``int`` nor ``Symbol``
+ ValueError
+ If not all sequence indices are between ``0`` and ``2*half_dimension - 1``
+ TypeError
+ If ``sequences`` contains something other than ``Expr`` or ``int``
+ TypeError
+ If the symbolic sequences have the wrong format
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_circular_symplectic
- >>> d = Symbol("d")
- >>> seq_i_num, seq_j_num = (0, 3, 2, 1), (0, 1, 2, 3)
- >>> haar_integral_circular_symplectic((seq_i_num, seq_j_num), 2)
- Fraction(1, 6)
- >>> seq_i_symb, seq_j_symb = (0, d+1, d, 1), (0, 1, d, d + 1)
- >>> haar_integral_circular_symplectic((seq_i_symb, seq_j_symb), d)
- -1/(2*d*(2*d - 3)*(2*d - 1))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_circular_symplectic
+ >>> d = Symbol("d")
+ >>> seq_i_num, seq_j_num = (0, 3, 2, 1), (0, 1, 2, 3)
+ >>> haar_integral_circular_symplectic((seq_i_num, seq_j_num), 2)
+ Fraction(1, 6)
+ >>> seq_i_symb, seq_j_symb = (0, d+1, d, 1), (0, 1, d, d + 1)
+ >>> haar_integral_circular_symplectic((seq_i_symb, seq_j_symb), d)
+ -1/(2*d*(2*d - 3)*(2*d - 1))
See Also
--------
- weingarten_circular_symplectic
+ :func:`haarpy.circular_ensembles.weingarten_circular_symplectic`
+ Returns the circular symplectic ensemble's Weingarten functions
"""
if len(sequences) != 2:
raise ValueError("Wrong sequence format")
diff --git a/haarpy/orthogonal.py b/haarpy/orthogonal.py
index db57fe3..d1b6cbf 100644
--- a/haarpy/orthogonal.py
+++ b/haarpy/orthogonal.py
@@ -18,8 +18,10 @@
----------
[1] Collins, B., & Śniady, P. (2006). Integration with respect to the Haar measure on unitary,
orthogonal and symplectic group. Communications in Mathematical Physics, 264(3), 773-795.
+
[2] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
symmetric spaces. arXiv preprint arXiv:1301.5401.
+
[3] Macdonald, I. G. (1998). Symmetric functions and Hall polynomials. Oxford university press.
"""
@@ -27,7 +29,6 @@
from fractions import Fraction
from itertools import product
from functools import lru_cache
-from typing import Union
from collections import Counter
from sympy import Symbol, Expr, factorial
from sympy.combinatorics import Permutation
@@ -45,33 +46,43 @@
@lru_cache
-def zonal_spherical_function(permutation: Permutation, partition: tuple[int]) -> Fraction:
- """Returns the zonal spherical function of the Gelfand pair (S_2k, H_k)
+def zonal_spherical_function(permutation: Permutation, partition: tuple[int, ...]) -> Fraction:
+ """Returns the zonal spherical function of the Gelfand pair :math:`(S_{2p}, H_p)`
Parameters
----------
- permutation (Permutation) : a permutation of the symmetric group S_2k
- partition (tuple[int]) : a partition of k
+ permutation : Permutation
+ A permutation of the symmetric group :math:`S_{2p}`
+
+ partition : tuple[int, ...]
+ An integer partition of :math:`p`
Returns
-------
- Fraction : the zonal spherical function of the given permutation
+ Fraction
+ The zonal spherical function of the given permutation
- Raise
- -----
- TypeError : if partition argument is not a tuple
- TypeError : if permutation argument is not a permutation
+ Raises
+ ------
+ TypeError
+ If ``partition`` is not a tuple
+ TypeError
+ If ``permutation`` is not a permutation
Examples
--------
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import zonal_spherical_function
- >>> zonal_spherical_function(Permutation(5)(0,1,2), (2,1))
- Fraction(1, 6)
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import zonal_spherical_function
+ >>> zonal_spherical_function(Permutation(5)(0,1,2), (2,1))
+ Fraction(1, 6)
See Also
--------
- HyperoctahedralGroup, murn_naka_rule
+ :func:`haarpy.symmetric.HyperoctahedralGroup`
+ Returns the hyperoctahedral group :math:`H_p`
+ :func:`haarpy.symmetric.murn_naka_rule`
+ Implementation of the Murnaghan-Nakayama rule for the characters irreducible
+ representations of the symmetric group
"""
if not isinstance(partition, tuple):
raise TypeError
@@ -97,43 +108,59 @@ def zonal_spherical_function(permutation: Permutation, partition: tuple[int]) ->
@lru_cache
def weingarten_orthogonal(
- permutation: Union[Permutation, tuple[int]], orthogonal_dimension: Symbol
+ permutation: Permutation | tuple[int, ...], orthogonal_dimension: Symbol
) -> Expr:
"""Returns the orthogonal Weingarten function
Parameters
----------
- permutation (Permutation, tuple[int]) : a permutation of S_2k or its coset-type
- orthogonal_dimension (int): dimension of the orthogonal group
+ permutation : Permutation | tuple[int, ...]
+ A permutation of the symmetric group or its coset-type
+
+ orthogonal_dimension : Symbol
+ The dimension of the orthogonal group
Returns
-------
- Symbol : the Weingarten function
-
- Raise
+ Expr
+ The Weingarten function
+
+ Raises
+ ------
+ TypeError
+ If unitary_dimension has the wrong type
+ TypeError
+ If ``permutation`` has the wrong type
+ ValueError
+ If the degree :math:`2p` of the symmetric group :math:`S_{2p}` is not even
+
+ Notes
-----
- TypeError : if unitary_dimension has the wrong type
- TypeError : if permutation has the wrong type
- ValueError : if the degree 2k of the symmetric group S_2k is not a factor of 2
+ Since the orthogonal Weingarten function is invariant over coset-types, the argument
+ may be given either as a permutation or as its coset-type
Examples
--------
- >>> from sympy import Symbol
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import weingarten_orthogonal
- >>> d = Symbol("d")
- >>> weingarten_orthogonal(Permutation(5)(0,1,2), 6)
- Fraction(-1, 1200)
- >>> weingarten_orthogonal(Permutation(5)(0,1,2), d)
- -1/(d*(d - 2)*(d - 1)*(d + 4))
- >>> weingarten_orthogonal((2,1), d)
- -1/(d*(d - 2)*(d - 1)*(d + 4))
-
- Where (2,1) is the coset-type of Permutation(5)(0,1,2)
+ >>> from sympy import Symbol
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import weingarten_orthogonal
+ >>> d = Symbol("d")
+ >>> weingarten_orthogonal(Permutation(5)(0,1,2), 6)
+ Fraction(-1, 1200)
+ >>> weingarten_orthogonal(Permutation(5)(0,1,2), d)
+ -1/(d*(d - 2)*(d - 1)*(d + 4))
+ >>> weingarten_orthogonal((2,1), d)
+ -1/(d*(d - 2)*(d - 1)*(d + 4))
See Also
--------
- zonal_spherical_function, coset_type_representative
+ :func:`haarpy.orthogonal.zonal_spherical_function`
+ Returns the zonal spherical function of the Gelfand pair :math:`(S_{2p}, H_p)`
+ :func:`haarpy.symmetric.coset_type`
+ Returns the coset-type of a given permutation of the symmetric group
+ :func:`haarpy.symmetric.coset_type_representative`
+ Returns a representative permutation of the symmetric group :math:`S_{2p}`
+ for a given input coset-type
"""
if not isinstance(orthogonal_dimension, (Expr, int)):
raise TypeError("orthogonal_dimension must be an instance of int or sympy.Symbol")
@@ -200,37 +227,50 @@ def weingarten_orthogonal(
@lru_cache
-def haar_integral_orthogonal(sequences: tuple[tuple[int]], orthogonal_dimension: Symbol) -> Expr:
+def haar_integral_orthogonal(
+ sequences: tuple[tuple[int, ...], ...], orthogonal_dimension: Symbol
+) -> Expr:
"""Returns the integral over orthogonal group polynomial sampled at random from the Haar measure
Parameters
----------
- sequences (tuple[tuple[int]]) : indices of matrix elements
- orthogonal_dimension (int) : dimension of the orthogonal group
+ sequences : tuple[tuple[int, ...], ...]
+ Sequences of matrix elements
+ orthogonal_dimension : Symbol
+ The dimension of the orthogonal group
Returns
-------
- Expr : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
- Raise
- -----
- ValueError : if sequences do not contain 2 tuples
- ValueError : if tuples i and j are of different length
+ Raises
+ ------
+ ValueError
+ If the argument ``sequences`` does not contain 2 tuples
+ ValueError
+ If the sequences are of different lengths
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_orthogonal
- >>> d = Symbol("d")
- >>> row_indices, column_indices = (0, 0, 1, 1, 2, 2), (0, 2, 2, 1, 1, 0)
- >>> haar_integral_orthogonal((row_indices, column_indices), 4)
- Fraction(1, 576)
- >>> haar_integral_orthogonal((row_indices, column_indices), d)
- 2/(d*(d - 2)*(d - 1)*(d + 2)*(d + 4))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_orthogonal
+ >>> d = Symbol("d")
+ >>> row_indices, column_indices = (0, 0, 1, 1, 2, 2), (0, 2, 2, 1, 1, 0)
+ >>> haar_integral_orthogonal((row_indices, column_indices), 4)
+ Fraction(1, 576)
+ >>> haar_integral_orthogonal((row_indices, column_indices), d)
+ 2/(d*(d - 2)*(d - 1)*(d + 2)*(d + 4))
See Also
--------
- hyperoctahedral_transversal, coset_type, weingarten_orthogonal
+ :func:`haarpy.symmetric.coset_type`
+ Returns the coset-type of a given permutation of the symmetric group
+ :func:`haarpy.symmetric.hyperoctahedral_transversal`
+ Yields the permutations of :math:`M_{2p}`, the complete set of coset
+ representatives of the quotient group :math:`S_{2p}/H_p`
+ :func:`haarpy.orthogonal.weingarten_orthogonal`
+ Computes the orthogonal Weingarten function
"""
if len(sequences) != 2:
raise ValueError("Wrong tuple format")
diff --git a/haarpy/partition.py b/haarpy/partition.py
index 80c9804..769b846 100644
--- a/haarpy/partition.py
+++ b/haarpy/partition.py
@@ -18,8 +18,10 @@
----------
[1] Collins, B., & Nagatsu, M. (2025). Weingarten calculus for centered random permutation
matrices. arXiv preprint arXiv:2503.18453.
+
[2] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
symmetric spaces. arXiv preprint arXiv:1301.5401.
+
[3] Nica, A., & Speicher, R. (2006). Lectures on the combinatorics of free probability
(Vol. 13). Cambridge University Press.
"""
@@ -32,30 +34,33 @@
def set_partitions(collection: tuple) -> Iterator[tuple[tuple, ...]]:
"""Returns the partitionning of a given collection (set) of objects
- into non-empty subsets.
+ into non-empty subsets
Parameters
----------
- collection (tuple) : an indexable iterable to be partitionned
+ collection :tuple
+ An indexable iterable to be partitionned
Returns
-------
- generator(tuple[tuple]) : all partitions of the input collection
+ Iterator : tuple[tuple]
+ All partitions of the input collection
- Raise
- -----
- ValueError : if the collection is not a tuple
+ Raises
+ ------
+ ValueError
+ If the collection is not a tuple
Examples
--------
- >>> from haarpy import set_partitions
- >>> for partition in set_partitions((0,1,2)):
- >>> print(partition)
- ((0, 1, 2),)
- ((0,), (1, 2))
- ((0, 1), (2,))
- ((0, 2), (1,))
- ((0,), (1,), (2,))
+ >>> from haarpy import set_partitions
+ >>> for partition in set_partitions((0,1,2)):
+ >>> print(partition)
+ ((0, 1, 2),)
+ ((0,), (1, 2))
+ ((0, 1), (2,))
+ ((0, 2), (1,))
+ ((0,), (1,), (2,))
"""
if not isinstance(collection, tuple):
raise TypeError("collection must be a tuple")
@@ -74,29 +79,32 @@ def set_partitions(collection: tuple) -> Iterator[tuple[tuple, ...]]:
def pair_partitions(
seed: tuple[int, ...],
) -> Iterator[tuple[tuple[int, ...], ...]]:
- """Returns the pair partitions of a given set.
+ """Returns the pair partitions of a given set
Parameters
----------
- seed (tuple[int]) : a tuple representing the (multi-)set that will be partitioned.
- Note that it must hold that ``len(s) >= 2``
+ seed : tuple[int]
+ A tuple representing the (multi-)set that will be partitioned.
+ Note that it must hold that ``len(s) >= 2``
Returns
-------
- generator : all the single-double partitions of the tuple
+ Iterator[tuple[tuple[int]]]
+ All the single-double partitions of the tuple
- Raise
- -----
- TypeError : if the seed is not a tuple
+ Raises
+ ------
+ TypeError
+ If the seed is not a tuple
Examples
--------
- >>> from haarpy import pair_partitions
- >>> for matching in pair_partitions((0,1,2,3)):
- >>> print(matching)
- ((0, 1), (2, 3))
- ((0, 2), (1, 3))
- ((0, 3), (1, 2))
+ >>> from haarpy import pair_partitions
+ >>> for matching in pair_partitions((0,1,2,3)):
+ >>> print(matching)
+ ((0, 1), (2, 3))
+ ((0, 2), (1, 3))
+ ((0, 3), (1, 2))
"""
if not isinstance(seed, tuple):
raise TypeError("seed must be a tuple")
@@ -116,31 +124,37 @@ def pair_partitions(
def partial_order(
partition_1: tuple[tuple[int, ...], ...], partition_2: tuple[tuple[int, ...], ...]
) -> bool:
- """Checks if parition_1 <= partition_2 in terms of partial order
-
- For parition_1 and partition_2, two partitions of the same set, we call
- partition_1 <= partition_2 if and only if each block of partition_1 is
- contained in some block of partition_2
+ """Checks if ``parition_1 <= partition_2`` in terms of partial order
Parameters
----------
- partition_1 (tuple[tuple[int]]) : the partition of lower order
- partition_2 (tuple[tuple[int]]) : the partition of higher order
+ partition_1 : tuple[tuple[int]]
+ The partition of lower order
+
+ partition_2 : tuple[tuple[int]]
+ The partition of higher order
Returns
-------
- bool : True if partition_1 <= partition_2
+ bool
+ ``True`` if ``partition_1 <= partition_2``
+
+ Notes
+ -----
+ For ``parition_1`` and ``partition_2``, two partitions of the same set, we call
+ ``partition_1 <= partition_2`` if and only if each block of ``partition_1`` is
+ contained in some block of ``partition_2``
Examples
--------
- >>> from haarpy import partial_order
- >>> partition_1 = ((0, 1), (2, 3), (4,))
- >>> partition_2 = ((0, 1), (2, 3, 4))
- >>> partition_3 = ((0, 4), (1, 2, 3))
- >>> partial_order(partition_1, partition_2)
- True
- >>> partial_order(partition_1, partition_3)
- False
+ >>> from haarpy import partial_order
+ >>> partition_1 = ((0, 1), (2, 3), (4,))
+ >>> partition_2 = ((0, 1), (2, 3, 4))
+ >>> partition_3 = ((0, 4), (1, 2, 3))
+ >>> partial_order(partition_1, partition_2)
+ True
+ >>> partial_order(partition_1, partition_3)
+ False
"""
for part in partition_1:
if all(not set(part).issubset(bigger_part) for bigger_part in partition_2):
@@ -155,28 +169,34 @@ def meet_operation(
) -> tuple[tuple[int, ...], ...]:
"""Returns the greatest lower bound of the two input partitions
- For parition_1 and partition_2, two partitions of the same set,
- the meet operation yields the greatest lower bound of both partitions
-
- The meet operation symbol is ∧, for instance
- ((0, 1), (2, 3), (4,)) ∧ ((0, 1, 2), (3, 4)) = ((0, 1), (2,), (3,), (4,))
-
Parameters
----------
- partition_1 (tuple[tuple[int]]) : partition of a set
- partition_2 (tuple[tuple[int]]) : partition of a set
+ partition_1 : tuple[tuple[int]]
+ The partition of a set
+
+ partition_2 : tuple[tuple[int]]
+ A second partition of the same set
Returns
-------
- tuple[tuple] : greatest lower bound
+ tuple[tuple]
+ The greatest lower bound
+
+ Notes
+ -----
+ For ``parition_1`` and ``partition_2``, two partitions of the same set,
+ the meet operation yields the greatest lower bound of both partitions
+
+ The meet operation symbol is ``∧``, for instance
+ ``((0, 1), (2, 3), (4,)) ∧ ((0, 1, 2), (3, 4)) = ((0, 1), (2,), (3,), (4,))``
Examples
--------
- >>> from haarpy import meet_operation
- >>> partition_1 = ((0, 1), (2, 3), (4,))
- >>> partition_2 = ((0, 1, 2), (3, 4))
- >>> meet_operation(partition_1, partition_2)
- ((0, 1), (2,), (3,), (4,))
+ >>> from haarpy import meet_operation
+ >>> partition_1 = ((0, 1), (2, 3), (4,))
+ >>> partition_2 = ((0, 1, 2), (3, 4))
+ >>> meet_operation(partition_1, partition_2)
+ ((0, 1), (2,), (3,), (4,))
"""
partition_1 = tuple(set(part) for part in partition_1)
partition_2 = tuple(set(part) for part in partition_2)
@@ -196,28 +216,34 @@ def join_operation(
) -> tuple[tuple[int, ...], ...]:
"""Returns the least upper bound of the two input partitions
- For parition_1 and partition_2, two partitions of the same set,
- the join operation yields the least upper bound of both partitions
-
- The join operation symbol is ∨, for instance
- ((0, 1), (2,), (3, 4)) ∨ ((0, 2), (1,), (3,), (4,)) = ((0, 1, 2), (3, 4))
-
Parameters
----------
- partition_1 (tuple[tuple[int]]) : partition of a set
- partition_2 (tuple[tuple[int]]) : partition of a set
+ partition_1 : tuple[tuple[int]]
+ The partition of a set
+
+ partition_2 : tuple[tuple[int]]
+ A second partition of the same set
Returns
------
- tuple[tuple[int]] : least upper bound
+ tuple[tuple[int]]
+ The least upper bound
+
+ Notes
+ -----
+ For ``parition_1`` and ``partition_2``, two partitions of the same set,
+ the join operation yields the least upper bound of both partitions
+
+ The join operation symbol is ``∨``, for instance
+ ``((0, 1), (2,), (3, 4)) ∨ ((0, 2), (1,), (3,), (4,)) = ((0, 1, 2), (3, 4))``
Examples
--------
- >>> from haarpy import join_operation
- >>> partition_1 = ((0, 1), (2,), (3, 4))
- >>> partition_2 = ((0, 2), (1,), (3,), (4,))
- >>> join_operation(partition_1, partition_2)
- ((0, 1, 2), (3, 4))
+ >>> from haarpy import join_operation
+ >>> partition_1 = ((0, 1), (2,), (3, 4))
+ >>> partition_2 = ((0, 2), (1,), (3,), (4,))
+ >>> join_operation(partition_1, partition_2)
+ ((0, 1, 2), (3, 4))
"""
parent = [
{index for value in block1 for index, block2 in enumerate(partition_2) if value in block2}
@@ -241,39 +267,45 @@ def join_operation(
def non_crossing_partitions(n: int, pair: bool = False) -> Iterator[tuple[tuple[int, ...], ...]]:
- """Yields non crossing partitions of [n] = {1,2,...,n}
+ """Yields non crossing partitions of the set :math:`[n] = \{1,2,...,n\}`
Parameters
----------
- n (int) : the size of the partitioned set [n]
- pair (bool) : True if limited to pair partitions
+ n : int
+ The size of the partitioned set :math:`[n]`
+
+ pair : bool
+ ``True`` to yield only pair partitions
Returns
-------
- Iterator : yields the non-crossing partitions
+ Iterator[tuple[tuple[int, ...], ...]]
+ Yields the non-crossing partitions
- Raise
- -----
- TypeError : if n is not int
- ValueError : if n < 0 or if pair is True and n is odd
+ Raises
+ ------
+ TypeError
+ If parameter ``n`` is not of type ``int``
+ ValueError
+ If ``n < 0`` or if pair is ``True`` and ``n`` is odd
Examples
--------
- >>> from haarpy import non_crossing_partitions
- >>> for partition in non_crossing_partitions(3):
- >>> print(partition)
- ((0,), (1,), (2,))
- ((0,), (1, 2))
- ((0, 2), (1,))
- ((0, 1), (2,))
- ((0, 1, 2),)
- >>> for partition in non_crossing_partitions(6, pair = True):
- >>> print(partition)
- ((0, 5), (1, 4), (2, 3))
- ((0, 5), (1, 2), (3, 4))
- ((0, 3), (1, 2), (4, 5))
- ((0, 1), (2, 5), (3, 4))
- ((0, 1), (2, 3), (4, 5))
+ >>> from haarpy import non_crossing_partitions
+ >>> for partition in non_crossing_partitions(3):
+ >>> print(partition)
+ ((0,), (1,), (2,))
+ ((0,), (1, 2))
+ ((0, 2), (1,))
+ ((0, 1), (2,))
+ ((0, 1, 2),)
+ >>> for partition in non_crossing_partitions(6, pair = True):
+ >>> print(partition)
+ ((0, 5), (1, 4), (2, 3))
+ ((0, 5), (1, 2), (3, 4))
+ ((0, 3), (1, 2), (4, 5))
+ ((0, 1), (2, 5), (3, 4))
+ ((0, 1), (2, 3), (4, 5))
"""
if not isinstance(n, int):
raise TypeError
@@ -328,19 +360,21 @@ def is_crossing_partition(partition: tuple[tuple[int, ...], ...]) -> bool:
Parameters
----------
- partition (tuple[tuple[int]])) : partition of a set
+ partition : tuple[tuple[int, ...], ...]
+ The partition of a set
Returns
-------
- bool : True if the partition is crossing, False otherwise
+ bool
+ ``True`` if the partition is crossing, ``False`` otherwise
Examples
--------
- >>> from haarpy import is_crossing_partition
- >>> is_crossing_partition(((0,2,4), (1,3)))
- True
- >>> is_crossing_partition(((0,3,4), (1,2)))
- False
+ >>> from haarpy import is_crossing_partition
+ >>> is_crossing_partition(((0,2,4), (1,3)))
+ True
+ >>> is_crossing_partition(((0,3,4), (1,2)))
+ False
"""
filtered_partition = tuple(
block for block in partition if len(block) != 1 and block[0] + 1 != block[-1]
@@ -366,27 +400,32 @@ def gram_matrix(
Parameters
----------
- partition_tuple (tuple[tuple[tuple[int]])) : set of partitions
- group_dimension (Symbol) : the dimension of the underlying group
+ partition_tuple : tuple[tuple[tuple[int, ...], ...], ...]
+ A set of partitions
+
+ group_dimension : Symbol
+ The dimension of the underlying group
Returns
-------
- Matrix : the symbolic Gram matrix
+ Matrix
+ The symbolic Gram matrix
- Raise
- -----
- TypeError : group_dimension is neither a symbol or an integer
+ Raises
+ ------
+ TypeError
+ If the parameter ``group_dimension`` is neither a symbol or an integer
Examples
--------
- >>> from haarpy import gram_matrix
- >>> from sympy import Symbol
- >>> n = Symbol('n')
- >>> pair_partition_tuple = (((0, 1), (2, 3)), ((0, 3), (1, 2)))
- >>> gram_matrix(pair_partition_tuple, n)
- Matrix([
- [n**2, n],
- [ n, n**2]])
+ >>> from haarpy import gram_matrix
+ >>> from sympy import Symbol
+ >>> n = Symbol('n')
+ >>> pair_partition_tuple = (((0, 1), (2, 3)), ((0, 3), (1, 2)))
+ >>> gram_matrix(pair_partition_tuple, n)
+ Matrix([
+ [n**2, n],
+ [ n, n**2]])
"""
if not isinstance(group_dimension, (Expr, int)):
raise TypeError
diff --git a/haarpy/permutation.py b/haarpy/permutation.py
index 0f9bd21..be6c0de 100644
--- a/haarpy/permutation.py
+++ b/haarpy/permutation.py
@@ -25,31 +25,37 @@
from itertools import product
from collections.abc import Sequence
from fractions import Fraction
-from sympy import Symbol, binomial
+from sympy import Expr, Symbol, binomial
from haarpy import set_partitions, meet_operation, join_operation, partial_order
from ._utils import _simplify
@lru_cache
-def mobius_function(partition_1: tuple[tuple[int]], partition_2: tuple[tuple[int]]) -> int:
+def mobius_function(
+ partition_1: tuple[tuple[int, ...], ...], partition_2: tuple[tuple[int, ...], ...]
+) -> int:
"""Returns the Möbius function of two given partitions
Parameters
----------
- partition_1 (tuple[tuple[int]) : the intersected partition
- partition_2 (tuple[tuple[int]]) : the partition summed over
+ partition_1 : tuple[tuple[int]
+ The intersected partition
+
+ partition_2 : tuple[tuple[int]]
+ The partition summed over
Returns
-------
- int : the value of the Möbius function
+ int
+ The value of the Möbius function
Examples
--------
- >>> from haarpy import mobius_function
- >>> partition_1 = ((0, 3), (1, 2), (4,), (5,))
- >>> partition_2 = ((0, 1, 2), (3, 4, 5))
- >>> mobius_function(partition_1, partition_2)
- -2
+ >>> from haarpy import mobius_function
+ >>> partition_1 = ((0, 3), (1, 2), (4,), (5,))
+ >>> partition_2 = ((0, 1, 2), (3, 4, 5))
+ >>> mobius_function(partition_1, partition_2)
+ -2
"""
partition_set_1 = tuple(set(block) for block in partition_1)
partition_set_2 = tuple(set(block) for block in partition_2)
@@ -66,37 +72,46 @@ def mobius_function(partition_1: tuple[tuple[int]], partition_2: tuple[tuple[int
@lru_cache
def weingarten_permutation(
- first_partition: tuple[tuple[int]],
- second_partition: tuple[tuple[int]],
+ first_partition: tuple[tuple[int, ...], ...],
+ second_partition: tuple[tuple[int, ...], ...],
dimension: Symbol,
-) -> Symbol:
+) -> Expr:
"""Returns the Weingarten function for random permutation matrices
Parameters
----------
- first_partition (tuple(tuple(int))) : a set partition of integer k
- second_partition (tuple(tuple(int))) : a set partition of integer k
- dimension (Symbol) : the dimension of the random permutation matrices
+ first_partition : tuple[tuple[int, ...], ...]
+ A partition of the set of :math:`k` integers :math:`[k] = \{1,2,\dots, k\}`
+
+ second_partition : tuple[tuple[int, ...], ...]
+ A second partition of the same set
+
+ dimension : Symbol
+ The dimension of the random permutation matrices
Returns
-------
- Symbol : the Weingarten function
+ Expr
+ The Weingarten function
Example
-------
- >>> from sympy import Symbol
- >>> from haarpy import weingarten_permutation
- >>> d = Symbol('d')
- >>> partition_1 = ((0, 1, 2), (3,))
- >>> partition_2 = ((0,), (1, 2, 3))
- >>> weingarten_permutation(partition_1, partition_2, 4)
- Fraction(5, 24)
- >>> weingarten_permutation(partition_1, partition_2, d)
- (d + 1)/(d*(d - 3)*(d - 2)*(d - 1))
+ >>> from sympy import Symbol
+ >>> from haarpy import weingarten_permutation
+ >>> d = Symbol('d')
+ >>> partition_1 = ((0, 1, 2), (3,))
+ >>> partition_2 = ((0,), (1, 2, 3))
+ >>> weingarten_permutation(partition_1, partition_2, 4)
+ Fraction(5, 24)
+ >>> weingarten_permutation(partition_1, partition_2, d)
+ (d + 1)/(d*(d - 3)*(d - 2)*(d - 1))
See Also
--------
- meet_operation, mobius_function
+ :func:`haarpy.partition.meet_operation`
+ Returns the greatest lower bound of the two input partitions
+ :func:`haarpy.permutation.mobius_function`
+ Returns the Möbius function of two given partitions
"""
disjoint_partition_tuple = tuple(
(partition for partition in set_partitions(block))
@@ -131,37 +146,48 @@ def weingarten_permutation(
@lru_cache
def weingarten_centered_permutation(
- first_partition: tuple[tuple[int]],
- second_partition: tuple[tuple[int]],
+ first_partition: tuple[tuple[int, ...], ...],
+ second_partition: tuple[tuple[int, ...], ...],
dimension: Symbol,
-) -> Symbol:
+) -> Expr:
"""Returns the Weingarten function for centered random permutation matrices
Parameters
----------
- first_partition (tuple(tuple(int))) : a set partition of integer k
- second_partition (tuple(tuple(int))) : a set partition of integer k
- dimension (Symbol) : the dimension of the centered random permutation matrices
+ first_partition : tuple[tuple[int, ...], ...]
+ A partition of the set of :math:`k` integers :math:`[k] = \{1,2,\dots, k\}`
+
+ second_partition : tuple[tuple[int, ...], ...]
+ A second partition of the same set
+
+ dimension : Symbol
+ The dimension of the random permutation matrices
Returns
-------
- Symbol : the Weingarten function
+ Expr
+ The Weingarten function
Example
-------
- >>> from sympy import Symbol
- >>> from haarpy import weingarten_centered_permutation
- >>> d = Symbol('d')
- >>> partition_1 = ((0,), (1,), (2,))
- >>> partition_2 = ((0, 2), (1,))
- >>> weingarten_centered_permutation(partition_1, partition_2, 3)
- Fraction(-1, 9)
- >>> weingarten_centered_permutation(partition_1, partition_2, d)
- -2/(d**2*(d - 2)*(d - 1))
+ >>> from sympy import Symbol
+ >>> from haarpy import weingarten_centered_permutation
+ >>> d = Symbol('d')
+ >>> partition_1 = ((0,), (1,), (2,))
+ >>> partition_2 = ((0, 2), (1,))
+ >>> weingarten_centered_permutation(partition_1, partition_2, 3)
+ Fraction(-1, 9)
+ >>> weingarten_centered_permutation(partition_1, partition_2, d)
+ -2/(d**2*(d - 2)*(d - 1))
See Also
--------
- join_operation, meet_operation, mobius_function
+ :func:`haarpy.partition.meet_operation`
+ Returns the greatest lower bound of the two input partitions
+ :func:`haarpy.partition.join_operation`
+ Returns the least upper bound of the two input partitions
+ :func:`haarpy.permutation.mobius_function`
+ Returns the Möbius function of two given partitions
"""
singleton_set_size = sum(
1 for block in join_operation(first_partition, second_partition) if len(block) == 1
@@ -212,37 +238,48 @@ def weingarten_centered_permutation(
@lru_cache
def haar_integral_permutation(
- row_indices: tuple[int],
- column_indices: tuple[int],
+ row_indices: tuple[int, ...],
+ column_indices: tuple[int, ...],
dimension: Symbol,
-) -> Symbol:
+) -> Expr:
"""Returns the integral over Haar random permutation matrices
Parameters
----------
- row_indices (tuple(int)) : sequence of row indices
- column_indices (tuple(int)) : sequence of column indices
+ row_indices : tuple[int, ...]
+ A sequence of row indices
+
+ column_indices : tuple[int, ...]
+ A sequence of column indices
Returns
-------
- Symbol : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
- Raise
- -----
- TypeError : if row_indices and column_indices are not Sequence
- ValueError : if row_indices and column_indices are of different length
+ Raises
+ ------
+ TypeError
+ If the input parameters are not of type ``Sequence``
+ ValueError
+ If the input sequences are of different length
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_permutation
- >>> d = Symbol('d')
- >>> row_indices = (0, 1, 2, 2)
- >>> column_indices = (1, 0, 2, 2)
- >>> haar_integral_permutation(row_indices, column_indices, 3)
- Fraction(1, 6)
- >>> haar_integral_permutation(row_indices, column_indices, d)
- 1/(d*(d - 2)*(d - 1))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_permutation
+ >>> d = Symbol('d')
+ >>> row_indices = (0, 1, 2, 2)
+ >>> column_indices = (1, 0, 2, 2)
+ >>> haar_integral_permutation(row_indices, column_indices, 3)
+ Fraction(1, 6)
+ >>> haar_integral_permutation(row_indices, column_indices, d)
+ 1/(d*(d - 2)*(d - 1))
+
+ See Also
+ --------
+ :func:`haarpy.permutation.weingarten_permutation`
+ Returns the Weingarten function for random permutation matrices
"""
if not (isinstance(row_indices, Sequence) and isinstance(column_indices, Sequence)):
raise TypeError
@@ -272,38 +309,50 @@ def sequence_to_partition(sequence: tuple) -> tuple[tuple[int]]:
@lru_cache
def haar_integral_centered_permutation(
- row_indices: tuple[int],
- column_indices: tuple[int],
+ row_indices: tuple[int, ...],
+ column_indices: tuple[int, ...],
dimension: Symbol,
-) -> Symbol:
+) -> Expr:
"""Returns the integral over Haar random centered permutation matrices
- Args:
- row_indices (tuple(int)) : sequence of row indices
- column_indices (tuple(int)) : sequence of column indices
+ Parameters
+ ----------
+ row_indices : tuple[int, ...]
+ A sequence of row indices
- Returns:
- Symbol : integral under the Haar measure
+ column_indices : tuple[int, ...]
+ A sequence of column indices
+
+ Returns
+ -------
+ Expr
+ The integral under the Haar measure
- Raise:
- TypeError : if row_indices and column_indices are not Sequence
- ValueError : if row_indices and column_indices are of different length
+ Raises
+ ------
+ TypeError
+ If the input parameters are not of type ``Sequence``
+ ValueError
+ If the input sequences are of different length
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_centered_permutation
- >>> d = Symbol('d')
- >>> row_indices = (0, 1, 2)
- >>> column_indices = (0, 1, 1)
- >>> haar_integral_centered_permutation(row_indices, column_indices, 3)
- Fraction(-1, 27)
- >>> haar_integral_centered_permutation(row_indices, column_indices, d)
- -2/(d**3*(d - 1))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_centered_permutation
+ >>> d = Symbol('d')
+ >>> row_indices = (0, 1, 2)
+ >>> column_indices = (0, 1, 1)
+ >>> haar_integral_centered_permutation(row_indices, column_indices, 3)
+ Fraction(-1, 27)
+ >>> haar_integral_centered_permutation(row_indices, column_indices, d)
+ -2/(d**3*(d - 1))
See Also
--------
- partial_order, weingarten_centered_permutation
+ :func:`haarpy.partition.partial_order`
+ Compares two partitions in terms of partial order
+ :func:`haarpy.permutation.weingarten_centered_permutation`
+ Returns the Weingarten function for centered random permutation matrices
"""
if not (isinstance(row_indices, Sequence) and isinstance(column_indices, Sequence)):
raise TypeError
diff --git a/haarpy/quantum.py b/haarpy/quantum.py
index 8c214fa..6ff8f15 100644
--- a/haarpy/quantum.py
+++ b/haarpy/quantum.py
@@ -38,22 +38,32 @@ def _haar_integral_quantum(
Parameters
----------
- sequences (tuple[tuple[int]]) : indices of matrix elements
- group_dimension (Symbol) : dimension of the quantum group
- pair (bool) : True for free orthogonal group, False for free symmetric group
+ sequences : tuple[tuple[int, ...], ...]
+ The indices of matrix elements
+
+ group_dimension : Symbol
+ The dimension of the quantum group
+
+ pair : bool
+ ``True`` for free orthogonal group, ``False`` for free symmetric group
Returns
-------
- Expr : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
- Raise
- -----
- TypeError : if the dimension is neither int nor Symbol
- ValueError : if sequences do not contain 2 tuples or if they are of different length
+ Raises
+ ------
+ TypeError
+ If the dimension is neither ``int`` nor ``Symbol``
+ ValueError : if sequences do not contain 2 tuples or if they are of different length
See Also
--------
- gram_matrix
+ :func:`haarpy.partition.gram_matrix`
+ Generates the Gram matrix of a given input set of partitions
+ :func:`haarpy.partition.non_crossing_partitions`
+ Yields non crossing partitions of the set :math:`[n] = \{1,2,...,n\}`
"""
if not isinstance(group_dimension, (Expr, int)):
raise TypeError
@@ -101,27 +111,34 @@ def haar_integral_free_symmetric(
Parameters
----------
- sequences (tuple[tuple[int]]) : indices of matrix elements
- group_dimension (Symbol) : dimension of the quantum group
+ sequences : tuple[tuple[int, ...], ...]
+ Sequences of matrix elements
+
+ group_dimension : Symbol
+ The dimension of the free symmetric group
Returns
-------
- Expr : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_free_symmetric
- >>> d = Symbol("d")
- >>> sequences = ((0, 1, 2), (2, 1, 0))
- >>> haar_integral_free_symmetric(sequences, d)
- 1/(d*(d - 2)*(d - 1))
- >>> haar_integral_free_symmetric(sequences, 4)
- 1/24
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_free_symmetric
+ >>> d = Symbol("d")
+ >>> sequences = ((0, 1, 2), (2, 1, 0))
+ >>> haar_integral_free_symmetric(sequences, d)
+ 1/(d*(d - 2)*(d - 1))
+ >>> haar_integral_free_symmetric(sequences, 4)
+ 1/24
See Also
--------
- _haar_integral_quantum
+ :func:`haarpy.partition.gram_matrix`
+ Generates the Gram matrix of a given input set of partitions
+ :func:`haarpy.partition.non_crossing_partitions`
+ Yields non crossing partitions of the set :math:`[n] = \{1,2,...,n\}`
"""
return _haar_integral_quantum(sequences, group_dimension, False)
@@ -135,26 +152,33 @@ def haar_integral_free_orthogonal(
Parameters
----------
- sequences (tuple[tuple[int]]) : indices of matrix elements
- group_dimension (Symbol) : dimension of the quantum group
+ sequences : tuple[tuple[int, ...], ...]
+ Sequences of matrix elements
+
+ group_dimension : Symbol
+ The dimension of the free orthogonal group
Returns
-------
- Expr : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_free_symmetric
- >>> d = Symbol("d")
- >>> sequences = ((0, 1, 1, 0), (0, 0, 1, 1))
- >>> haar_integral_free_symmetric(sequences, d)
- -1/(d*(d - 1)*(d + 1))
- >>> haar_integral_free_symmetric(sequences, 4)
- -1/60
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_free_symmetric
+ >>> d = Symbol("d")
+ >>> sequences = ((0, 1, 1, 0), (0, 0, 1, 1))
+ >>> haar_integral_free_symmetric(sequences, d)
+ -1/(d*(d - 1)*(d + 1))
+ >>> haar_integral_free_symmetric(sequences, 4)
+ -1/60
See Also
--------
- _haar_integral_quantum
+ :func:`haarpy.partition.gram_matrix`
+ Generates the Gram matrix of a given input set of partitions
+ :func:`haarpy.partition.non_crossing_partitions`
+ Yields non crossing partitions of the set :math:`[n] = \{1,2,...,n\}`
"""
return _haar_integral_quantum(sequences, group_dimension, True)
diff --git a/haarpy/symmetric.py b/haarpy/symmetric.py
index af407f5..d7d6c3e 100644
--- a/haarpy/symmetric.py
+++ b/haarpy/symmetric.py
@@ -19,14 +19,16 @@
[1] Collins, B. (2003). Moments and cumulants of polynomial random variables on unitarygroups,
the Itzykson-Zuber integral, and free probability. International Mathematics Research Notices,
2003(17), 953-982.
+
[2] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
symmetric spaces. arXiv preprint arXiv:1301.5401.
+
[3] Macdonald, I. G. (1998). Symmetric functions and Hall polynomials. Oxford university press.
"""
from math import factorial, prod
from functools import lru_cache
-from typing import Generator
+from collections.abc import Iterator
from fractions import Fraction
from sympy.combinatorics import (
Permutation,
@@ -39,26 +41,29 @@
@lru_cache
def get_conjugacy_class(permutation: Permutation) -> tuple[int, ...]:
- """Returns the conjugacy class of an element of the symmetric group Sp
+ """Returns the conjugacy class of an element of the symmetric group :math:`S_p`
Parameters
----------
- permutation (Permutation) : permutation of the symmetric group
+ permutation : Permutation
+ A permutation of the symmetric group
Returns
-------
- tuple[int] : the conjugacy class (cycle-type) of the permutation
+ tuple[int, ...]
+ The conjugacy class (cycle-type) of the permutation
- Raise
- -----
- TypeError : cycle must be of type sympy.combinatorics.Permutation
+ Raises
+ ------
+ TypeError
+ The cycle must be of type `sympy.combinatorics.Permutation`
Examples
--------
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import get_conjugacy_class
- >>> get_conjugacy_class(Permutation(5)(0,1,3))
- (3, 1, 1, 1)
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import get_conjugacy_class
+ >>> get_conjugacy_class(Permutation(5)(0,1,3))
+ (3, 1, 1, 1)
"""
if not isinstance(permutation, Permutation):
raise TypeError("Permutation must be of type sympy.combinatorics.Permutation")
@@ -87,20 +92,27 @@ def get_conjugacy_class(permutation: Permutation) -> tuple[int, ...]:
def derivative_tableaux(
- tableau: tuple[tuple[int]], increment: int, partition: tuple[int]
-) -> Generator[tuple[tuple[int]], None, None]:
- """Takes a single tableau and adds the selected number to its contents
- in a way that keeps it semi-standard. All possible tableaux are yielded
+ tableau: tuple[tuple[int, ...], ...], increment: int, partition: tuple[int, ...]
+) -> Iterator[tuple[tuple[int, ...], ...]]:
+ """
+ Takes a single tableau and adds the selected number to its contents
+ in a way that keeps it semi-standard. All possible tableaux are yielded.
Parameters
----------
- tableau (tuple[tuple[int]]) : an incomplete Young tableau
- increment (int) : selected number to be added
- partition (tuple[int]) : partition characterizing an irrep of Sp
+ tableau : tuple[tuple[int, ...], ...]
+ An incomplete Young tableau
- Yields
- ------
- tuple[tuple[int]] : modified tableaux
+ increment : int
+ Integer to be added to the tableaux
+
+ partition : tuple[int, ...]
+ A partition characterizing an irrep of the symmetric group
+
+ Returns
+ -------
+ iterator of tuple[tuple[int, ...], ...]
+ Yielded modified tableaux
"""
# empty tableau
if not tableau[0]:
@@ -133,26 +145,30 @@ def derivative_tableaux(
@lru_cache
def semi_standard_young_tableaux(
- partition: tuple[int], conjugacy_class: tuple[int]
-) -> set[tuple[tuple[int]]]:
- """all eligible semi-standard young tableaux based of the partition
+ partition: tuple[int, ...], conjugacy_class: tuple[int, ...]
+) -> set[tuple[tuple[int, ...], ...]]:
+ """All eligible semi-standard young tableaux based of the partition
Parameters
----------
- partition (tuple[int]) : partition characterizing an irrep of Sp
- conjugacy_class (tuple[int]) : a conjugacy class, in partition form, of Sp
+ partition : tuple[int, ...]
+ A partition characterizing an irrep of the symmetric group
+
+ conjugacy_class : tuple[int, ...]
+ A conjugacy class, in partition form, of the symmetric group
Returns
-------
- set[tuple[tuple[int]]] : all eligible semi-standard young tableaux
+ set[tuple[tuple[int, ...], ...]]
+ All eligible semi-standard young tableaux
Examples
--------
- >>> from haarpy import semi_standard_young_tableaux
- >>> semi_standard_young_tableaux((3, 1), (2, 1, 1))
- {((0, 1, 2), (0,)), ((0, 0, 2), (1,)), ((0, 0, 1), (2,))}
- >>> semi_standard_young_tableaux((3,2),(4,1))
- {((0, 0, 1), (0, 0)), ((0, 0, 0), (0, 1))}
+ >>> from haarpy import semi_standard_young_tableaux
+ >>> semi_standard_young_tableaux((3, 1), (2, 1, 1))
+ {((0, 1, 2), (0,)), ((0, 0, 2), (1,)), ((0, 0, 1), (2,))}
+ >>> semi_standard_young_tableaux((3,2),(4,1))
+ {((0, 0, 1), (0, 0)), ((0, 0, 0), (0, 1))}
"""
tableaux = (tuple(() for _ in partition),)
cell_values = (i for i, m in enumerate(conjugacy_class) for _ in range(m))
@@ -167,25 +183,31 @@ def semi_standard_young_tableaux(
@lru_cache
-def proper_border_strip(tableau: tuple[tuple[int]], conjugacy_class: tuple[int]) -> bool:
+def proper_border_strip(
+ tableau: tuple[tuple[int, ...], ...], conjugacy_class: tuple[int, ...]
+) -> bool:
"""Returns True if input Young tableau is a valid border-strip tableau
Parameters
----------
- tableau (tuple[tuple[int]]) : a semi-standard Young tableau
- conjugacy_class (tuple[int]) : a conjugacy class, in partition form, of Sp
+ tableau : tuple[tuple[int, ...], ...]
+ A semi-standard Young tableau
+
+ conjugacy_class : tuple[int, ...]
+ A conjugacy class, in partition form, of the symmetric group
Returns
-------
- bool : True if the tableau is a border-strip
+ bool
+ True if the tableau is a border-strip
Examples
--------
- >>> from haarpy import proper_border_strip
- >>> ap.proper_border_strip(((0, 0, 0), (0, 1)), (4,1))
- True
- >>> ap.proper_border_strip(((0, 0, 1), (0, 0)), (4,1))
- False
+ >>> from haarpy import proper_border_strip
+ >>> ap.proper_border_strip(((0, 0, 0), (0, 1)), (4,1))
+ True
+ >>> ap.proper_border_strip(((0, 0, 1), (0, 0)), (4,1))
+ False
"""
if len(tableau) == 1:
return True
@@ -215,30 +237,34 @@ def proper_border_strip(tableau: tuple[tuple[int]], conjugacy_class: tuple[int])
@lru_cache
-def murn_naka_rule(partition: tuple[int], conjugacy_class: tuple[int]) -> int:
+def murn_naka_rule(partition: tuple[int, ...], conjugacy_class: tuple[int, ...]) -> int:
"""Implementation of the Murnaghan-Nakayama rule for the characters irreducible
- representations of the symmetric group Sp
+ representations of the symmetric group
Parameters
----------
- partition (tuple[int]) : partition characterizing an irrep of Sp
- conjugacy_class (tuple[int]) : a conjugacy class, in partition form, of Sp
+ partition : tuple[int, ...]
+ A partition characterizing an irrep of the symmetric group
+ conjugacy_class : tuple[int, ...]
+ A conjugacy class, in partition form, of the symmetric group
Returns
-------
- int : character of the elements in class mu of the irrep of the symmetric group
+ int
+ The character of the input permutation in the given irrep of the symmetric group
Examples
--------
- >>> from haarpy import murn_naka_rule
- >>> murn_naka_rule((4, 1, 1), (3, 2, 1))
- -1
- >>> murn_naka_rule((3, 1), (1, 1, 1, 1))
- 3
+ >>> from haarpy import murn_naka_rule
+ >>> murn_naka_rule((4, 1, 1), (3, 2, 1))
+ -1
+ >>> murn_naka_rule((3, 1), (1, 1, 1, 1))
+ 3
See Also
--------
- semi_standard_young_tableaux, proper_border_strip
+ :func:`haarpy.symmetric.semi_standard_young_tableaux`
+ Returns all eligible semi-standard young tableaux based of the partition
"""
if sum(partition) != sum(conjugacy_class):
return 0
@@ -257,22 +283,24 @@ def murn_naka_rule(partition: tuple[int], conjugacy_class: tuple[int]) -> int:
@lru_cache
-def irrep_dimension(partition: tuple[int]) -> int:
- """Returns the dimension of the irrep of the symmetric group Sp labelled by the input partition
+def irrep_dimension(partition: tuple[int, ...]) -> int:
+ """Returns the dimension of the irrep of the symmetric group labelled by the input partition
Parameters
----------
- partition (tuple[int]) : a partition labelling an irrep of Sp
+ partition : tuple[int]
+ A partition labelling an irrep of the symmetric group
Returns
-------
- int : the dimension of the irrep
+ int
+ The dimension of the irrep
Examples
--------
- >>> from haarpy import irrep_dimension
- >>> irrep_dimension((2, 1, 1))
- 3
+ >>> from haarpy import irrep_dimension
+ >>> irrep_dimension((2, 1, 1))
+ 3
"""
numerator = prod(
part_i - part_j + j + 1
@@ -286,37 +314,39 @@ def irrep_dimension(partition: tuple[int]) -> int:
@lru_cache
-def sorting_permutation(*sequence: tuple[int]) -> Permutation:
- """Returns the sorting permutation of a given sequence
-
- If two sequences, sequence_1 and sequence_2, are given as parameters,
- the function returns the permutation such that
- permutation(sequence_1) = sequence_2
-
- Note that the function return the first permutation found if the sequences
- contain multiplicity
+def sorting_permutation(*sequence: tuple[int, ...]) -> Permutation:
+ """Returns the sorting permutation of a given sequence. If two sequences are given
+ as parameters, the function returns the permutation sending the first sequence to the second.
Parameters
----------
- *sequence (tuple[int]) : one or two sequences of unorderd elements
+ *sequence : tuple[int])
+ One or two unorderd sequences
Returns
-------
- Permutation : the sorting permutation
+ Permutation
+ Tthe sorting permutation
- Raise
+ Raises
+ ------
+ ValueError
+ If the two sequences are incompatible
+ TypeError
+ If more than two sequences are passed as arguments
+
+ Notes
-----
- ValueError : for incompatible sequence inputs
- TypeError : if more than two sequences are passed as arguments
+ If the sorting permutation is not unique, the function returns the first permutation obtained.
Examples
--------
- >>> from haarpy import sorting_permutation
- >>> sequence_1, sequence_2 = (2, 1, 2, 1, 3), (3, 2, 2, 1, 1)
- >>> sorting_permutation(sequence_1)
- Permutation(4)(0, 1, 3, 2)
- >>> sorting_permutation(sequence_1, sequence_2)
- Permutation(0, 4, 3, 1)
+ >>> from haarpy import sorting_permutation
+ >>> sequence_1, sequence_2 = (2, 1, 2, 1, 3), (3, 2, 2, 1, 1)
+ >>> sorting_permutation(sequence_1)
+ Permutation(4)(0, 1, 3, 2)
+ >>> sorting_permutation(sequence_1, sequence_2)
+ Permutation(0, 4, 3, 1)
"""
if len(sequence) == 1:
return Permutation(sorted(range(len(sequence[0])), key=lambda k: sequence[0][k]))
@@ -329,32 +359,32 @@ def sorting_permutation(*sequence: tuple[int]) -> Permutation:
# pylint: disable=invalid-name
-def YoungSubgroup(partition: tuple[int]) -> PermutationGroup:
+def YoungSubgroup(partition: tuple[int, ...]) -> PermutationGroup:
"""Returns the Young subgroup of a given input partition
Parameters
----------
- partition (tuple[int]) : a partition
+ partition : tuple[int, ...]
+ A partition
Returns
-------
- PermutationGroup : the associated Young subgroup
+ PermutationGroup
+ The associated Young subgroup
- Raise
- -----
- TypeError : if partition is not a tuple or a list
- TypeError : if partition is not made of positive integers
+ Raises
+ ------
+ TypeError
+ If the partition is neither a tuple or a list
+ TypeError
+ If the partition is not made of positive integers
Examples
--------
- >>> from haarpy import YoungSubgroup
- >>> young = YoungSubgroup((2, 2))
- >>> list(young.generate())
- [Permutation(3), Permutation(3)(0, 1), Permutation(2, 3), Permutation(0, 1)(2, 3)]
-
- See Also
- --------
- sympy.combinatorics.DirectProduct, sympy.combinatorics.SymmetricGroup
+ >>> from haarpy import YoungSubgroup
+ >>> young = YoungSubgroup((2, 2))
+ >>> list(young.generate())
+ [Permutation(3), Permutation(3)(0, 1), Permutation(2, 3), Permutation(0, 1)(2, 3)]
"""
if not isinstance(partition, (tuple, list)):
raise TypeError
@@ -363,37 +393,39 @@ def YoungSubgroup(partition: tuple[int]) -> PermutationGroup:
return DirectProduct(*[SymmetricGroup(part) for part in partition])
-def stabilizer_coset(*sequence: tuple) -> Generator[Permutation, None, None]:
- """Returns all permutations that, when acting on sequence[0], return sequence[1]
-
- For a single input, the function returns the stabilizer group with respect to the sequence.
- For two inputs, it returns the stabilizer of the first sequence with respect to the second,
- that is, all permutations such that permutation(sequence[0]) == sequence[1].
+def stabilizer_coset(*sequence: tuple) -> Iterator[Permutation]:
+ """Returns all permutations that sends the first sequences to the second. For a single input,
+ the function returns the stabilizer group with respect to the sequence. For two inputs,
+ it returns the stabilizer of the first sequence with respect to the second.
Parameters
----------
- *sequence (tuple) : the sequences acted upon
+ *sequence : tuple
+ The sequences acted upon
Returns
-------
- Generator[Permutation] : permutations that, when acting on sequence[0], return sequence[1]
+ Iterator[Permutation]
+ The stabilizer group
- Raise
- -----
- TypeError : if the sequence argument contains more than two sequences
+ Raises
+ ------
+ TypeError
+ If the sequence argument contains more than two sequences
Examples
--------
- >>> from haarpy import stabilizer_coset
- >>> sequence_1, sequence_2 = (2, 2, 1, 3), (3, 2, 2, 1)
- >>> list(stabilizer_coset(sequence_1))
- [Permutation(3), Permutation(3)(0, 1)]
- >>> list(stabilizer_coset(sequence_1, sequence_2))
- [Permutation(0, 3, 2, 1), Permutation(0, 3, 2)]
+ >>> from haarpy import stabilizer_coset
+ >>> sequence_1, sequence_2 = (2, 2, 1, 3), (3, 2, 2, 1)
+ >>> list(stabilizer_coset(sequence_1))
+ [Permutation(3), Permutation(3)(0, 1)]
+ >>> list(stabilizer_coset(sequence_1, sequence_2))
+ [Permutation(0, 3, 2, 1), Permutation(0, 3, 2)]
See Also
--------
- sorting_permutation, YoungSubgroup
+ :func:`haarpy.symmetric.YoungSubgroup`
+ The Young subgroup of a given input partition
"""
if len(sequence) == 1:
sequence = tuple(sequence[0] for _ in range(2))
@@ -414,30 +446,29 @@ def stabilizer_coset(*sequence: tuple) -> Generator[Permutation, None, None]:
# pylint: disable=invalid-name
@lru_cache
def HyperoctahedralGroup(degree: int) -> PermutationGroup:
- """Return the hyperoctahedral group
+ """Returns the hyperoctahedral group :math:`H_p`
Parameters
----------
- degree (int) : the degree k of the hyperoctahedral group H_k
+ degree : int
+ The degree :math:`p` of the hyperoctahedral group :math:`H_p`
Returns
-------
- PermutationGroup : the hyperoctahedral group
+ PermutationGroup
+ The hyperoctahedral group
- Raise
- -----
- TypeError : if degree is not of type int
+ Raises
+ ------
+ TypeError
+ If the degree is not an integer
Examples
--------
- >>> from haarpy import HyperoctahedralGroup
- >>> hyperoctahedral = ap.HyperoctahedralGroup(3)
- >>> hyperoctahedral.order()
- 48
-
- See Also
- --------
- sympy.combinatorics.PermutationGroup
+ >>> from haarpy import HyperoctahedralGroup
+ >>> hyperoctahedral = ap.HyperoctahedralGroup(3)
+ >>> hyperoctahedral.order()
+ 48
"""
if not isinstance(degree, int):
raise TypeError
@@ -450,28 +481,31 @@ def HyperoctahedralGroup(degree: int) -> PermutationGroup:
return PermutationGroup(transpositions + double_transpositions)
-def hyperoctahedral_transversal(degree: int) -> Generator[Permutation, None, None]:
- """Returns a generator with the permutations of M_2k, the complete set of coset
- representatives of S_2k/H_k
+def hyperoctahedral_transversal(degree: int) -> Iterator[Permutation]:
+ """Yields the permutations of :math:`M_{2p}`, the complete set of coset
+ representatives of the quotient group :math:`S_{2p}/H_p`
Parameters
----------
- degree (int) : degree 2k of the set M_2k
+ degree : int
+ The degree :math:`2p` of the associated symmetric group :math:`S_{2p}`
Returns
-------
- Generator[Permutation] : the permutations of M_2k
+ Iterator[Permutation]
+ The permutations of :math:`M_{2p}`
Examples
--------
- >>> from haarpy import hyperoctahedral_transversal
- >>> transversal = ap.hyperoctahedral_transversal(4)
- >>> list(transversal)
- [Permutation(3), Permutation(3)(1, 2), Permutation(1, 3, 2)]
+ >>> from haarpy import hyperoctahedral_transversal
+ >>> transversal = ap.hyperoctahedral_transversal(4)
+ >>> list(transversal)
+ [Permutation(3), Permutation(3)(1, 2), Permutation(1, 3, 2)]
See Also
--------
- pair_partitions
+ :func:`haarpy.partition.pair_partitions`
+ Yields the pairings of a given set
"""
if degree % 2:
raise ValueError("degree should be a factor of 2")
@@ -484,32 +518,32 @@ def hyperoctahedral_transversal(degree: int) -> Generator[Permutation, None, Non
@lru_cache
-def coset_type(permutation: Permutation) -> tuple[int]:
- """Returns the coset-type of a given permutation of S_2k
+def coset_type(permutation: Permutation) -> tuple[int, ...]:
+ """Returns the coset-type of a given permutation of the symmetric group
Parameters
----------
- permutation (Permutation) : a permutation of the symmetric group S_2k
+ permutation : Permutation
+ A permutation of the symmetric group :math:`S_{2p}`
Returns
-------
- tuple[int] : the associated coset-type as a partition of k
+ tuple[int]
+ The coset-type as a partition of :math:`p`
- Raise
- -----
- TypeError : if partition is not a Permutation
- ValueError : if the symmetric group is of odd degree
+ Raises
+ ------
+ TypeError
+ If the permutation is of incorrect type
+ ValueError
+ If the symmetric group is of odd degree
Examples
--------
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import coset_type
- >>> coset_type(Permutation(0, 5, 4, 2, 1, 3))
- (2, 1)
-
- See Also
- --------
- join_operation
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import coset_type
+ >>> coset_type(Permutation(0, 5, 4, 2, 1, 3))
+ (2, 1)
"""
if not isinstance(permutation, Permutation):
raise TypeError
@@ -532,27 +566,30 @@ def coset_type(permutation: Permutation) -> tuple[int]:
@lru_cache
-def coset_type_representative(partition: tuple[int]) -> Permutation:
- """Returns a representative permutation of S_2k for a given
- input coset-type (partition of k)
+def coset_type_representative(partition: tuple[int, ...]) -> Permutation:
+ """Returns a representative permutation of the symmetric group :math:`S_{2p}`
+ for a given input coset-type
Parameters
----------
- partition (tuple[int]) : the coset-type (partition of k)
+ partition : tuple[int, ...]
+ The coset-type as a partition of :math:`p`
Returns
-------
- Permutation : the associated permutation of S_2k
+ Permutation
+ The representative
- Raise
- -----
- TypeError : if partition is not a tuple
+ Raises
+ ------
+ TypeError
+ If the input partition is of incorrect type
Examples
--------
- >>> from haarpy import coset_type_representative
- >>> coset_type_representative((2, 1))
- Permutation(5)(1, 3, 2)
+ >>> from haarpy import coset_type_representative
+ >>> coset_type_representative((2, 1))
+ Permutation(5)(1, 3, 2)
"""
if not isinstance(partition, tuple):
raise TypeError
diff --git a/haarpy/symplectic.py b/haarpy/symplectic.py
index a8bd7f2..46467a8 100644
--- a/haarpy/symplectic.py
+++ b/haarpy/symplectic.py
@@ -18,8 +18,10 @@
----------
[1] Collins, B., & Śniady, P. (2006). Integration with respect to the Haar measure on unitary,
orthogonal and symplectic group. Communications in Mathematical Physics, 264(3), 773-795.
+
[2] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
symmetric spaces. arXiv preprint arXiv:1301.5401.
+
[3] Macdonald, I. G. (1998). Symmetric functions and Hall polynomials. Oxford university press.
"""
@@ -42,35 +44,45 @@
@lru_cache
-def twisted_spherical_function(permutation: Permutation, partition: tuple[int]) -> Fraction:
- """Returns the twisted spherical function of the Gelfand pair (S_2k, H_k)
+def twisted_spherical_function(permutation: Permutation, partition: tuple[int, ...]) -> Fraction:
+ """Returns the twisted spherical function of the Gelfand pair :math:`(S_{2p}, H_p)`
Parameters
----------
- permutation (Permutation) : a permutation of the symmetric group S_2k
- partition (tuple[int]) : a partition of k
+ permutation : Permutation
+ A permutation of the symmetric group :math:`S_{2p}`
+
+ partition : tuple[int, ...]
+ An integer partition of :math:`p`
Returns
-------
- Fraction : the twisted spherical function of the given permutation
-
- Raise
- -----
- TypeError : if partition is not a tuple
- TypeError : if permutation argument is not a permutation.
- ValueError : if the degree of the permutation is not a factor of 2
- ValueError : if the degree of the partition and the permutation are incompatible
+ Fraction
+ The twisted spherical function of the given permutation
+
+ Raises
+ ------
+ TypeError
+ If ``partition`` is not of type ``tuple``
+ TypeError
+ If ``permutation`` of type ``Permutation``
+ ValueError
+ If the degree of the permutation is odd
+ ValueError
+ If the partition and the permutation are incompatible
Examples
--------
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import twisted_spherical_function
- >>> twisted_spherical_function(Permutation(5, 0, 1, 2), (2, 1))
- Fraction(1, 4)
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import twisted_spherical_function
+ >>> twisted_spherical_function(Permutation(5, 0, 1, 2), (2, 1))
+ Fraction(1, 4)
See Also
--------
- murn_naka_rule
+ :func:`haarpy.symmetric.murn_naka_rule`
+ Implementation of the Murnaghan-Nakayama rule for the characters irreducible
+ representations of the symmetric group
"""
if not isinstance(partition, tuple):
raise TypeError
@@ -101,31 +113,36 @@ def weingarten_symplectic(permutation: Permutation, half_dimension: Symbol) -> E
Parameters
----------
- permutation (Permutation) : a permutation of the symmetric group S_2k
- half_dimension (Symbol) : half the dimension of the symplectic group
+ permutation : Permutation
+ A permutation of the symmetric group :math:`S_{2p}`
+ half_dimension : Symbol
+ Half the dimension of the symplectic group
Returns
-------
- Expr : the Weingarten function
+ Expr
+ The Weingarten function
- Raise
- -----
- ValueError : if the degree 2k of the symmetric group S_2k is not a factor of 2
+ Raises
+ ------
+ ValueError
+ If the degree :math:`2p` of the symmetric group :math:`S_{2p}` is odd
Examples
--------
- >>> from sympy import Symbol
- >>> from sympy.combinatorics import Permutation
- >>> from haarpy import weingarten_symplectic
- >>> d = Symbol("d")
- >>> weingarten_symplectic(Permutation(0, 1, 2, 3, 4, 5), 7)
- Fraction(-1, 201600)
- >>> weingarten_symplectic(Permutation(0, 1, 2, 3, 4, 5), d)
- -1/(8*d*(d - 2)*(d - 1)*(d + 1)*(2*d + 1))
+ >>> from sympy import Symbol
+ >>> from sympy.combinatorics import Permutation
+ >>> from haarpy import weingarten_symplectic
+ >>> d = Symbol("d")
+ >>> weingarten_symplectic(Permutation(0, 1, 2, 3, 4, 5), 7)
+ Fraction(-1, 201600)
+ >>> weingarten_symplectic(Permutation(0, 1, 2, 3, 4, 5), d)
+ -1/(8*d*(d - 2)*(d - 1)*(d + 1)*(2*d + 1))
See Also
--------
- twisted_spherical_function, sympy.utilities.iterables.partitions
+ :func:`haarpy.symplectic.twisted_spherical_function`
+ Computes the twisted spherical function of the Gelfand pair :math:`(S_{2p}, H_p)`
"""
degree = permutation.size
if degree % 2:
@@ -182,48 +199,63 @@ def weingarten_symplectic(permutation: Permutation, half_dimension: Symbol) -> E
@lru_cache
def haar_integral_symplectic(
- sequences: tuple[tuple[Expr]],
+ sequences: tuple[tuple[Expr, ...], ...],
half_dimension: Symbol,
) -> Expr:
"""Returns integral over symplectic group polynomial sampled at random from the Haar measure
Parameters
----------
- sequences (tuple[tuple[Expr]]) : indices of matrix elements
- half_dimension (Symbol) : half the dimension of the symplectic group
+ sequences : tuple[tuple[Expr, ...], ...]
+ Indices of matrix elements
+
+ half_dimension : Symbol
+ Half the dimension of the symplectic group
Returns
-------
- Expr : integral under the Haar measure
-
- Raise
- -----
- ValueError : if sequences do not contain 2 tuples
- ValueError : if tuples i and j are of different length
- TypeError : if the half_dimension is not int nor Symbol
- TypeError : if dimension is int and sequence is not
- ValueError : if all sequence indices are not between 0 and 2*dimension - 1
- TypeError : if sequence containt something else than Expr
- TypeError : if symbolic sequences have the wrong format
+ Expr
+ Integral under the Haar measure
+
+ Raises
+ ------
+ ValueError
+ If ``sequences`` do not contain 2 tuples
+ ValueError
+ If the sequences are of different lengths
+ TypeError
+ If ``half_dimension`` is neither ``int`` or ``Symbol``
+ TypeError
+ If ``half_dimension`` is an ``int`` but ``sequences`` contain a ``Symbol``
+ ValueError
+ If not all sequence indices are between ``0`` and ``2*half_dimension - 1``
+ TypeError
+ If ``sequences`` containt something else than ``Expr`` and ``int``
+ TypeError
+ If symbolic sequences have the wrong format
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_symplectic
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_symplectic
- >>> d = Symbol("d")
- >>> sequence_1, sequence_2 = (0, 1, 2, d, d+1, d+2), (0, 0, 0, d, d, d)
- >>> haar_integral_symplectic((sequence_1, sequence_2), d)
- 1/(4*d*(d + 1)*(2*d + 1))
+ >>> d = Symbol("d")
+ >>> sequence_1, sequence_2 = (0, 1, 2, d, d+1, d+2), (0, 0, 0, d, d, d)
+ >>> haar_integral_symplectic((sequence_1, sequence_2), d)
+ 1/(4*d*(d + 1)*(2*d + 1))
- >>> d = 4
- >>> sequence_1, sequence_2 = (0, 1, 2, d, d+1, d+2), (0, 0, 0, d, d, d)
- >>> haar_integral_symplectic((sequence_1, sequence_2), d)
- Fraction(1, 720)
+ >>> d = 4
+ >>> sequence_1, sequence_2 = (0, 1, 2, d, d+1, d+2), (0, 0, 0, d, d, d)
+ >>> haar_integral_symplectic((sequence_1, sequence_2), d)
+ Fraction(1, 720)
See Also
--------
- hyperoctahedral_transversal, weingarten_symplectic
+ :func:`haarpy.symmetric.hyperoctahedral_transversal`
+ Yields the permutations of :math:`M_{2p}`, the complete set of coset
+ representatives of the quotient group :math:`S_{2p}/H_p`
+ :func:`haarpy.symplectic.weingarten_symplectic`
+ Returns the symplectic Weingarten function
"""
if len(sequences) != 2:
raise ValueError("Wrong sequence format")
diff --git a/haarpy/unitary.py b/haarpy/unitary.py
index 7a80900..18a5d05 100644
--- a/haarpy/unitary.py
+++ b/haarpy/unitary.py
@@ -19,13 +19,13 @@
[1] Collins, B. (2003). Moments and cumulants of polynomial random variables on unitarygroups,
the Itzykson-Zuber integral, and free probability. International Mathematics Research Notices,
2003(17), 953-982.
+
[2] Matsumoto, S. (2013). Weingarten calculus for matrix ensembles associated with compact
symmetric spaces. arXiv preprint arXiv:1301.5401.
"""
from math import factorial, prod
from functools import lru_cache
-from typing import Union
from itertools import product
from collections import Counter
from fractions import Fraction
@@ -42,27 +42,31 @@
@lru_cache
-def representation_dimension(partition: tuple[int], unitary_dimension: Symbol) -> Expr:
- """Returns the dimension of the unitary group U(d) labelled by the input partition
+def representation_dimension(partition: tuple[int, ...], unitary_dimension: Symbol) -> Expr:
+ """Returns the dimension of the unitary group's representation labelled by the input partition
Parameters
----------
- partition (tuple[int]) : a partition labelling a representation of U(d)
- unitary_dimension (Symbol) : dimension d of the unitary matrix U
+ partition : tuple[int, ...]
+ A partition labelling a representation of the unitary group :math:`U(d)`
+
+ unitary_dimension : Symbol
+ The dimension :math:`d` of the unitary group
Returns
-------
- Expr : the dimension of the representation of U(d) labeled by the partition
+ Expr
+ The dimension of the unitary group's representation labelled by the input partition
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import representation_dimension
- >>> d = Symbol("d")
- >>> representation_dimension((2,1,1), 4)
- 15
- >>> representation_dimension((2,1,1), d)
- d*(d/2 - 1/2)*(d - 2)*(d + 1)/4
+ >>> from sympy import Symbol
+ >>> from haarpy import representation_dimension
+ >>> d = Symbol("d")
+ >>> representation_dimension((2,1,1), 4)
+ 15
+ >>> representation_dimension((2,1,1), d)
+ d*(d/2 - 1/2)*(d - 2)*(d + 1)/4
"""
conjugate_partition = tuple(
sum(1 for part in partition if i < part) for i in range(partition[0])
@@ -89,40 +93,54 @@ def representation_dimension(partition: tuple[int], unitary_dimension: Symbol) -
@lru_cache
-def weingarten_unitary(cycle: Union[Permutation, tuple[int]], unitary_dimension: Symbol) -> Expr:
- """Returns the Weingarten function
-
- The function works with both a permutation or a conjugacy class as a partition
+def weingarten_unitary(cycle: Permutation | tuple[int, ...], unitary_dimension: Symbol) -> Expr:
+ """Returns the unitary Weingarten function
Parameters
----------
- cycle (Permutation, tuple[int]) : permutation from the symmetric group or its cycle-type
- unitary_dimension (Symbol) : dimension d of the unitary matrix U
+ cycle : Permutation | tuple[int, ...]
+ A permutation from the symmetric group or a partition reprensenting its cycle-type
+
+ unitary_dimension : Symbol
+ The dimension :math:`d` of the unitary matrix :math:`U(d)`
Returns
-------
- Expr : the Weingarten function
+ Expr
+ The Weingarten function
- Raise
+ Raises
+ ------
+ TypeError
+ If unitary_dimension has the wrong type
+ TypeError
+ If cycle has the wrong type
+
+ Notes
-----
- TypeError : if unitary_dimension has the wrong type
- TypeError : if cycle has the wrong type
+ Since the unitary Weingarten function is a class function on the symmetric group, the argument
+ may be given either as a permutation or as its cycle-type
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import weingarten_unitary
- >>> d = Symbol("d")
- >>> weingarten_unitary(Permutation(2)(0, 1), 4)
- Fraction(-1, 180)
- >>> weingarten_unitary(Permutation(2)(0, 1), d)
- -1/((d - 2)*(d - 1)*(d + 1)*(d + 2))
- >>> weingarten_unitary((2, 1), d)
- -1/((d - 2)*(d - 1)*(d + 1)*(d + 2))
+ >>> from sympy import Symbol
+ >>> from haarpy import weingarten_unitary
+ >>> d = Symbol("d")
+ >>> weingarten_unitary(Permutation(2)(0, 1), 4)
+ Fraction(-1, 180)
+ >>> weingarten_unitary(Permutation(2)(0, 1), d)
+ -1/((d - 2)*(d - 1)*(d + 1)*(d + 2))
+ >>> weingarten_unitary((2, 1), d)
+ -1/((d - 2)*(d - 1)*(d + 1)*(d + 2))
See Also
--------
- murn_naka_rule, representation_dimension, sympy.utilities.iterables.partitions
+ :func:`haarpy.symmetric.murn_naka_rule`
+ Implementation of the Murnaghan-Nakayama rule for the characters irreducible
+ representations of the symmetric group :math:`S_p`
+ :func:`haarpy.unitary.representation_dimension`
+ Computes the dimension of the unitary group's representation labelled by a
+ given partition
"""
if not isinstance(unitary_dimension, (Expr, int)):
raise TypeError("unitary_dimension must be an instance of int or sympy.Expr")
@@ -164,38 +182,49 @@ def weingarten_unitary(cycle: Union[Permutation, tuple[int]], unitary_dimension:
@lru_cache
-def haar_integral_unitary(sequences: tuple[tuple[int]], unitary_dimension: Symbol) -> Expr:
- """Returns integral over unitary group polynomial sampled at random from the Haar measure
+def haar_integral_unitary(
+ sequences: tuple[tuple[int, ...], ...], unitary_dimension: Symbol
+) -> Expr:
+ """Returns the integral of a monomial over the unitary group
Parameters
----------
- sequences (tuple[tuple[int]]) : indices of matrix elements
- unitary_dimension (Symbol) : dimension of the unitary group
+ sequences : tuple[tuple[int, ...], ...]
+ Sequences of matrix elements
+
+ unitary_dimension : Symbol
+ The dimension of the unitary group
Returns
-------
- Expr : integral under the Haar measure
+ Expr
+ The integral under the Haar measure
- Raise
- -----
- ValueError : if sequences do not contain 4 tuples
- ValueError : if tuples i and j are of different length
+ Raises
+ ------
+ ValueError
+ if sequences do not contain 4 tuples
+ ValueError
+ if the input sequences are of different lengths
Examples
--------
- >>> from sympy import Symbol
- >>> from haarpy import haar_integral_unitary
- >>> d = Symbol("d")
- >>> seq_i, seq_j = (0, 1, 2), (0, 0, 1)
- >>> seq_i_prime, seq_j_prime = (0, 1, 2), (0, 1, 0)
- >>> haar_integral_unitary((seq_i, seq_j, seq_i_prime, seq_j_prime), 5)
- Fraction(-1, 840)
- >>> haar_integral_unitary((seq_i, seq_j, seq_i_prime, seq_j_prime), d)
- -1/(d*(d - 1)*(d + 1)*(d + 2))
+ >>> from sympy import Symbol
+ >>> from haarpy import haar_integral_unitary
+ >>> d = Symbol("d")
+ >>> seq_i, seq_j = (0, 1, 2), (0, 0, 1)
+ >>> seq_i_prime, seq_j_prime = (0, 1, 2), (0, 1, 0)
+ >>> haar_integral_unitary((seq_i, seq_j, seq_i_prime, seq_j_prime), 5)
+ Fraction(-1, 840)
+ >>> haar_integral_unitary((seq_i, seq_j, seq_i_prime, seq_j_prime), d)
+ -1/(d*(d - 1)*(d + 1)*(d + 2))
See Also
--------
- stabilizer_coset, weingarten_unitary
+ :func:`haarpy.symmetric.stabilizer_coset`
+ Returns all permutations sending a first sequence to a second sequence
+ :func:`haarpy.unitary.weingarten_unitary`
+ Returns the unitary Weingarten function
"""
if len(sequences) != 4:
raise ValueError("Wrong tuple format")
diff --git a/setup.py b/setup.py
index 3132d05..92cc71e 100644
--- a/setup.py
+++ b/setup.py
@@ -33,10 +33,10 @@
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering :: Physics",
]