1- using PrecompileTools: PrecompileTools, @compile_workload
2- using Preferences: @load_preference , load_preference
1+ module Precompilation
2+
3+ export precompile_indexmanipulations, precompile_contract, precompile_factorizations
4+
5+ using .. TensorKit
6+ using .. TensorKit: TO
7+ using VectorInterface: One, Zero
8+ using TensorOperations: @tensor
9+ using PrecompileTools: @setup_workload , @compile_workload
10+ using Preferences: @load_preference
311
412# Preferences
513# -----------
@@ -18,150 +26,46 @@ const PRECOMPILE_ELTYPES = _validate_precompile_eltypes(
1826 @load_preference (" precompile_eltypes" , [" Float64" , " ComplexF64" ])
1927)
2028
21- # Tensor arities (numbers of legs) to precompile the contraction/permutation machinery for.
22- # The heavy transform/braid machinery is specialized per arity, so this controls how many
23- # leg-counts get the full precompilation benefit (higher arities cost more precompile time).
24- const PRECOMPILE_NDIMS = let n = @load_preference (" precompile_ndims" , [2 , 3 , 4 ])
25- (n isa Vector{Int} && all (≥ (1 ), n)) ||
26- throw (ArgumentError (" `precompile_ndims` should be a vector of positive `Int`s, got `$n `" ))
29+ const PRECOMPILE_NDIMS = let n = @load_preference (" precompile_ndims" , 4 )
30+ (n isa Int && n ≥ 1 ) ||
31+ throw (ArgumentError (" `precompile_ndims` should be a positive `Int`, got `$n `" ))
2732 n
2833end
2934
30- # Representative space for each supported sector-name in the `precompile_sectors` preference.
31- # One representative per (fusion style, coefficient type) suffices — the heavy kernels are
32- # sector-agnostic (see `precompile_contract`). Other sectors can be precompiled by calling
33- # `precompile_contract` directly.
3435function _precompile_space (name:: AbstractString )
35- name == " Trivial" && return ℂ^ 2
36- name == " Z2Irrep" && return Vect[Z2Irrep](0 => 1 , 1 => 1 )
37- name == " U1Irrep" && return Vect[U1Irrep](- 1 => 1 , 0 => 1 , 1 => 1 )
38- name == " SU2Irrep" && return Vect[SU2Irrep](0 => 1 , 1 // 2 => 1 )
39- name == " FermionParity" && return Vect[FermionParity](0 => 1 , 1 => 1 )
40- return error (" unknown precompile_sectors entry `$name `; precompile it directly with `precompile_contract`" )
36+ I = Base. eval (TensorKit, Meta. parse (name))
37+ (I isa DataType && I <: Sector ) ||
38+ error (" invalid `precompile_sectors` entry `$name `; expected a `Sector` type" )
39+ return oneunit (Vect[I])
4140end
4241
4342const PRECOMPILE_SECTORS = let s = @load_preference (
44- " precompile_sectors" , [ " Trivial" , " Z2Irrep" , " SU2Irrep" , " FermionParity" ]
43+ " precompile_sectors" , map (x -> repr (x; context = :module => TensorKit), [ Trivial, Z2Irrep, SU2Irrep, FermionParity])
4544 )
4645 s isa Vector{String} ||
4746 throw (ArgumentError (" `precompile_sectors` should be a vector of strings, got $(typeof (s)) " ))
4847 s
4948end
5049
51- # Whether to run the precompile workload. Respects the ecosystem-wide
52- # `PrecompileTools` switch and a TensorKit-local `precompile_workload` preference
53- # (default `true`). Mirrors the pattern used by TensorOperations.
54- function _precompile_workload_enabled (mod:: Module = @__MODULE__ )
55- return try
56- if load_preference (PrecompileTools, " precompile_workloads" , true )
57- return load_preference (mod, " precompile_workload" , true )
58- else
59- return false
60- end
61- catch
62- false
63- end
64- end
50+ const PRECOMPILE_CONTRACT = @load_preference (" precompile_contract" , true )
51+ const PRECOMPILE_INDEXMANIPULATIONS = @load_preference (" precompile_indexmanipulations" , true )
52+ const PRECOMPILE_FACTORIZATIONS = @load_preference (" precompile_factorizations" , true )
6553
6654# Workload
6755# --------
68- """
69- precompile_contract(V::IndexSpace; eltypes=[Float64, ComplexF64], ndims=[2, 3, 4])
70-
71- Run a small, representative set of tensor operations (contraction, trace, permutation) on
72- tensors built from the space `V`, for each element type in `eltypes` and each tensor arity
73- (number of legs) in `ndims`. This forces compilation of the contraction/permutation machinery
74- for the sector type of `V`.
75-
76- The machinery is specialized per arity, so `ndims` controls which leg-counts get the full
77- benefit; arities not covered still benefit partially from the sector- and arity-agnostic parts
78- (most importantly the per-block BLAS `mul!`).
79-
80- The expensive, numerically-heavy kernels (`add_transform!`, the tree transformers, and the
81- per-block BLAS `mul!`) are *sector-agnostic* — they depend only on the element type, the
82- arity, and `sectorscalartype(sectortype(V))`. Consequently, calling this once for a
83- representative symmetry precompiles code that is reused by every other symmetry with the same
84- fusion style and coefficient type.
85-
86- Downstream packages that define their own symmetry can precompile TensorKit's contraction path
87- for it by calling this inside their own `PrecompileTools.@compile_workload`, e.g.
88-
89- ```julia
90- @compile_workload begin
91- TensorKit.precompile_contract(Vect[MyAnyon](s₀ => 1, s₁ => 1))
92- end
93- ```
94-
95- TensorKit's own workload (enabled by default) calls this for `Trivial`, `Z2Irrep`, `SU2Irrep`
96- and `FermionParity`. It can be tuned or disabled via preferences:
97-
98- ```julia
99- using TensorKit, Preferences
100- set_preferences!(TensorKit, "precompile_eltypes" => ["Float64", "ComplexF64"]; force=true)
101- set_preferences!(TensorKit, "precompile_workload" => false; force=true) # disable entirely
102- ```
103- """
104- function precompile_contract (V:: IndexSpace ; eltypes = PRECOMPILE_ELTYPES, ndims = PRECOMPILE_NDIMS)
105- backend = TO. DefaultBackend ()
106- allocator = TO. DefaultAllocator ()
107- for T in eltypes
108- α, β = rand (T), rand (T)
109-
110- # contraction + permutation for each requested arity (leg count)
111- for N in ndims
112- _precompile_contract_arity (V, T, Val (N), α, β, backend, allocator)
56+ include (" precompile/indexmanipulations.jl" )
57+ include (" precompile/contract.jl" )
58+ include (" precompile/factorizations.jl" )
59+
60+ @setup_workload begin
61+ for name in PRECOMPILE_SECTORS
62+ V = _precompile_space (name)
63+ @compile_workload begin
64+ PRECOMPILE_INDEXMANIPULATIONS && precompile_indexmanipulations (V; eltypes = PRECOMPILE_ELTYPES)
65+ PRECOMPILE_CONTRACT && precompile_contract (V; eltypes = PRECOMPILE_ELTYPES)
66+ PRECOMPILE_FACTORIZATIONS && precompile_factorizations (V; eltypes = PRECOMPILE_ELTYPES)
11367 end
114-
115- # the conjugated-operand branch (`conjA=true`) is a distinct runtime path (adjoint
116- # handling) that `conj(A) * B` networks hit; `@tensor` handles the space bookkeeping
117- A2 = randn (T, V ← V)
118- B2 = randn (T, V ← V)
119- @tensor Cc[a; c] := conj (A2[b; a]) * B2[b; c]
120-
121- # partial trace (the two traced legs are mutually dual)
122- At = randn (T, V ⊗ V' ← V)
123- TO. tensortrace! (
124- TO. tensoralloc_add (T, At, ((3 ,), ()), false , Val (false )),
125- At, ((3 ,), ()), ((1 ,), (2 ,)), false , α, β, backend, allocator
126- )
12768 end
128- return nothing
129- end
130-
131- # `V^{⊗M}` (the unit space for `M == 0`), with `M` a compile-time constant.
132- _repeat_space (V, :: Val{0} ) = one (V)
133- _repeat_space (V, :: Val{M} ) where {M} = foldl (⊗ , ntuple (_ -> V, Val (M)))
134-
135- # Precompile the contraction and permutation machinery for tensors with `N` legs. `N` is a
136- # `Val` so the index tuples are compile-time concrete (the machinery specializes per arity).
137- function _precompile_contract_arity (V, :: Type{T} , :: Val{N} , α, β, backend, allocator) where {T, N}
138- W = _repeat_space (V, Val (N - 1 )) # N-1 legs
139- # contraction of two arity-N tensors over their N-1 shared legs -> arity-2 result
140- A = randn (T, V ← W)
141- B = randn (T, W ← V)
142- pA = ((1 ,), ntuple (i -> i + 1 , Val (N - 1 )))
143- pB = (ntuple (identity, Val (N - 1 )), (N,))
144- _precompile_contract! (A, pA, B, pB, ((1 ,), (2 ,)), α, β, backend, allocator)
145-
146- # a non-trivial permutation exercises the repartition/braid/transform machinery at arity N
147- # (the contraction above takes the no-copy view path for its natural partition)
148- permute (A, (ntuple (i -> N - i + 1 , Val (N)), ()))
149- return nothing
150- end
151-
152- # both scalar-type paths: generic `(α,β)` and the identity `(One(), Zero())` fast path
153- function _precompile_contract! (A, pA, B, pB, pAB, α, β, backend, allocator)
154- T = promote_type (scalartype (A), scalartype (B))
155- C = TO. tensoralloc_contract (T, A, pA, false , B, pB, false , pAB, Val (false ))
156- TO. tensorcontract! (C, A, pA, false , B, pB, false , pAB, α, β, backend, allocator)
157- TO. tensorcontract! (C, A, pA, false , B, pB, false , pAB, One (), Zero (), backend, allocator)
158- return C
15969end
16070
161- if _precompile_workload_enabled ()
162- @compile_workload begin
163- for name in PRECOMPILE_SECTORS
164- precompile_contract (_precompile_space (name); eltypes = PRECOMPILE_ELTYPES)
165- end
166- end
167- end
71+ end # module Precompilation
0 commit comments