diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 886a077..ec3733c 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -3,10 +3,9 @@ ### New features * Added a new module `orthogonal` allowing the calculation of the orthogonal Weingarten function and its moments [(#28)](https://github.com/polyquantique/haarpy/pull/28). * Added a new module `symplectic` allowing the calculation of the symplectic Weingarten function [(#31)](https://github.com/polyquantique/haarpy/pull/31). -* Added a new module `circular_ensembles` allowing the calculation of the circular orthogonal ensembles and circular symplectic ensembles Weingarten functions [(#32)](https://github.com/polyquantique/haarpy/pull/32). +* Added a new module `circular_ensembles` allowing the calculation of the circular orthogonal ensembles and circular symplectic ensembles Weingarten functions [(#32)](https://github.com/polyquantique/haarpy/pull/32) and moments [(#43)](https://github.com/polyquantique/haarpy/pull/43). * Added a new module `permutation` allowing the calculation of the permutation matrices and centered permuation matrices' Weingarten functions as well as their moments [(#36)](https://github.com/polyquantique/haarpy/pull/36). * Added a new module `partition` allowing to generate partitions of a set as well as implementing some operations on them such as the meet and the join operations [(#36)](https://github.com/polyquantique/haarpy/pull/36). -* Added moment calculation for Haar random symplectic matrices and circular symplectic ensemble [(#43)](https://github.com/polyquantique/haarpy/pull/43). ### Breaking changes @@ -15,11 +14,13 @@ * `weingarten_class()` -> `weingarten_unitary()` * `weingarten_element()` -> `weingarten_unitary()` The argument `degree` has been removed for the degree of the symmetric group is already contained in both the permutation and the conjugacy class. * `coset_type()` -> `coset_type_representative()` +* `get_conjugacy_class()` : `degree` has been removed from the arguments since it is already contained in `permutation` [(#51)](https://github.com/polyquantique/haarpy/pull/51). ### Improvements * The `README` has been improved to describe the expanding capabilities of `haarpy` [(#39)](https://github.com/polyquantique/haarpy/pull/39). * 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). ### Bug fixes diff --git a/haarpy/orthogonal.py b/haarpy/orthogonal.py index d9c86ab..0a57491 100644 --- a/haarpy/orthogonal.py +++ b/haarpy/orthogonal.py @@ -88,7 +88,7 @@ def zonal_spherical_function(permutation: Permutation, partition: tuple[int]) -> double_partition = tuple(2 * part for part in partition) hyperocta = HyperoctahedralGroup(degree // 2) numerator = sum( - murn_naka_rule(double_partition, get_conjugacy_class(permutation * zeta, degree)) + murn_naka_rule(double_partition, get_conjugacy_class(permutation * zeta)) for zeta in hyperocta.generate() ) return Fraction(numerator, hyperocta.order()) diff --git a/haarpy/symmetric.py b/haarpy/symmetric.py index a1c579f..480296d 100644 --- a/haarpy/symmetric.py +++ b/haarpy/symmetric.py @@ -38,53 +38,52 @@ @lru_cache -def get_conjugacy_class(perm: Permutation, degree: int) -> tuple: +def get_conjugacy_class(permutation: Permutation) -> tuple[int, ...]: """Returns the conjugacy class of an element of the symmetric group Sp Parameters ---------- - perm (Permutation) : permutation cycle from the symmetric group - degree (integer) : order of the symmetric group + permutation (Permutation) : permutation of the symmetric group Returns ------- - tuple[int] : the conjugacy class in partition form + tuple[int] : the conjugacy class (cycle-type) of the permutation Raise ----- - TypeError : order must be of type int - ValueError : if order is not an integer greaten than 1 - TypeError : cycle must be of type sympy.combinatorics.permutations.Permutation - ValueError : incompatible degree and permutation cycle + TypeError : 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), 6) + >>> get_conjugacy_class(Permutation(5)(0,1,3)) (3, 1, 1, 1) """ - if not isinstance(degree, int): - raise TypeError("degree must be of type int") + if not isinstance(permutation, Permutation): + raise TypeError("Permutation must be of type sympy.combinatorics.Permutation") + perm_array = permutation.array_form + degree = len(perm_array) - if degree < 1: - raise ValueError( - "The degree you have provided is too low. It must be an integer greater than 0." - ) - if not isinstance(perm, Permutation): - raise TypeError("Permutation must be of type sympy.combinatorics.permutations.Permutation") + visited = [False] * degree + cycle_type = [] - if perm.size > degree: - raise ValueError("Incompatible degree and permutation cycle") + for i in range(degree): + if not visited[i]: + j = i + cycle_len = 0 - perm = perm * Permutation(degree - 1) + for _ in range(degree): + visited[j] = True + j = perm_array[j] + cycle_len += 1 + if visited[j]: + break - return tuple( - sorted( - (key for key, value in perm.cycle_structure.items() for _ in range(value)), - reverse=True, - ) - ) + cycle_type.append(cycle_len) + + cycle_type.sort(reverse=True) + return tuple(cycle_type) def derivative_tableaux( diff --git a/haarpy/symplectic.py b/haarpy/symplectic.py index a25ea34..7a066fe 100644 --- a/haarpy/symplectic.py +++ b/haarpy/symplectic.py @@ -87,7 +87,7 @@ def twisted_spherical_function(permutation: Permutation, partition: tuple[int]) duplicate_partition = tuple(part for part in partition for _ in range(2)) hyperocta = HyperoctahedralGroup(degree // 2) numerator = sum( - murn_naka_rule(duplicate_partition, get_conjugacy_class(~zeta * permutation, degree)) + murn_naka_rule(duplicate_partition, get_conjugacy_class(~zeta * permutation)) * zeta.signature() for zeta in hyperocta.generate() ) diff --git a/haarpy/tests/test_symmetric.py b/haarpy/tests/test_symmetric.py index 0f78a25..127a11a 100644 --- a/haarpy/tests/test_symmetric.py +++ b/haarpy/tests/test_symmetric.py @@ -27,82 +27,42 @@ @pytest.mark.parametrize( - "degree, cycle, conjugacy", + "cycle, conjugacy", [ - (3, Permutation(0, 1, 2), (3,)), - (3, Permutation(2)(0, 1), (2, 1)), - (4, Permutation(0, 1, 2, 3), (4,)), - (4, Permutation(0, 1, 2), (3, 1)), - (4, Permutation(0, 1)(2, 3), (2, 2)), - (5, Permutation(0, 1, 2), (3, 1, 1)), - (5, Permutation(1, 2, 4), (3, 1, 1)), - (5, Permutation(3)(0, 1, 2), (3, 1, 1)), - (6, Permutation(2)(3, 4, 5)(0, 1), (3, 2, 1)), - (6, Permutation(2, 3, 4, 0, 1, 5), (6,)), - (7, Permutation(2, 3, 4, 0, 1, 6), (6, 1)), - (1, Permutation(0), (1,)), + (Permutation(0, 1, 2), (3,)), + (Permutation(2)(0, 1), (2, 1)), + (Permutation(0, 1, 2, 3), (4,)), + (Permutation(3)(0, 1, 2), (3, 1)), + (Permutation(0, 1)(2, 3), (2, 2)), + (Permutation(4)(0, 1, 2), (3, 1, 1)), + (Permutation(1, 2, 4), (3, 1, 1)), + (Permutation(4)(0, 1, 2), (3, 1, 1)), + (Permutation(2)(3, 4, 5)(0, 1), (3, 2, 1)), + (Permutation(2, 3, 4, 0, 1, 5), (6,)), + (Permutation(2, 3, 4, 0, 1, 6), (6, 1)), + (Permutation(0), (1,)), ], ) -def test_get_conjugacy_class(degree, cycle, conjugacy): +def test_get_conjugacy_class(cycle, conjugacy): "Test the normal usage of get_conjugacy_class" - assert ap.get_conjugacy_class(cycle, degree) == conjugacy + assert ap.get_conjugacy_class(cycle) == conjugacy @pytest.mark.parametrize( - "degree, cycle", + "cycle", [ - ("a", Permutation(0, 1, 2)), - (0.1, Permutation(2)(0, 1)), - ((1,), Permutation(0, 1, 2, 3)), - ((5,), Permutation(0, 1, 2)), + ((1, 2, 3)), + ("a"), + (2.0), ], ) -def test_get_conjugacy_class_degree_type_error(degree, cycle): - "Test the degree parameter TypeError" - with pytest.raises(TypeError, match=".*degree must be of type int.*"): - ap.get_conjugacy_class(cycle, degree) - - -@pytest.mark.parametrize("degree", range(-3, 1)) -def test_get_conjugacy_class_degree_value_error(degree): - "Test the degree parameter ValueError" - with pytest.raises( - ValueError, - match=".*The degree you have provided is too low. It must be an integer greater than 0.*", - ): - ap.get_conjugacy_class(Permutation(0, 1, 2), degree) - - -@pytest.mark.parametrize( - "degree, cycle", - [ - (3, (1, 2, 3)), - (4, "a"), - (7, 2.0), - ], -) -def test_get_conjugacy_class_cycle_type_error(degree, cycle): +def test_get_conjugacy_class_cycle_type_error(cycle): "Test the cycle parameter TypeError" with pytest.raises( TypeError, - match=".*Permutation must be of type sympy.combinatorics.permutations.Permutation.*", + match=".*Permutation must be of type sympy.combinatorics.Permutation.*", ): - ap.get_conjugacy_class(cycle, degree) - - -@pytest.mark.parametrize( - "degree, cycle", - [ - (3, Permutation(0, 1, 2, 3)), - (3, Permutation(2, 3)(0, 1)), - (4, Permutation(0, 1, 2, 3, 5)), - (4, Permutation(4, 1)), - ], -) -def test_get_conjugacy_class_cycle_value_error(degree, cycle): - "Test the cycle parameter ValueError if permutation maximum value is greater than the degree" - with pytest.raises(ValueError, match=".*Incompatible degree and permutation cycle.*"): - ap.get_conjugacy_class(cycle, degree) + ap.get_conjugacy_class(cycle) @pytest.mark.parametrize( diff --git a/haarpy/tests/test_unitary.py b/haarpy/tests/test_unitary.py index fdbf62e..3189d3d 100644 --- a/haarpy/tests/test_unitary.py +++ b/haarpy/tests/test_unitary.py @@ -123,7 +123,7 @@ def test_weingarten_unitary_element(cycle, dimension, num, denum): def test_weingarten_reconciliation_numeric(cycle): "Numeric reconciliation of permutation and conjugacy class input" assert ap.weingarten_unitary(cycle, 9) == ap.weingarten_unitary( - ap.get_conjugacy_class(cycle, cycle.size), 9 + ap.get_conjugacy_class(cycle), 9 ) @@ -147,7 +147,7 @@ def test_weingarten_reconciliation_symbolic(cycle): "Symbolic reconciliation of permutation and conjugacy class input" d = Symbol("d") assert ap.weingarten_unitary(cycle, d) == ap.weingarten_unitary( - ap.get_conjugacy_class(cycle, cycle.size), d + ap.get_conjugacy_class(cycle), d ) @@ -216,7 +216,7 @@ def test_gram_orthogonality_elements(n): def test_gram_orthogonality_classes(n): "Test the orthogonality relation between Weingarten matrix and Graham matrix" d = Symbol("d") - weight = lambda g: d ** (g.cycles) * ap.weingarten_unitary(ap.get_conjugacy_class(g, n), d) + weight = lambda g: d ** (g.cycles) * ap.weingarten_unitary(ap.get_conjugacy_class(g), d) orthogonality = sum(len(c) * weight(c.pop()) for c in SymmetricGroup(n).conjugacy_classes()) assert simplify(orthogonality) == 1 diff --git a/haarpy/unitary.py b/haarpy/unitary.py index a7ac36b..0b9c043 100644 --- a/haarpy/unitary.py +++ b/haarpy/unitary.py @@ -128,7 +128,7 @@ def weingarten_unitary(cycle: Union[Permutation, tuple[int]], unitary_dimension: if isinstance(cycle, Permutation): degree = cycle.size - conjugacy_class = get_conjugacy_class(cycle, degree) + conjugacy_class = get_conjugacy_class(cycle) elif isinstance(cycle, (tuple, list)) and all(isinstance(value, int) for value in cycle): degree = sum(cycle) conjugacy_class = tuple(cycle) @@ -136,7 +136,8 @@ def weingarten_unitary(cycle: Union[Permutation, tuple[int]], unitary_dimension: raise TypeError partition_tuple = tuple( - sum((value * (key,) for key, value in part.items()), ()) for part in partitions(degree) + tuple(summand for summand, mult in partition.items() for _ in range(mult)) + for partition in partitions(degree) ) irrep_dimension_tuple = (irrep_dimension(part) for part in partition_tuple) @@ -209,7 +210,7 @@ def haar_integral_unitary(sequences: tuple[tuple[int]], unitary_dimension: Symbo degree = len(seq_i) class_mapping = Counter( - get_conjugacy_class(cycle_i * ~cycle_j, degree) + get_conjugacy_class(cycle_i * ~cycle_j) for cycle_i, cycle_j in product( stabilizer_coset(seq_i, seq_i_prime), stabilizer_coset(seq_j, seq_j_prime),