Skip to content

Commit 0c53afd

Browse files
authored
Refactoring and perf improvements (#15)
* Fix array expression integration * Update manuscript * fix example * add benchmark code * update manuscript * refactor and improve performance of weingarten calculation * remove bigint * almost working refacctored code * workings tests * fix trace output * bump version * update docs * fix symbolic traces * fix docs * update benchmarks in manuscript * fix permutation group performance * remove shows
1 parent d147406 commit 0c53afd

35 files changed

Lines changed: 1279 additions & 840 deletions

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "IntU"
22
uuid = "70c8b9a1-b4c5-4d7a-a56e-8e7e6495d68b"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
authors = ["Łukasz Pawela", "Zbigniew Puchała"]
55

66
[deps]

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,26 @@ For detailed documentation, please visit [iitis.github.io/IntU.jl](https://iitis
1212

1313
To introduce the main functionality of IntU, consider the problem of averaging
1414
$|U_{i,j}|^2$ over the unitary group, i.e., computing $\int dU |U_{i,j}|^2 =
15-
\int dU U_{i,j} U_{i,j}^*$.
15+
\int dU U_{i,j} U_{k,l}^* dU$.
1616

1717
While numerical approaches (like sampling random matrices) can estimate this,
1818
they are slow and approximate. IntU provides the **exact** analytic result
1919
instantly, even for symbolic dimensions.
2020

21+
**New Feature**: You can now integrate matrix-valued expressions directly!
22+
```julia
23+
using IntU, Symbolics, LinearAlgebra
24+
25+
d = 3
26+
@variables U[1:d, 1:d]::Complex
27+
measure = dU(U, d)
28+
29+
# Integrate the matrix expression U * U'
30+
# This performs element-wise integration automatically
31+
res = integrate(collect(U * U'), measure)
32+
# Output: Identity Matrix (I)
33+
```
34+
2135
```julia
2236
using IntU, Symbolics
2337

@@ -161,7 +175,7 @@ U_sym = SymbolicMatrix(:U, false, :U) # unitary
161175
# Compute ∫ tr(U A U† B) dU
162176
expr = tr(U_sym * A * U_sym' * B)
163177
integrate(expr, dU(d))
164-
# Output: tr(A)*tr(B) / d
178+
# Output: (tr(A)*tr(B)) / d
165179
```
166180

167181
### Stiefel Manifolds
@@ -192,6 +206,38 @@ A = randomITensor(j, i)
192206
res = integrate([U, A], dU(2))
193207
```
194208

209+
## Running Examples and Benchmarks
210+
211+
IntU.jl comes with a comprehensive set of examples and benchmarks.
212+
213+
### Examples
214+
215+
You can run all examples at once using the provided script:
216+
217+
```bash
218+
sh examples/runexamples.sh
219+
```
220+
221+
To run a single example, pass the example number to the script. For instance, to run `01_basics.jl`:
222+
223+
```bash
224+
sh examples/runexamples.sh 01
225+
```
226+
227+
### Benchmarks
228+
229+
To run all benchmarks:
230+
231+
```bash
232+
sh benchmarks/runbenchmarks.sh
233+
```
234+
235+
To run a single benchmark, pass the number to the script:
236+
237+
```bash
238+
sh benchmarks/runbenchmarks.sh 01
239+
```
240+
195241
## Installation
196242

197243
IntU is tested with Julia 1.11 or later. Installation can be done through the

benchmarks/05_symbolic_trace.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ using Symbolics
44

55
function run_benchmarks()
66
@variables d
7-
@variables u_dummy[1:1, 1:1]
8-
measure = dU(u_dummy, d)
97
U = SymbolicMatrix(:U, false, :U)
8+
measure = dU(U, d)
109
Ud = U'
1110

1211
# Create constants

benchmarks/09_stress_test_2.jl

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ doublefactorial_odd(n::Int) = prod(1:2:n) # n must be odd
4141

4242
# Symbolic zero test (best-effort; also backs up by numeric spot-checks)
4343
function sym_is_zero(x)
44-
xs = Symbolics.simplify(x)
45-
return (xs == 0) || (xs === 0)
44+
try
45+
xs = Symbolics.simplify(x)
46+
return (xs == 0) || (xs === 0)
47+
catch
48+
return false
49+
end
4650
end
4751

4852
function safe_string(x)
@@ -55,7 +59,12 @@ end
5559

5660
# Evaluate symbolic expression at d = val (if d is a Symbolics variable)
5761
function eval_at(expr, dvar, val::Int)
58-
return Symbolics.simplify(Symbolics.substitute(expr, Dict(dvar => val)))
62+
subbed = Symbolics.substitute(expr, Dict(dvar => val))
63+
try
64+
return Symbolics.simplify(subbed)
65+
catch
66+
return subbed
67+
end
5968
end
6069

6170
function full_simplify(x)
@@ -64,7 +73,11 @@ function full_simplify(x)
6473
# Canonicalize by expanding then simplifying
6574
return Symbolics.simplify(Symbolics.expand(x))
6675
catch
67-
return Symbolics.simplify(x)
76+
try
77+
return Symbolics.simplify(x)
78+
catch
79+
return x
80+
end
6881
end
6982
end
7083

@@ -148,6 +161,15 @@ U_cases = [
148161
),
149162
]
150163

164+
function safe_eq(x, y)
165+
try
166+
val = (x == y)
167+
return val isa Bool ? val : false
168+
catch
169+
return false
170+
end
171+
end
172+
151173
for (name, expr, expected) in U_cases
152174
got, bm = bench_integrate(expr, μU; samples = samples)
153175
diff0 = sym_is_zero(got - expected)
@@ -157,7 +179,7 @@ for (name, expr, expected) in U_cases
157179
g = eval_at(got, d, dv)
158180
e = eval_at(expected, d, dv)
159181
numeric_checks[string(dv)] =
160-
Dict("got" => safe_string(g), "expected" => safe_string(e), "ok" => (g == e))
182+
Dict("got" => safe_string(g), "expected" => safe_string(e), "ok" => safe_eq(g, e))
161183
end
162184

163185
println("[U(d)] $name")
@@ -234,7 +256,7 @@ for (name, expr, k) in O_cases
234256
numeric_checks[string(dv)] = Dict(
235257
"got" => safe_string(g),
236258
"expected" => safe_string(e),
237-
"ok" => (g == e),
259+
"ok" => safe_eq(g, e),
238260
)
239261
end
240262
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using IntU
2+
using Symbolics
3+
using LinearAlgebra
4+
using BenchmarkTools
5+
6+
function benchmark_matrix_integration(sizes)
7+
println("=== Matrix Integration Benchmark (Haar U * U') ===")
8+
@variables d
9+
10+
for N in sizes
11+
println("\nBenchmarking Matrix Size N=$N")
12+
# Define N x N symbolic matrix
13+
# Note: integration dimension is symbolic 'd', but matrix size is fixed N
14+
@variables U[1:N, 1:N]::Complex
15+
measure = dU(U, d)
16+
17+
# We need to collect to ensure we pass a Matrix{Num} to integrate
18+
expr = collect(U * U')
19+
println(" Expression size: $(size(expr))")
20+
21+
# Warmup
22+
integrate(expr, measure)
23+
24+
# Benchmark
25+
t = @benchmark integrate($expr, $measure)
26+
display(t)
27+
end
28+
end
29+
30+
benchmark_matrix_integration([2, 3, 4])

docs/src/api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,7 @@ IntU.character_at_id
113113
IntU.irrep_dimension
114114
IntU.get_weingarten_orthogonal_data
115115
IntU.compute_symplectic_contraction
116+
IntU.weingarten_orthogonal_val_canonical
117+
IntU.INTEGRATION_RULES
118+
IntU.measure_info
116119
```

docs/src/symbolic_trace.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ expr = IntU.tr(U * A * U' * A)
3737
# 4. Integrate
3838
res = integrate(expr, measure)
3939
println(res)
40-
# Output: (tr(A)^2)/d
40+
# Output: (tr(A)^2) / d
4141
```
4242

4343
## Implementation Details
@@ -62,14 +62,12 @@ Information Theory without getting bogged down in index hell.
6262
# Product of traces
6363
expr = tr(U * A) * tr(U' * B)
6464
integrate(expr, measure)
65-
# Output: tr(A B) / d
65+
# Output: tr(A*B) / d
6666

6767
# Sum of traces
6868
expr_sum = tr(U * A * U') + tr(B)
6969
integrate(expr_sum, measure)
70-
# Output: tr(A)/d * tr(I) + tr(B) = tr(A) + tr(B)
70+
# Output: (tr(A) / d) * tr(I_d) + tr(B) = tr(A) + tr(B)
7171
```
7272

7373
The underlying engine handles the "wiring" of indices across multiple trace cycles automatically.
74-
75-
The underlying engine handles the "wiring" of indices across multiple trace cycles automatically.

docs/src/unitary_integration.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,20 @@ println(res3)
121121
# Output: 1
122122
```
123123

124+
### 4. Matrix Integration
125+
126+
New in v0.2: You can integrate matrix-valued expressions directly. The function `integrate` will element-wise integrate any `AbstractArray` passed to it.
127+
128+
```julia
129+
using LinearAlgebra
130+
131+
# Integrate U * U' (should be identity)
132+
# We collect the symbolic expression to a Matrix{Num} to ensure it's treated as an array of expressions
133+
expr_mat = collect(U * U')
134+
res_mat = integrate(expr_mat, measure)
135+
# Result is the Identity matrix
136+
```
137+
124138
## Potential Pitfalls
125139

126140
- **Symbolic vs Numeric Dimension**: The dimension $d$ can be symbolic.

examples/01_basics.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# examples/01_basics.jl
21
using IntU
32
using Symbolics
3+
using LinearAlgebra
44

55
# 1. Define the dimension and variables
66
d_val = 3
@@ -25,6 +25,16 @@ result2 = integrate(expr2, measure)
2525
println("Result: ", result2)
2626
println("Expected: ", 1//(d_val^2 - 1))
2727

28-
# 4. Example: General d formula (symbolic d is not fully supported yet,
28+
# 5. Example: Matrix Integration
29+
# You can integrate matrix-valued expressions directly.
30+
# We use collect() to ensure we pass a standard Julia Matrix of symbolic numbers.
31+
println("\n5. Example: Matrix Integration")
32+
println("Integrating U * U' (should be Identity)")
33+
expr_mat = collect(U * U')
34+
result_mat = integrate(expr_mat, measure)
35+
println("Result[1,1]: ", result_mat[1, 1])
36+
println("Result is Identity? ", result_mat == I)
37+
38+
# 6. Example: General d formula (symbolic d is not fully supported yet,
2939
# but we can show it matches the theoretical value for a specific d)
3040
println("\nNote: Theoretical value involves Weingarten function Wg(1^2, d) = 1/(d^2-1)")

examples/09_real_integration.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ println("Result (symbolic sum k=1..3): ", res3)
3131
println("Value at d=3: ", Symbolics.substitute(res3, Dict(d => 3)))
3232

3333

34+
println("\n4. Matrix Integration Example")
35+
println("Integrating O * O^T (should be Identity)")
36+
# We need to collect symbolic array to standard Matrix{Num} to ensure element-wise integration
37+
O_mat = collect(O)
38+
res_mat = integrate(O_mat * O_mat', mO)
39+
40+
# Check first element
41+
println("Result[1,1]: ", res_mat[1,1])
42+
println("Expected: 1")
43+
44+
3445
# --- Symplectic Group Sp(d) ---
3546
println("\n--- Symplectic Group Sp(d) ---")
3647
println("(Note: Sp(d) integration requires d to be even)")

0 commit comments

Comments
 (0)