From 96dbe1544ef682aadf91024b099f70980b099b77 Mon Sep 17 00:00:00 2001 From: Nicolas Quesada Date: Sat, 6 Dec 2025 22:50:13 -0500 Subject: [PATCH 1/3] cleaning test_symplectic --- haarpy/tests/test_symplectic.py | 64 +++++++-------------------------- 1 file changed, 12 insertions(+), 52 deletions(-) diff --git a/haarpy/tests/test_symplectic.py b/haarpy/tests/test_symplectic.py index 95b7224..f997478 100644 --- a/haarpy/tests/test_symplectic.py +++ b/haarpy/tests/test_symplectic.py @@ -27,15 +27,14 @@ d = Symbol('d') monte_carlo_symplectic_dict = { - ((0, 0, 0, 0), (0, 0, 0, 0), 2) : (0.09948889355794269+6.851639354662377e-21j), - ((0, 1, 0, 1), (0, 0, 0, 0), 2) : (0.05025720143792921+3.8747831289542884e-21j), - ((0, 1, 0, 1), (0, 1, 1, 0), 2) : (-0.02505580071352494-9.625388767948332e-07j), - ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3) : (0.018000029928256993+2.5207244033791384e-21j), - ((0, 1, 2, 0, 1, 2), (0, 0, 0, 0, 0, 0), 3) : (0.0029514164273132704-1.8345674171674893e-21j), - ((0, 0, 1, 0, 0, 1), (0, 2, 2, 0, 2, 2), 3) : (0.0037630369365813177+9.729122199612046e-23j), - ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0), 4) : (0.0029909334194272684-2.5931001156985104e-21j), - ((0, 1, 2, 3, 0, 1, 2, 3), (0, 0, 0, 0, 0, 0, 0, 0), 4) : (0.0001260733931029849-4.471619720427258e-23j), - ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4) : (0.000502809515889242-4.1519024921401192e-22j), + ((0, 0, 0, 0), (0, 0, 0, 0), 2) : 1/10, + ((0, 1, 0, 1), (0, 0, 0, 0), 2) : 1/20, + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3) : 1/56, + ((0, 1, 2, 0, 1, 2), (0, 0, 0, 0, 0, 0), 3) : 1/336, + ((0, 0, 1, 0, 0, 1), (0, 2, 2, 0, 2, 2), 3) : 5/1344, + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0), 4) : 1/330, + ((0, 1, 2, 3, 0, 1, 2, 3), (0, 0, 0, 0, 0, 0, 0, 0), 4) : 1/7920, + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4) : 1/1980, } @@ -276,58 +275,19 @@ def test_weingarten_symplectic_degree_value_error(permutation): ((0,0,0,0,0,0,0,0),(0,0,1,1,0,0,1,1), 4), ] ) -def test_haar_integral_symplectic_monte_carlo_symbolic(seq_i, seq_j, half_dimension): +def test_haar_integral_symplectic_symbolic(seq_i, seq_j, half_dimension): "Test haar integral symplectic moments against Monte Carlo simulation symbolic" - epsilon_real = 2e-2 - epsilon_imag = 1e-6 half_length = len(seq_i) // 2 seq_i_symbolic = seq_i[:half_length] + tuple(i+d for i in seq_i[half_length:]) seq_j_symbolic = seq_j[:half_length] + tuple(j+d for j in seq_j[half_length:]) - integral = ap.haar_integral_symplectic((seq_i_symbolic, seq_j_symbolic), d) - integral = float(integral.subs(d, half_dimension)) + integral_frac = ap.haar_integral_symplectic((seq_i_symbolic, seq_j_symbolic), d) + integral_num = integral_frac.subs(d, half_dimension) mc_integral = monte_carlo_symplectic_dict[(seq_i, seq_j, half_dimension)] - assert ( - abs((integral - mc_integral.real) / integral) < epsilon_real - and abs(mc_integral.imag) < epsilon_imag - and integral != 0 - ) - - -@pytest.mark.parametrize( - 'seq_i, seq_j, half_dimension', - [ - ((0,0,0,0),(0,0,0,0), 2), - ((0,1,0,1),(0,0,0,0), 2), - ((0,0,0,0,0,0),(0,0,0,0,0,0), 3), - ((0,1,2,0,1,2),(0,0,0,0,0,0), 3), - ((0,0,1,0,0,1),(0,2,2,0,2,2), 3), - ((0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0), 4), - ((0,1,2,3,0,1,2,3),(0,0,0,0,0,0,0,0), 4), - ((0,0,0,0,0,0,0,0),(0,0,1,1,0,0,1,1), 4), - ] -) -def test_haar_integral_symplectic_monte_carlo_numeric(seq_i, seq_j, half_dimension): - "Test haar integral symplectic moments against Monte Carlo simulation symbolic" - epsilon_real = 2e-2 - epsilon_imag = 1e-6 - - half_length = len(seq_i) // 2 - seq_i_numeric = seq_i[:half_length] + tuple(i+half_dimension for i in seq_i[half_length:]) - seq_j_numeric = seq_j[:half_length] + tuple(j+half_dimension for j in seq_j[half_length:]) - - integral = float(ap.haar_integral_symplectic((seq_i_numeric, seq_j_numeric), half_dimension)) - - mc_integral = monte_carlo_symplectic_dict[(seq_i, seq_j, half_dimension)] - - assert ( - abs((integral - mc_integral.real) / integral) < epsilon_real - and abs(mc_integral.imag) < epsilon_imag - and integral != 0 - ) + assert mc_integral == float(integral_num) @pytest.mark.parametrize( From 73fd23fef58474f1aa1bb0148ba0fee0503bb8ae Mon Sep 17 00:00:00 2001 From: Nicolas Quesada Date: Sat, 6 Dec 2025 23:18:13 -0500 Subject: [PATCH 2/3] minor typo --- haarpy/symplectic.py | 2 +- haarpy/tests/test_circular_ensembles.py | 323 +++++++++----------- haarpy/tests/test_symplectic.py | 390 +++++++++++++----------- 3 files changed, 359 insertions(+), 356 deletions(-) diff --git a/haarpy/symplectic.py b/haarpy/symplectic.py index 5bd9ba5..33c1590 100644 --- a/haarpy/symplectic.py +++ b/haarpy/symplectic.py @@ -168,7 +168,7 @@ def haar_integral_symplectic( Expr: Integral under the Haar measure Raise: - ValueError: If sequences doesn't contain 2 tuples + ValueError: If sequences don't 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 diff --git a/haarpy/tests/test_circular_ensembles.py b/haarpy/tests/test_circular_ensembles.py index dafa591..9a9751b 100644 --- a/haarpy/tests/test_circular_ensembles.py +++ b/haarpy/tests/test_circular_ensembles.py @@ -23,187 +23,184 @@ import pytest import haarpy as ap -d = Symbol('d') - -monte_carlo_cse_dict = { - ((0, 0), (0, 0), 1) : (1+0j), - ((0, 1), (0, 1), 1) : (6.202449849650142e-34+0j), - ((0, 1, 2, 3), (0, 1, 2, 3), 2) : (0.16670734637771975+0j), - ((0, 0, 0, 0), (0, 0, 0, 0), 2) : (0.16594246254246486+0j), - ((0, 3, 0, 3), (0, 3, 0, 3), 2) : (0.16705053183343524+0j), - ((0, 0, 0, 3), (0, 3, 0, 0), 2) : (0.08309679067762976+0j), - ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3) : (0.028659601947427615+0j), - ((5, 5, 5, 5, 5, 5), (5, 5, 5, 5, 5, 5), 3) : (0.028909295959926393+0j), - ((2, 2, 5, 5, 5, 5), (2, 2, 5, 5, 5, 5), 3) : (0.028304583571937526+0j), - ((2, 2, 5, 5, 5, 5), (2, 5, 5, 2, 5, 5), 3) : (-2.6504451440099176e-37-7.700460878474545e-38j), - ((2, 2, 5, 5, 3, 3), (2, 2, 5, 5, 3, 3), 3) : (0.015927428707892998+0j), - ((7, 7, 0, 0, 0, 0, 0, 0), (7, 0, 0, 7, 0, 0, 0, 0), 4) : (-0.00023053596111631466-2.3274900374779226e-06j), +d = Symbol("d") + +# The values in the dictionary below were verified against Montecarlo simulations +cse_dict = { + ((0, 0), (0, 0), 1): 1, + ((0, 1), (0, 1), 1): 0, + ((0, 1, 2, 3), (0, 1, 2, 3), 2): 1 / 6, + ((0, 0, 0, 0), (0, 0, 0, 0), 2): 1 / 6, + ((0, 3, 0, 3), (0, 3, 0, 3), 2): 1 / 6, + ((0, 0, 0, 3), (0, 3, 0, 0), 2): 1 / 12, + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3): 1 / 35, + ((5, 5, 5, 5, 5, 5), (5, 5, 5, 5, 5, 5), 3): 1 / 35, + ((2, 2, 5, 5, 5, 5), (2, 2, 5, 5, 5, 5), 3): 1 / 35, + ((2, 2, 5, 5, 5, 5), (2, 5, 5, 2, 5, 5), 3): 0, + ((2, 2, 5, 5, 3, 3), (2, 2, 5, 5, 3, 3), 3): 1 / 63, + ((7, 7, 0, 0, 0, 0, 0, 0), (7, 0, 0, 7, 0, 0, 0, 0), 4): -1 / 4200, } -@pytest.mark.parametrize("half_degree", range(1,3)) + +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_circular_orthogonal_hyperoctahedral_symbolic(half_degree): """Symbolic validation of COE Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): - assert ap.weingarten_circular_orthogonal(permutation, d) == 1/(d+1) + for permutation in SymmetricGroup(2 * half_degree).generate(): + assert ap.weingarten_circular_orthogonal(permutation, d) == 1 / (d + 1) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = 1/(d*(d+1)*(d+3)) + coefficient = 1 / (d * (d + 1) * (d + 3)) assert ap.weingarten_circular_orthogonal(permutation, d) == ( - simplify((d+2)*coefficient) if permutation in hyperoctahedral - else -coefficient + simplify((d + 2) * coefficient) if permutation in hyperoctahedral else -coefficient ) -@pytest.mark.parametrize("half_degree", range(1,3)) +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_circular_orthogonal_hyperoctahedral_numeric(half_degree): """Symbolic validation of COE Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): - assert ap.weingarten_circular_orthogonal(permutation, 7) == 1/(7+1) + for permutation in SymmetricGroup(2 * half_degree).generate(): + assert ap.weingarten_circular_orthogonal(permutation, 7) == 1 / (7 + 1) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = Fraction(1,(7*(7+1)*(7+3))) + coefficient = Fraction(1, (7 * (7 + 1) * (7 + 3))) assert ap.weingarten_circular_orthogonal(permutation, 7) == ( - simplify((7+2)*coefficient) if permutation in hyperoctahedral - else -coefficient + simplify((7 + 2) * coefficient) if permutation in hyperoctahedral else -coefficient ) -@pytest.mark.parametrize("half_degree", range(1,3)) +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_circular_symplectic_hyperoctahedral_symbolic(half_degree): """Symbolic validation of CSE Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): assert ap.weingarten_circular_symplectic(permutation, d) == ( - permutation.signature()/(2*d-1) + permutation.signature() / (2 * d - 1) ) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = permutation.signature()/(d*(2*d-1)*(2*d-3)) + coefficient = permutation.signature() / (d * (2 * d - 1) * (2 * d - 3)) assert ap.weingarten_circular_symplectic(permutation, d) == ( - simplify((d-1)*coefficient) if permutation in hyperoctahedral - else coefficient/2 + simplify((d - 1) * coefficient) + if permutation in hyperoctahedral + else coefficient / 2 ) -@pytest.mark.parametrize("half_degree", range(1,3)) +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_circular_symplectic_hyperoctahedral_numeric(half_degree): """Symbolic validation of CSE Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): assert ap.weingarten_circular_symplectic(permutation, 7) == Fraction( permutation.signature(), - (2*7-1), + (2 * 7 - 1), ) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = Fraction(permutation.signature(), (7*(2*7-1)*(2*7-3))) + coefficient = Fraction(permutation.signature(), (7 * (2 * 7 - 1) * (2 * 7 - 3))) assert ap.weingarten_circular_symplectic(permutation, 7) == ( - simplify((7-1)*coefficient) if permutation in hyperoctahedral - else coefficient*Fraction(1,2) + simplify((7 - 1) * coefficient) + if permutation in hyperoctahedral + else coefficient * Fraction(1, 2) ) @pytest.mark.parametrize( "sequences", [ - ((0,1,2,3), (0,1)), - ((0,1,2,3,4,5), (0,1,2,3)), - ((0,1,2,3), (0,1,2,2)), - ((0,0,0,0), (0,0,0,1)), - ] + ((0, 1, 2, 3), (0, 1)), + ((0, 1, 2, 3, 4, 5), (0, 1, 2, 3)), + ((0, 1, 2, 3), (0, 1, 2, 2)), + ((0, 0, 0, 0), (0, 0, 0, 1)), + ], ) def test_haar_integral_coe_zero(sequences): "test cases that return 0" assert not ap.haar_integral_circular_orthogonal(sequences, d) -@pytest.mark.parametrize("power", range(2,8,2)) +@pytest.mark.parametrize("power", range(2, 8, 2)) def test_haar_integral_coe_diagonal_entry(power): """Test Thm. 4.2 as seen in `Matsumoto. General moments of matrix elements from circular orthogonal ensembles `_ """ - sequences = (power*(0,), power*(0,)) - assert ( - ap.haar_integral_circular_orthogonal(sequences, d) - == factorial2(power)/prod((d+i) for i in range(1,power,2)) + sequences = (power * (0,), power * (0,)) + assert ap.haar_integral_circular_orthogonal(sequences, d) == factorial2(power) / prod( + (d + i) for i in range(1, power, 2) ) -@pytest.mark.parametrize("power", range(2,8,2)) +@pytest.mark.parametrize("power", range(2, 8, 2)) def test_haar_integral_coe_off_diagonal_entry(power): """Test Thm. 4.3 as seen in `Matsumoto. General moments of matrix elements from circular orthogonal ensembles `_ """ - half_power = int(power/2) - sequences = (half_power*(0,1), half_power*(0,1)) - assert ( - ap.haar_integral_circular_orthogonal(sequences, d) - == factorial(half_power)/((d+(power-1))*prod((d+i) for i in range(half_power-1))) + half_power = int(power / 2) + sequences = (half_power * (0, 1), half_power * (0, 1)) + assert ap.haar_integral_circular_orthogonal(sequences, d) == factorial(half_power) / ( + (d + (power - 1)) * prod((d + i) for i in range(half_power - 1)) ) -@pytest.mark.parametrize("power", range(2,6,2)) +@pytest.mark.parametrize("power", range(2, 6, 2)) def test_haar_integral_coe_diagonal_entry_numeric(power): """Test Thm. 4.2 as seen in `Matsumoto. General moments of matrix elements from circular orthogonal ensembles `_ """ dimension = 17 - sequences = (power*(0,), power*(0,)) - assert ( - ap.haar_integral_circular_orthogonal(sequences, dimension) - == Fraction(factorial2(power),prod((dimension+i) for i in range(1,power,2))) + sequences = (power * (0,), power * (0,)) + assert ap.haar_integral_circular_orthogonal(sequences, dimension) == Fraction( + factorial2(power), prod((dimension + i) for i in range(1, power, 2)) ) -@pytest.mark.parametrize("power", range(2,6,2)) +@pytest.mark.parametrize("power", range(2, 6, 2)) def test_haar_integral_coe_off_diagonal_entry_numeric(power): """Test Thm. 4.3 as seen in `Matsumoto. General moments of matrix elements from circular orthogonal ensembles `_ """ dimension = 17 - half_power = int(power/2) - sequences = (half_power*(0,1), half_power*(0,1)) - assert ( - ap.haar_integral_circular_orthogonal(sequences, dimension) - == Fraction( - factorial(half_power), - ((dimension+(power-1))*prod((dimension+i) for i in range(half_power-1))), - ) + half_power = int(power / 2) + sequences = (half_power * (0, 1), half_power * (0, 1)) + assert ap.haar_integral_circular_orthogonal(sequences, dimension) == Fraction( + factorial(half_power), + ((dimension + (power - 1)) * prod((dimension + i) for i in range(half_power - 1))), ) + @pytest.mark.parametrize( "sequences", [ ((1,),), - ((1,2,3),), - ((1,2),(3,4),(4,5)), + ((1, 2, 3),), + ((1, 2), (3, 4), (4, 5)), "str", - ((1,2),(3,4,5)), - ((1,2,3),(3,4,5,6)), - ((1,2,3),(1,2,3)), - ] + ((1, 2), (3, 4, 5)), + ((1, 2, 3), (3, 4, 5, 6)), + ((1, 2, 3), (1, 2, 3)), + ], ) def test_haar_integral_coe_value_error(sequences): "Test haar integral value error" @@ -212,59 +209,48 @@ def test_haar_integral_coe_value_error(sequences): @pytest.mark.parametrize( - 'seq_i, seq_j, half_dim', + "seq_i, seq_j, half_dim", [ - ((0,0),(0,0), 1), - ((0,1),(0,1), 1), - ((0,1,2,3),(0,1,2,3), 2), - ((0,0,0,0),(0,0,0,0), 2), - ((0,3,0,3),(0,3,0,3), 2), - ((0,0,0,3),(0,3,0,0), 2), - ((0,0,0,0,0,0),(0,0,0,0,0,0), 3), - ((5,5,5,5,5,5),(5,5,5,5,5,5), 3), - ((2,2,5,5,5,5),(2,2,5,5,5,5), 3), - ((2,2,5,5,5,5),(2,5,5,2,5,5), 3), - ((2,2,5,5,3,3),(2,2,5,5,3,3), 3), - ((7,7,0,0,0,0,0,0),(7,0,0,7,0,0,0,0), 4), - ] + ((0, 0), (0, 0), 1), + ((0, 1), (0, 1), 1), + ((0, 1, 2, 3), (0, 1, 2, 3), 2), + ((0, 0, 0, 0), (0, 0, 0, 0), 2), + ((0, 3, 0, 3), (0, 3, 0, 3), 2), + ((0, 0, 0, 3), (0, 3, 0, 0), 2), + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3), + ((5, 5, 5, 5, 5, 5), (5, 5, 5, 5, 5, 5), 3), + ((2, 2, 5, 5, 5, 5), (2, 2, 5, 5, 5, 5), 3), + ((2, 2, 5, 5, 5, 5), (2, 5, 5, 2, 5, 5), 3), + ((2, 2, 5, 5, 3, 3), (2, 2, 5, 5, 3, 3), 3), + ((7, 7, 0, 0, 0, 0, 0, 0), (7, 0, 0, 7, 0, 0, 0, 0), 4), + ], ) def test_haar_integral_circular_symplectic_monte_carlo_numeric(seq_i, seq_j, half_dim): "Test haar integral circular symplectic moments against Monte Carlo simulation numeric" - epsilon_real = 5e-2 - epsilon = 1e-5 - integral = float(ap.haar_integral_circular_symplectic((seq_i, seq_j), half_dim)) + integral = ap.haar_integral_circular_symplectic((seq_i, seq_j), half_dim) - mc_integral = monte_carlo_cse_dict[(seq_i, seq_j, half_dim)] + mc_integral = cse_dict[(seq_i, seq_j, half_dim)] - if integral: - assert ( - abs((integral-mc_integral.real)/integral) < epsilon_real - and abs(mc_integral.imag) < epsilon - ) - else: - assert ( - abs(mc_integral.real) < epsilon - and abs(mc_integral.imag) < epsilon - ) + assert float(integral) == mc_integral @pytest.mark.parametrize( - 'seq_i, seq_j, half_dim', + "seq_i, seq_j, half_dim", [ - ((0,0),(0,0), 1), - ((0,d),(0,d), 1), - ((0,1,d,d+1),(0,1,d,d+1), 2), - ((0,0,0,0),(0,0,0,0), 2), - ((0,1+d,0,1+d),(0,1+d,0,1+d), 2), - ((0,0,0,1+d),(0,1+d,0,0), 2), - ((0,0,0,0,0,0),(0,0,0,0,0,0), 3), - ((d+2,d+2,d+2,d+2,d+2,d+2),(d+2,d+2,d+2,d+2,d+2,d+2), 3), - ((2,2,d+2,d+2,d+2,d+2),(2,2,d+2,d+2,d+2,d+2), 3), - ((2,2,d+2,d+2,d+2,d+2),(2,d+2,d+2,2,d+2,d+2), 3), - ((2,2,d+2,d+2,d,d),(2,2,d+2,d+2,d,d), 3), - ((d+3,d+3,0,0,0,0,0,0),(d+3,0,0,d+3,0,0,0,0), 4), - ] + ((0, 0), (0, 0), 1), + ((0, d), (0, d), 1), + ((0, 1, d, d + 1), (0, 1, d, d + 1), 2), + ((0, 0, 0, 0), (0, 0, 0, 0), 2), + ((0, 1 + d, 0, 1 + d), (0, 1 + d, 0, 1 + d), 2), + ((0, 0, 0, 1 + d), (0, 1 + d, 0, 0), 2), + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3), + ((d + 2, d + 2, d + 2, d + 2, d + 2, d + 2), (d + 2, d + 2, d + 2, d + 2, d + 2, d + 2), 3), + ((2, 2, d + 2, d + 2, d + 2, d + 2), (2, 2, d + 2, d + 2, d + 2, d + 2), 3), + ((2, 2, d + 2, d + 2, d + 2, d + 2), (2, d + 2, d + 2, 2, d + 2, d + 2), 3), + ((2, 2, d + 2, d + 2, d, d), (2, 2, d + 2, d + 2, d, d), 3), + ((d + 3, d + 3, 0, 0, 0, 0, 0, 0), (d + 3, 0, 0, d + 3, 0, 0, 0, 0), 4), + ], ) def test_haar_integral_circular_symplectic_monte_carlo_symbolic(seq_i, seq_j, half_dim): "Test haar integral circular symplectic moments against Monte Carlo simulation symbolic" @@ -277,48 +263,37 @@ def test_haar_integral_circular_symplectic_monte_carlo_symbolic(seq_i, seq_j, ha seq_i = tuple(i if isinstance(i, int) else i.subs(d, half_dim) for i in seq_i) seq_j = tuple(i if isinstance(i, int) else i.subs(d, half_dim) for i in seq_j) - mc_integral = monte_carlo_cse_dict[((seq_i, seq_j, half_dim))] + mc_integral = cse_dict[((seq_i, seq_j, half_dim))] + + assert float(integral) == mc_integral - if integral: - assert ( - abs((integral-mc_integral.real)/integral) < epsilon_real - and abs(mc_integral.imag) < epsilon - ) - else: - assert ( - abs(mc_integral.real) < epsilon - and abs(mc_integral.imag) < epsilon - ) @pytest.mark.parametrize( "sequences", [ - ((1,2,3,4),(1,2,3,4),(1,2,3,4)), - ((1,2,3,4),), - ((1,2,3,4,5), (1,2,3,4,5)), - ((1,2,3,4,5,6), (1,2,3,4,5,6,7)), - ] + ((1, 2, 3, 4), (1, 2, 3, 4), (1, 2, 3, 4)), + ((1, 2, 3, 4),), + ((1, 2, 3, 4, 5), (1, 2, 3, 4, 5)), + ((1, 2, 3, 4, 5, 6), (1, 2, 3, 4, 5, 6, 7)), + ], ) def test_haar_integral_circular_symplectic_value_error_wrong_tuple(sequences): "Value error for wrong sequence format" - with pytest.raises( - ValueError, - match="Wrong sequence format" - ): + with pytest.raises(ValueError, match="Wrong sequence format"): ap.haar_integral_circular_symplectic(sequences, d) @pytest.mark.parametrize( "sequences", [ - (('a','b','c','d'), (1,2,3,4)), - ((1,1,d+1,d+1), (1,1,1,1)), - ] + (("a", "b", "c", "d"), (1, 2, 3, 4)), + ((1, 1, d + 1, d + 1), (1, 1, 1, 1)), + ], ) def test_haar_integral_circular_symplectic_type_error_integer_dimension(sequences): "Type error for integer dimension with not integer sequences" - dimension = randint(1,99) + dimension = randint(1, 99) with pytest.raises(TypeError): ap.haar_integral_circular_symplectic(sequences, dimension) @@ -326,12 +301,14 @@ def test_haar_integral_circular_symplectic_type_error_integer_dimension(sequence @pytest.mark.parametrize( "sequences, dimension", [ - (((1,3),(1,3)), 1), - (((1,2,3,5),(1,2,3,4)), 2), - (((1,2,3,41),(1,2,3,41)), 20), - ] + (((1, 3), (1, 3)), 1), + (((1, 2, 3, 5), (1, 2, 3, 4)), 2), + (((1, 2, 3, 41), (1, 2, 3, 41)), 20), + ], ) -def test_haar_integral_circular_symplectic_value_error_outside_dimension_range(sequences, dimension): +def test_haar_integral_circular_symplectic_value_error_outside_dimension_range( + sequences, dimension +): "Value error for sequences values outside dimension range" with pytest.raises( ValueError, @@ -343,16 +320,16 @@ def test_haar_integral_circular_symplectic_value_error_outside_dimension_range(s @pytest.mark.parametrize( "sequences", [ - ((1,2,3,4),(1,2,3,'a')), - ((1,2,3,4), (1,2,3,{1,2})), - ((1,2,3,4),(1,2,3,4*d)), - ((1,2,3,2*d+1), (1,2,3,4)), - ((1,2,3,d+1), (1,2,3,4.0)), - ((1,2,3,d-1), (1,2,3,4)), - ((1,2,3,4), (1,2,3,d**2)), - ((1,2,3,4), (1,2,3,1+d**2+d)), - ((1,2,3,4), (1,2,3, d + Symbol('s'))), - ] + ((1, 2, 3, 4), (1, 2, 3, "a")), + ((1, 2, 3, 4), (1, 2, 3, {1, 2})), + ((1, 2, 3, 4), (1, 2, 3, 4 * d)), + ((1, 2, 3, 2 * d + 1), (1, 2, 3, 4)), + ((1, 2, 3, d + 1), (1, 2, 3, 4.0)), + ((1, 2, 3, d - 1), (1, 2, 3, 4)), + ((1, 2, 3, 4), (1, 2, 3, d**2)), + ((1, 2, 3, 4), (1, 2, 3, 1 + d**2 + d)), + ((1, 2, 3, 4), (1, 2, 3, d + Symbol("s"))), + ], ) def test_haar_integral_circular_symplectic_type_error_wrong_format(sequences): "Type error for symbolic dimension with wrong sequence format" @@ -363,25 +340,25 @@ def test_haar_integral_circular_symplectic_type_error_wrong_format(sequences): @pytest.mark.parametrize( "dimension", [ - 'a', - [1,2], - {1,2}, + "a", + [1, 2], + {1, 2}, 3.0, - ] + ], ) def test_haar_integral_circular_symplectic_wrong_dimension_format(dimension): "Type error if the symplectic dimension is not an int nor a symbol" with pytest.raises(TypeError): - ap.haar_integral_circular_symplectic(((1,2,3,4),(1,2,3,4)), dimension) + ap.haar_integral_circular_symplectic(((1, 2, 3, 4), (1, 2, 3, 4)), dimension) @pytest.mark.parametrize( "sequences, dimension", [ - (((1,1),(1,1,1,1)), d), - (((1,1,d+1,d+2),(1,1)), d), - (((0,0,0,0), (0,0,0,0,2,2)), 2), - ] + (((1, 1), (1, 1, 1, 1)), d), + (((1, 1, d + 1, d + 2), (1, 1)), d), + (((0, 0, 0, 0), (0, 0, 0, 0, 2, 2)), 2), + ], ) def test_haar_integral_circular_symplectic_zero_cases(sequences, dimension): "Test cases that yield zero" diff --git a/haarpy/tests/test_symplectic.py b/haarpy/tests/test_symplectic.py index f997478..5edf82e 100644 --- a/haarpy/tests/test_symplectic.py +++ b/haarpy/tests/test_symplectic.py @@ -24,32 +24,36 @@ import pytest import haarpy as ap -d = Symbol('d') - -monte_carlo_symplectic_dict = { - ((0, 0, 0, 0), (0, 0, 0, 0), 2) : 1/10, - ((0, 1, 0, 1), (0, 0, 0, 0), 2) : 1/20, - ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3) : 1/56, - ((0, 1, 2, 0, 1, 2), (0, 0, 0, 0, 0, 0), 3) : 1/336, - ((0, 0, 1, 0, 0, 1), (0, 2, 2, 0, 2, 2), 3) : 5/1344, - ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0), 4) : 1/330, - ((0, 1, 2, 3, 0, 1, 2, 3), (0, 0, 0, 0, 0, 0, 0, 0), 4) : 1/7920, - ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4) : 1/1980, +d = Symbol("d") + + +# The values in the dictionary below were verified against Montecarlo simulations +# Using the code in here +# https://github.com/XanaduAI/unicirc/blob/740cd16a2e392bdbcd47466684a58d9341532d42/unicirc/optimization.py#L300 +symplectic_dict = { + ((0, 0, 0, 0), (0, 0, 0, 0), 2): 1 / 10, + ((0, 1, 0, 1), (0, 0, 0, 0), 2): 1 / 20, + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3): 1 / 56, + ((0, 1, 2, 0, 1, 2), (0, 0, 0, 0, 0, 0), 3): 1 / 336, + ((0, 0, 1, 0, 0, 1), (0, 2, 2, 0, 2, 2), 3): 5 / 1344, + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0), 4): 1 / 330, + ((0, 1, 2, 3, 0, 1, 2, 3), (0, 0, 0, 0, 0, 0, 0, 0), 4): 1 / 7920, + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4): 1 / 1980, } @pytest.mark.parametrize( - "partition", - [ - ((1,1)), - ((2,)), - ((1,1,1)), - ((2,1)), - ((3,)), - ((1,1,1,1)), - ((2,2)), - ((3,1,1)), - ] + "partition", + [ + ((1, 1)), + ((2,)), + ((1, 1, 1)), + ((2, 1)), + ((3,)), + ((1, 1, 1, 1)), + ((2, 2)), + ((3, 1, 1)), + ], ) def test_twisted_spherical_image(partition): """Validates that the twisted spherical function is the image of the zonal spherical function @@ -57,9 +61,7 @@ def test_twisted_spherical_image(partition): symmetric spaces: `_ """ half_degree = sum(partition) - conjugate_partition = tuple( - sum(1 for i in partition if i > j) for j in range(partition[0]) - ) + conjugate_partition = tuple(sum(1 for i in partition if i > j) for j in range(partition[0])) for coset_type in partitions(half_degree): coset_type = tuple(key for key, value in coset_type.items() for _ in range(value)) coset_type_permutation = ap.coset_type_representative(coset_type) @@ -75,21 +77,21 @@ def test_twisted_spherical_image(partition): @pytest.mark.parametrize( "permutation, partition1, partition2", [ - (Permutation(3), (1,1), (2,)), - (Permutation(3)(0,1), (1,1), (2,)), - (Permutation(0,1,2,3), (1,1), (2,)), - (Permutation(3)(0,1,2), (1,1), (2,)), - (Permutation(0,1,2,3,4,5), (3,), (2,1)), - (Permutation(5)(0,4), (3,), (1,1,1)), - (Permutation(5)(0,4), (3,), (2,1)), - (Permutation(5)(0,1,2,3,4), (3,), (1,1,1)), - (Permutation(5)(0,1,2,3,4), (1,1,1), (2,1)), - (Permutation(5)(0,1,2,3,4), (3,), (2,1)), - (Permutation(5), (3,), (1,1,1)), - (Permutation(5), (3,), (2,1)), - (Permutation(5), (2,1), (1,1,1)), - (Permutation(7), (2,1,1), (2,2)), - (Permutation(7)(0,1), (2,1,1), (2,2)), + (Permutation(3), (1, 1), (2,)), + (Permutation(3)(0, 1), (1, 1), (2,)), + (Permutation(0, 1, 2, 3), (1, 1), (2,)), + (Permutation(3)(0, 1, 2), (1, 1), (2,)), + (Permutation(0, 1, 2, 3, 4, 5), (3,), (2, 1)), + (Permutation(5)(0, 4), (3,), (1, 1, 1)), + (Permutation(5)(0, 4), (3,), (2, 1)), + (Permutation(5)(0, 1, 2, 3, 4), (3,), (1, 1, 1)), + (Permutation(5)(0, 1, 2, 3, 4), (1, 1, 1), (2, 1)), + (Permutation(5)(0, 1, 2, 3, 4), (3,), (2, 1)), + (Permutation(5), (3,), (1, 1, 1)), + (Permutation(5), (3,), (2, 1)), + (Permutation(5), (2, 1), (1, 1, 1)), + (Permutation(7), (2, 1, 1), (2, 2)), + (Permutation(7)(0, 1), (2, 1, 1), (2, 2)), ], ) def test_twisted_spherical_orthogonality_transversal_zero(permutation, partition1, partition2): @@ -99,11 +101,7 @@ def test_twisted_spherical_orthogonality_transversal_zero(permutation, partition `_ """ degree = permutation.size - convolution = sum( - ap.twisted_spherical_function(tau, partition1) - * ap.twisted_spherical_function(permutation * ~tau, partition2) - for tau in ap.hyperoctahedral_transversal(degree) - ) + convolution = sum(ap.twisted_spherical_function(tau, partition1) * ap.twisted_spherical_function(permutation * ~tau, partition2) for tau in ap.hyperoctahedral_transversal(degree)) assert not convolution @@ -111,48 +109,77 @@ def test_twisted_spherical_orthogonality_transversal_zero(permutation, partition @pytest.mark.parametrize( "permutation, partition", [ - (Permutation(3,), (2,)), - (Permutation(3,), (1,1)), - (Permutation(5,)(0,1), (2,1)), - (Permutation(0,1,2,3,4,5), (3,)), - (Permutation(5,)(0,3,4), (3,)), - (Permutation(0,1,2,3,4,5), (1,1,1)), - (Permutation(0,1,2,3,4,5), (2,1)), - (Permutation(0,3,5), (2,1)), - (Permutation(0,3,4,5), (2,1)), - (Permutation(0,2,3,4,5), (2,1)), + ( + Permutation( + 3, + ), + (2,), + ), + ( + Permutation( + 3, + ), + (1, 1), + ), + ( + Permutation( + 5, + )(0, 1), + (2, 1), + ), + (Permutation(0, 1, 2, 3, 4, 5), (3,)), + ( + Permutation( + 5, + )(0, 3, 4), + (3,), + ), + (Permutation(0, 1, 2, 3, 4, 5), (1, 1, 1)), + (Permutation(0, 1, 2, 3, 4, 5), (2, 1)), + (Permutation(0, 3, 5), (2, 1)), + (Permutation(0, 3, 4, 5), (2, 1)), + (Permutation(0, 2, 3, 4, 5), (2, 1)), ], ) def test_twisted_spherical_orthogonality_transversal_none_zero(permutation, partition): """Orthogonality relation for the twisted spherical function - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ degree = permutation.size half_degree = degree // 2 - convolution = sum( - ap.twisted_spherical_function(tau, partition) - * ap.twisted_spherical_function(permutation * ~tau, partition) - for tau in ap.hyperoctahedral_transversal(degree) - ) + convolution = sum(ap.twisted_spherical_function(tau, partition) * ap.twisted_spherical_function(permutation * ~tau, partition) for tau in ap.hyperoctahedral_transversal(degree)) duplicate_partition = tuple(part for part in partition for _ in range(2)) - orthogonality = ( - Fraction(factorial(degree), 2**half_degree*factorial(half_degree)) - * ap.twisted_spherical_function(permutation, partition) - / ap.irrep_dimension(duplicate_partition) - ) + orthogonality = Fraction(factorial(degree), 2**half_degree * factorial(half_degree)) * ap.twisted_spherical_function(permutation, partition) / ap.irrep_dimension(duplicate_partition) assert convolution == orthogonality @pytest.mark.parametrize( "permutation, partition", [ - (Permutation(3,), [2,]), - ((3,1), (1,1)), - (Permutation(5,)(0,1), 'a'), - ('a', (3,)), - (Permutation(5,)(0,3,4), 7), - (7, (1,1,1)), + ( + Permutation( + 3, + ), + [ + 2, + ], + ), + ((3, 1), (1, 1)), + ( + Permutation( + 5, + )(0, 1), + "a", + ), + ("a", (3,)), + ( + Permutation( + 5, + )(0, 3, 4), + 7, + ), + (7, (1, 1, 1)), ], ) def test_twisted_spherical_function_type_error(permutation, partition): @@ -164,10 +191,30 @@ def test_twisted_spherical_function_type_error(permutation, partition): @pytest.mark.parametrize( "permutation, partition", [ - (Permutation(3,), (2,2)), - (Permutation(3,), (1,1,1)), - (Permutation(4,), (2,1)), - (Permutation(4,), (1,1,1)), + ( + Permutation( + 3, + ), + (2, 2), + ), + ( + Permutation( + 3, + ), + (1, 1, 1), + ), + ( + Permutation( + 4, + ), + (2, 1), + ), + ( + Permutation( + 4, + ), + (1, 1, 1), + ), ], ) def test_twisted_spherical_function_degree_value_error(permutation, partition): @@ -176,46 +223,36 @@ def test_twisted_spherical_function_degree_value_error(permutation, partition): ap.twisted_spherical_function(permutation, partition) -@pytest.mark.parametrize("half_degree", range(1,3)) +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_symplectic_hyperoctahedral_symbolic(half_degree): """Symbolic validation of symplectic Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): - assert ap.weingarten_symplectic(permutation, d) == ( - permutation.signature()/(2*d) - ) + for permutation in SymmetricGroup(2 * half_degree).generate(): + assert ap.weingarten_symplectic(permutation, d) == (permutation.signature() / (2 * d)) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = permutation.signature()/(4*d*(d-1)*(2*d+1)) - assert ap.weingarten_symplectic(permutation, d) == ( - simplify((2*d-1)*coefficient) if permutation in hyperoctahedral - else coefficient - ) + coefficient = permutation.signature() / (4 * d * (d - 1) * (2 * d + 1)) + assert ap.weingarten_symplectic(permutation, d) == (simplify((2 * d - 1) * coefficient) if permutation in hyperoctahedral else coefficient) -@pytest.mark.parametrize("half_degree", range(1,3)) +@pytest.mark.parametrize("half_degree", range(1, 3)) def test_weingarten_symplectic_hyperoctahedral_numeric(half_degree): """Symbolic validation of symplectic Weingarten function against results shown in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ if half_degree == 1: - for permutation in SymmetricGroup(2*half_degree).generate(): - assert ap.weingarten_symplectic(permutation, 7) == ( - Fraction(permutation.signature(),(2*7)) - ) + for permutation in SymmetricGroup(2 * half_degree).generate(): + assert ap.weingarten_symplectic(permutation, 7) == (Fraction(permutation.signature(), (2 * 7))) else: - for permutation in SymmetricGroup(2*half_degree).generate(): + for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) - coefficient = Fraction(permutation.signature(),(4*7*(7-1)*(2*7+1))) - assert ap.weingarten_symplectic(permutation, 7) == ( - (2*7-1)*coefficient if permutation in hyperoctahedral - else coefficient - ) + coefficient = Fraction(permutation.signature(), (4 * 7 * (7 - 1) * (2 * 7 + 1))) + assert ap.weingarten_symplectic(permutation, 7) == ((2 * 7 - 1) * coefficient if permutation in hyperoctahedral else coefficient) @pytest.mark.parametrize( @@ -223,28 +260,24 @@ def test_weingarten_symplectic_hyperoctahedral_numeric(half_degree): [ (Permutation(1)), (Permutation(3)), - (Permutation(0,1,2,3)), + (Permutation(0, 1, 2, 3)), (Permutation(5)), - (Permutation(2,3,4,5)), - (Permutation(0,1,2,3,4,5)), - (Permutation(0,1,2,3,4,5,6,7)), - (Permutation(0,1,2,3,4,7)(5,6)), - (Permutation(0,1,2,3)(4,5,6,7)), - (Permutation(4,5,6,7)), + (Permutation(2, 3, 4, 5)), + (Permutation(0, 1, 2, 3, 4, 5)), + (Permutation(0, 1, 2, 3, 4, 5, 6, 7)), + (Permutation(0, 1, 2, 3, 4, 7)(5, 6)), + (Permutation(0, 1, 2, 3)(4, 5, 6, 7)), + (Permutation(4, 5, 6, 7)), (Permutation(7)), - ] + ], ) def test_weingarten_symplectic_orthogonal_relation(permutation): - """Symbolic validation of the relation between the symplectic and + """Symbolic validation of the relation between the symplectic and orthogonal Weingarten functions as seen in - `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: + `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ - assert ap.weingarten_symplectic(permutation, d) == simplify( - (-1) ** (permutation.size//2) - * permutation.signature() - * ap.weingarten_orthogonal(permutation, -2*d) - ) + assert ap.weingarten_symplectic(permutation, d) == simplify((-1) ** (permutation.size // 2) * permutation.signature() * ap.weingarten_orthogonal(permutation, -2 * d)) @pytest.mark.parametrize( @@ -252,72 +285,68 @@ def test_weingarten_symplectic_orthogonal_relation(permutation): [ (Permutation(2)), (Permutation(4)), - (Permutation(0,1,2,3,4)), + (Permutation(0, 1, 2, 3, 4)), (Permutation(6)), - ] + ], ) def test_weingarten_symplectic_degree_value_error(permutation): "Test value error for odd symmetric group degree" - with pytest.raises(ValueError, match = "The degree of the symmetric group S_2k should be even"): + with pytest.raises(ValueError, match="The degree of the symmetric group S_2k should be even"): ap.weingarten_symplectic(permutation, d) @pytest.mark.parametrize( - 'seq_i, seq_j, half_dimension', + "seq_i, seq_j, half_dimension", [ - ((0,0,0,0),(0,0,0,0), 2), - ((0,1,0,1),(0,0,0,0), 2), - ((0,0,0,0,0,0),(0,0,0,0,0,0), 3), - ((0,1,2,0,1,2),(0,0,0,0,0,0), 3), - ((0,0,1,0,0,1),(0,2,2,0,2,2), 3), - ((0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0), 4), - ((0,1,2,3,0,1,2,3),(0,0,0,0,0,0,0,0), 4), - ((0,0,0,0,0,0,0,0),(0,0,1,1,0,0,1,1), 4), - ] + ((0, 0, 0, 0), (0, 0, 0, 0), 2), + ((0, 1, 0, 1), (0, 0, 0, 0), 2), + ((0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0), 3), + ((0, 1, 2, 0, 1, 2), (0, 0, 0, 0, 0, 0), 3), + ((0, 0, 1, 0, 0, 1), (0, 2, 2, 0, 2, 2), 3), + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0), 4), + ((0, 1, 2, 3, 0, 1, 2, 3), (0, 0, 0, 0, 0, 0, 0, 0), 4), + ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4), + ], ) def test_haar_integral_symplectic_symbolic(seq_i, seq_j, half_dimension): "Test haar integral symplectic moments against Monte Carlo simulation symbolic" half_length = len(seq_i) // 2 - seq_i_symbolic = seq_i[:half_length] + tuple(i+d for i in seq_i[half_length:]) - seq_j_symbolic = seq_j[:half_length] + tuple(j+d for j in seq_j[half_length:]) + seq_i_symbolic = seq_i[:half_length] + tuple(i + d for i in seq_i[half_length:]) + seq_j_symbolic = seq_j[:half_length] + tuple(j + d for j in seq_j[half_length:]) integral_frac = ap.haar_integral_symplectic((seq_i_symbolic, seq_j_symbolic), d) integral_num = integral_frac.subs(d, half_dimension) - mc_integral = monte_carlo_symplectic_dict[(seq_i, seq_j, half_dimension)] + mc_integral = symplectic_dict[(seq_i, seq_j, half_dimension)] assert mc_integral == float(integral_num) - @pytest.mark.parametrize( "sequences", [ - ((1,2,3,4),(1,2,3,4),(1,2,3,4)), - ((1,2,3,4),), - ((1,2,3,4), (1,2,3,4,5,6)), - ((1,2,3,4,5,6), (1,2,3,4,5,6,7)), - ] + ((1, 2, 3, 4), (1, 2, 3, 4), (1, 2, 3, 4)), + ((1, 2, 3, 4),), + ((1, 2, 3, 4), (1, 2, 3, 4, 5, 6)), + ((1, 2, 3, 4, 5, 6), (1, 2, 3, 4, 5, 6, 7)), + ], ) def test_haar_integral_symplectic_value_error_wrong_tuple(sequences): "Value error for wrong sequence format" - with pytest.raises( - ValueError, - match="Wrong sequence format" - ): + with pytest.raises(ValueError, match="Wrong sequence format"): ap.haar_integral_symplectic(sequences, d) @pytest.mark.parametrize( "sequences", [ - (('a','b','c','d'), (1,2,3,4)), - ((1,1,d+1,d+1), (1,1,1,1)), - ] + (("a", "b", "c", "d"), (1, 2, 3, 4)), + ((1, 1, d + 1, d + 1), (1, 1, 1, 1)), + ], ) def test_haar_integral_symplectic_type_error_integer_dimension(sequences): "Type error for integer dimension with not integer sequences" - dimension = randint(1,99) + dimension = randint(1, 99) with pytest.raises(TypeError): ap.haar_integral_symplectic(sequences, dimension) @@ -325,10 +354,10 @@ def test_haar_integral_symplectic_type_error_integer_dimension(sequences): @pytest.mark.parametrize( "sequences, dimension", [ - (((1,3),(1,3)), 1), - (((1,2,3,5),(1,2,3,4)), 2), - (((1,2,3,41),(1,2,3,41)), 20), - ] + (((1, 3), (1, 3)), 1), + (((1, 2, 3, 5), (1, 2, 3, 4)), 2), + (((1, 2, 3, 41), (1, 2, 3, 41)), 20), + ], ) def test_haar_integral_symplectic_value_error_outside_dimension_range(sequences, dimension): "Value error for sequences values outside dimension range" @@ -342,15 +371,15 @@ def test_haar_integral_symplectic_value_error_outside_dimension_range(sequences, @pytest.mark.parametrize( "sequences", [ - ((1,2,3,4),(1,2,3,'a')), - ((1,2,3,4), (1,2,3,{1,2})), - ((1,2,3,4),(1,2,3,4*d)), - ((1,2,3,2*d+1), (1,2,3,4)), - ((1,2,3,d+1), (1,2,3,4.0)), - ((1,2,3,4), (1,2,3,d**2)), - ((1,2,3,4), (1,2,3,1+d**2+d)), - ((1,2,3,4), (1,2,3, d + Symbol('s'))), - ] + ((1, 2, 3, 4), (1, 2, 3, "a")), + ((1, 2, 3, 4), (1, 2, 3, {1, 2})), + ((1, 2, 3, 4), (1, 2, 3, 4 * d)), + ((1, 2, 3, 2 * d + 1), (1, 2, 3, 4)), + ((1, 2, 3, d + 1), (1, 2, 3, 4.0)), + ((1, 2, 3, 4), (1, 2, 3, d**2)), + ((1, 2, 3, 4), (1, 2, 3, 1 + d**2 + d)), + ((1, 2, 3, 4), (1, 2, 3, d + Symbol("s"))), + ], ) def test_haar_integral_symplectic_type_error_wrong_format(sequences): "Type error for symbolic dimension with wrong sequence format" @@ -361,49 +390,46 @@ def test_haar_integral_symplectic_type_error_wrong_format(sequences): @pytest.mark.parametrize( "dimension", [ - 'a', - [1,2], - {1,2}, + "a", + [1, 2], + {1, 2}, 3.0, - ] + ], ) def test_haar_integral_symplectic_wrong_dimension_format(dimension): "Type error if the symplectic dimension is not an int nor a symbol" with pytest.raises(TypeError): - ap.haar_integral_symplectic(((1,2,3,4),(1,2,3,4)), dimension) + ap.haar_integral_symplectic(((1, 2, 3, 4), (1, 2, 3, 4)), dimension) @pytest.mark.parametrize( "sequences, dimension", [ - (((1,1,1),(1,1,1)), d), - (((1,1,1,1,1),(1,1,1,1,1)), d), - (((1,1+d,1+d),(1,1+d,1+d)), d), - (((1,1,1,1),(1,1,1,1)), d), - (((1,1,d+1,d+2),(1,1,d+1,d+1)), d), - (((1,0,0,d),(1,d+1,0,d)), d), - (((0,0,0), (0,0,0)), 2), - (((0,0,0,0), (0,0,0,0)), 2), - (((1,2,3,3), (1,2,3,3)), 4), - (((1,1,5,5,5), (1,1,5,5,5)), 4), - ] + (((1, 1, 1), (1, 1, 1)), d), + (((1, 1, 1, 1, 1), (1, 1, 1, 1, 1)), d), + (((1, 1 + d, 1 + d), (1, 1 + d, 1 + d)), d), + (((1, 1, 1, 1), (1, 1, 1, 1)), d), + (((1, 1, d + 1, d + 2), (1, 1, d + 1, d + 1)), d), + (((1, 0, 0, d), (1, d + 1, 0, d)), d), + (((0, 0, 0), (0, 0, 0)), 2), + (((0, 0, 0, 0), (0, 0, 0, 0)), 2), + (((1, 2, 3, 3), (1, 2, 3, 3)), 4), + (((1, 1, 5, 5, 5), (1, 1, 5, 5, 5)), 4), + ], ) def test_haar_integral_symplectic_zero_cases(sequences, dimension): "Test cases that yield zero" assert not ap.haar_integral_symplectic(sequences, dimension) -@pytest.mark.parametrize("half_degree", range(1,5)) +@pytest.mark.parametrize("half_degree", range(1, 5)) def test_haar_integral_symplectic_weingarten_reconciliation(half_degree): "Test single permutation moments match the symplectic weingarten function" - seq_dim_base = tuple(i+d for i in range(half_degree)) - sequence = tuple(i+1 for pair in zip(range(half_degree), seq_dim_base) for i in pair) + seq_dim_base = tuple(i + d for i in range(half_degree)) + sequence = tuple(i + 1 for pair in zip(range(half_degree), seq_dim_base) for i in pair) - for perm in ap.hyperoctahedral_transversal(2*half_degree): + for perm in ap.hyperoctahedral_transversal(2 * half_degree): inv_perm = ~perm perm_sequence = tuple(inv_perm(sequence)) - assert ( - ap.haar_integral_symplectic((sequence, perm_sequence), d) - == ap.weingarten_symplectic(perm, d) - ) + assert ap.haar_integral_symplectic((sequence, perm_sequence), d) == ap.weingarten_symplectic(perm, d) From f59c3ede8d69687fbf1ad1ce95030682a5f203ff Mon Sep 17 00:00:00 2001 From: yaniccd Date: Mon, 8 Dec 2025 15:02:53 +0900 Subject: [PATCH 3/3] symplectic tests merged from PR #44 --- haarpy/tests/test_circular_ensembles.py | 9 +- haarpy/tests/test_symplectic.py | 164 ++++++++++-------------- 2 files changed, 72 insertions(+), 101 deletions(-) diff --git a/haarpy/tests/test_circular_ensembles.py b/haarpy/tests/test_circular_ensembles.py index 9a9751b..5f557d5 100644 --- a/haarpy/tests/test_circular_ensembles.py +++ b/haarpy/tests/test_circular_ensembles.py @@ -25,7 +25,7 @@ d = Symbol("d") -# The values in the dictionary below were verified against Montecarlo simulations +# The values in the dictionary below were verified against Monte Carlo simulations cse_dict = { ((0, 0), (0, 0), 1): 1, ((0, 1), (0, 1), 1): 0, @@ -227,7 +227,6 @@ def test_haar_integral_coe_value_error(sequences): ) def test_haar_integral_circular_symplectic_monte_carlo_numeric(seq_i, seq_j, half_dim): "Test haar integral circular symplectic moments against Monte Carlo simulation numeric" - integral = ap.haar_integral_circular_symplectic((seq_i, seq_j), half_dim) mc_integral = cse_dict[(seq_i, seq_j, half_dim)] @@ -254,11 +253,7 @@ def test_haar_integral_circular_symplectic_monte_carlo_numeric(seq_i, seq_j, hal ) def test_haar_integral_circular_symplectic_monte_carlo_symbolic(seq_i, seq_j, half_dim): "Test haar integral circular symplectic moments against Monte Carlo simulation symbolic" - epsilon_real = 5e-2 - epsilon = 1e-5 - - integral = ap.haar_integral_circular_symplectic((seq_i, seq_j), d) - integral = float(integral.subs(d, half_dim)) + integral = ap.haar_integral_circular_symplectic((seq_i, seq_j), d).subs(d, half_dim) seq_i = tuple(i if isinstance(i, int) else i.subs(d, half_dim) for i in seq_i) seq_j = tuple(i if isinstance(i, int) else i.subs(d, half_dim) for i in seq_j) diff --git a/haarpy/tests/test_symplectic.py b/haarpy/tests/test_symplectic.py index 5edf82e..a55fde1 100644 --- a/haarpy/tests/test_symplectic.py +++ b/haarpy/tests/test_symplectic.py @@ -27,7 +27,7 @@ d = Symbol("d") -# The values in the dictionary below were verified against Montecarlo simulations +# The values in the dictionary below were verified against Monte Carlo simulations # Using the code in here # https://github.com/XanaduAI/unicirc/blob/740cd16a2e392bdbcd47466684a58d9341532d42/unicirc/optimization.py#L300 symplectic_dict = { @@ -101,7 +101,11 @@ def test_twisted_spherical_orthogonality_transversal_zero(permutation, partition `_ """ degree = permutation.size - convolution = sum(ap.twisted_spherical_function(tau, partition1) * ap.twisted_spherical_function(permutation * ~tau, partition2) for tau in ap.hyperoctahedral_transversal(degree)) + convolution = sum( + ap.twisted_spherical_function(tau, partition1) + * ap.twisted_spherical_function(permutation * ~tau, partition2) + for tau in ap.hyperoctahedral_transversal(degree) + ) assert not convolution @@ -109,36 +113,16 @@ def test_twisted_spherical_orthogonality_transversal_zero(permutation, partition @pytest.mark.parametrize( "permutation, partition", [ - ( - Permutation( - 3, - ), - (2,), - ), - ( - Permutation( - 3, - ), - (1, 1), - ), - ( - Permutation( - 5, - )(0, 1), - (2, 1), - ), - (Permutation(0, 1, 2, 3, 4, 5), (3,)), - ( - Permutation( - 5, - )(0, 3, 4), - (3,), - ), - (Permutation(0, 1, 2, 3, 4, 5), (1, 1, 1)), - (Permutation(0, 1, 2, 3, 4, 5), (2, 1)), - (Permutation(0, 3, 5), (2, 1)), - (Permutation(0, 3, 4, 5), (2, 1)), - (Permutation(0, 2, 3, 4, 5), (2, 1)), + (Permutation(3,), (2,)), + (Permutation(3,), (1,1)), + (Permutation(5,)(0,1), (2,1)), + (Permutation(0,1,2,3,4,5), (3,)), + (Permutation(5,)(0,3,4), (3,)), + (Permutation(0,1,2,3,4,5), (1,1,1)), + (Permutation(0,1,2,3,4,5), (2,1)), + (Permutation(0,3,5), (2,1)), + (Permutation(0,3,4,5), (2,1)), + (Permutation(0,2,3,4,5), (2,1)), ], ) def test_twisted_spherical_orthogonality_transversal_none_zero(permutation, partition): @@ -148,38 +132,29 @@ def test_twisted_spherical_orthogonality_transversal_none_zero(permutation, part """ degree = permutation.size half_degree = degree // 2 - convolution = sum(ap.twisted_spherical_function(tau, partition) * ap.twisted_spherical_function(permutation * ~tau, partition) for tau in ap.hyperoctahedral_transversal(degree)) + convolution = sum( + ap.twisted_spherical_function(tau, partition) + * ap.twisted_spherical_function(permutation * ~tau, partition) + for tau in ap.hyperoctahedral_transversal(degree) + ) duplicate_partition = tuple(part for part in partition for _ in range(2)) - orthogonality = Fraction(factorial(degree), 2**half_degree * factorial(half_degree)) * ap.twisted_spherical_function(permutation, partition) / ap.irrep_dimension(duplicate_partition) + orthogonality = ( + Fraction(factorial(degree), 2**half_degree * factorial(half_degree)) + * ap.twisted_spherical_function(permutation, partition) + / ap.irrep_dimension(duplicate_partition) + ) assert convolution == orthogonality @pytest.mark.parametrize( "permutation, partition", [ - ( - Permutation( - 3, - ), - [ - 2, - ], - ), - ((3, 1), (1, 1)), - ( - Permutation( - 5, - )(0, 1), - "a", - ), - ("a", (3,)), - ( - Permutation( - 5, - )(0, 3, 4), - 7, - ), - (7, (1, 1, 1)), + (Permutation(3,), [2,]), + ((3,1), (1,1)), + (Permutation(5,)(0,1), 'a'), + ('a', (3,)), + (Permutation(5,)(0,3,4), 7), + (7, (1,1,1)), ], ) def test_twisted_spherical_function_type_error(permutation, partition): @@ -191,30 +166,10 @@ def test_twisted_spherical_function_type_error(permutation, partition): @pytest.mark.parametrize( "permutation, partition", [ - ( - Permutation( - 3, - ), - (2, 2), - ), - ( - Permutation( - 3, - ), - (1, 1, 1), - ), - ( - Permutation( - 4, - ), - (2, 1), - ), - ( - Permutation( - 4, - ), - (1, 1, 1), - ), + (Permutation(3,), (2,2)), + (Permutation(3,), (1,1,1)), + (Permutation(4,), (2,1)), + (Permutation(4,), (1,1,1)), ], ) def test_twisted_spherical_function_degree_value_error(permutation, partition): @@ -236,7 +191,11 @@ def test_weingarten_symplectic_hyperoctahedral_symbolic(half_degree): for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) coefficient = permutation.signature() / (4 * d * (d - 1) * (2 * d + 1)) - assert ap.weingarten_symplectic(permutation, d) == (simplify((2 * d - 1) * coefficient) if permutation in hyperoctahedral else coefficient) + assert ap.weingarten_symplectic(permutation, d) == ( + simplify((2 * d - 1) * coefficient) + if permutation in hyperoctahedral + else coefficient + ) @pytest.mark.parametrize("half_degree", range(1, 3)) @@ -247,12 +206,16 @@ def test_weingarten_symplectic_hyperoctahedral_numeric(half_degree): """ if half_degree == 1: for permutation in SymmetricGroup(2 * half_degree).generate(): - assert ap.weingarten_symplectic(permutation, 7) == (Fraction(permutation.signature(), (2 * 7))) + assert ap.weingarten_symplectic(permutation, 7) == ( + Fraction(permutation.signature(), (2 * 7)) + ) else: for permutation in SymmetricGroup(2 * half_degree).generate(): hyperoctahedral = ap.HyperoctahedralGroup(half_degree) coefficient = Fraction(permutation.signature(), (4 * 7 * (7 - 1) * (2 * 7 + 1))) - assert ap.weingarten_symplectic(permutation, 7) == ((2 * 7 - 1) * coefficient if permutation in hyperoctahedral else coefficient) + assert ap.weingarten_symplectic(permutation, 7) == ( + (2 * 7 - 1) * coefficient if permutation in hyperoctahedral else coefficient + ) @pytest.mark.parametrize( @@ -277,7 +240,11 @@ def test_weingarten_symplectic_orthogonal_relation(permutation): `Matsumoto. Weingarten calculus for matrix ensembles associated with compact symmetric spaces: `_ """ - assert ap.weingarten_symplectic(permutation, d) == simplify((-1) ** (permutation.size // 2) * permutation.signature() * ap.weingarten_orthogonal(permutation, -2 * d)) + assert ap.weingarten_symplectic(permutation, d) == simplify( + (-1) ** (permutation.size // 2) + * permutation.signature() + * ap.weingarten_orthogonal(permutation, -2 * d) + ) @pytest.mark.parametrize( @@ -308,19 +275,26 @@ def test_weingarten_symplectic_degree_value_error(permutation): ((0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 1, 1, 0, 0, 1, 1), 4), ], ) -def test_haar_integral_symplectic_symbolic(seq_i, seq_j, half_dimension): - "Test haar integral symplectic moments against Monte Carlo simulation symbolic" - +def test_haar_integral_symplectic_symbolic_numeric(seq_i, seq_j, half_dimension): + """Test haar integral symplectic moments against Monte Carlo simulation by calling the + function with both a symbolic and a numeric dimension + """ half_length = len(seq_i) // 2 seq_i_symbolic = seq_i[:half_length] + tuple(i + d for i in seq_i[half_length:]) seq_j_symbolic = seq_j[:half_length] + tuple(j + d for j in seq_j[half_length:]) - - integral_frac = ap.haar_integral_symplectic((seq_i_symbolic, seq_j_symbolic), d) - integral_num = integral_frac.subs(d, half_dimension) - + seq_i_numeric = seq_i[:half_length] + tuple(i + half_dimension for i in seq_i[half_length:]) + seq_j_numeric = seq_j[:half_length] + tuple(j + half_dimension for j in seq_j[half_length:]) + + integral_symb = float( + ap.haar_integral_symplectic((seq_i_symbolic, seq_j_symbolic), d).subs(d, half_dimension) + ) + integral_num = float( + ap.haar_integral_symplectic((seq_i_numeric, seq_j_numeric), half_dimension) + ) mc_integral = symplectic_dict[(seq_i, seq_j, half_dimension)] - assert mc_integral == float(integral_num) + assert mc_integral == integral_symb == integral_num + @pytest.mark.parametrize( "sequences", @@ -432,4 +406,6 @@ def test_haar_integral_symplectic_weingarten_reconciliation(half_degree): inv_perm = ~perm perm_sequence = tuple(inv_perm(sequence)) - assert ap.haar_integral_symplectic((sequence, perm_sequence), d) == ap.weingarten_symplectic(perm, d) + assert ap.haar_integral_symplectic( + (sequence, perm_sequence), d + ) == ap.weingarten_symplectic(perm, d)