diff --git a/docs/src/methods.md b/docs/src/methods.md index 93464bb..1d9aa69 100644 --- a/docs/src/methods.md +++ b/docs/src/methods.md @@ -35,6 +35,7 @@ QuiverTools.weights_universal_bundle_on_stratum QuiverTools.local_quiver_setting QuiverTools.thin_dimension_vector QuiverTools.solve +QuiverTools._mobius QuiverTools.todd_Q QuiverTools.is_root QuiverTools.total_chern_class_universal diff --git a/docs/src/methods/quiver-moduli.md b/docs/src/methods/quiver-moduli.md index 4ee95d4..946c7c4 100644 --- a/docs/src/methods/quiver-moduli.md +++ b/docs/src/methods/quiver-moduli.md @@ -1,3 +1,9 @@ +```@meta +DocTestSetup = quote + using QuiverTools +end +``` + # Quiver moduli The main purpose of QuiverTools is to treat quiver moduli. @@ -94,3 +100,34 @@ hodge_diamond hodge_polynomial betti_numbers ``` + +## Intersection cohomology + +If some proper subdimension vector has the slope of ``\mathbf{d}`` there are properly +semistable representations and the moduli space is singular, so the methods above no +longer apply. Its intersection cohomology is still computable, by the theorem of +[Meinhardt--Reineke](https://mathscinet.ams.org/mathscinet-getitem?mr=4000572) +identifying the Donaldson--Thomas invariants of the quiver with it. This needs the +stability parameter to be generic for the slope of ``\mathbf{d}``, and stable +representations to exist. + +For instance, the 3-Kronecker quiver with dimension vector ``(2,2)`` has the ``(1,1)``'s +of the same slope, so its moduli space is singular. It turns out to have the intersection +cohomology of ``\mathbb{P}^5``: + +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 2]); + +julia> intersection_poincare_polynomial(M) +L^5 + L^4 + L^3 + L^2 + L + 1 +``` + +The quiver need not be acyclic. With one vertex, ``m`` loops and the trivial stability +parameter one gets the classical spaces of matrix invariants, which are affine, so that +the answer is then the Poincaré polynomial for cohomology with compact support. + +```@docs +intersection_poincare_polynomial +intersection_betti_numbers +intersection_hodge_diamond +``` diff --git a/src/Hodge.jl b/src/Hodge.jl index 8a903ee..027d6b6 100644 --- a/src/Hodge.jl +++ b/src/Hodge.jl @@ -596,3 +596,412 @@ function motive( # this branch agrees with the trivial-stability branch above (see issue #36) return -solve(T, y)[1] end + +######################################################################################## +# Intersection cohomology of quiver moduli spaces. +# +# When `d` and `theta` admit properly semistable representations the moduli space is +# singular and its ordinary cohomology is not determined by the Harder--Narasimhan +# recursion. Meinhardt--Reineke +# [[MR4000572](https://mathscinet.ams.org/mathscinet-getitem?mr=4000572)] identify the +# Donaldson--Thomas invariants of the quiver with the intersection cohomology of the +# moduli space, which makes it computable from the motives of the semistable stacks. +# +# With ``\Lambda_\mu`` the monoid of dimension vectors of the slope of ``d``, their +# Lemma in §3.1 and Theorem 3.4 read +# +# \sum_{e \in \Lambda_\mu} L^{(e, e)/2} [\mathfrak{M}^{ss}_e] t^e +# = Exp((\sum_{0 \neq e \in \Lambda_\mu} DT_e t^e)/(L^{1/2} - L^{-1/2})), +# E(IH^*(M^{ss}_d)) = L^{\dim/2} DT_d, \dim M^{ss}_d = 1 - (d, d), +# +# where ``(-, -)`` is the Euler form and ``Exp`` is the plethystic exponential. +# The motive of the stack ``\mathfrak{M}^{ss}_e`` is what `motive` computes. +# +# Quiver moduli have Hodge structures concentrated on the diagonal, so everything in +# sight is a rational function in the Lefschetz class alone, and the half powers only +# need a square root `w` of it, with ``L^{1/2} = -w`` because ``L^{1/2}`` sits in odd +# degree. The Adams operations are then the substitutions ``w \mapsto w^n``. +# +# For a dimension vector which is primitive in ``\Lambda_\mu`` the plethystic logarithm +# is its own leading term, and the answer is the ordinary Poincaré polynomial again. +######################################################################################## + +""" + _mobius(n::Int) + +Return the Möbius function ``\\mu(n)``, by trial division. + +This is an internal method, only used in the plethystic logarithm computing +intersection cohomology. + +# Examples + +```jldoctest +julia> QuiverTools._mobius.(1:10) +10-element Vector{Int64}: + 1 + -1 + -1 + 0 + -1 + 1 + -1 + 0 + 0 + 1 +``` +""" +function _mobius(n::Int) + sign, remaining = 1, n + for divisor in 2:isqrt(n) + if iszero(remaining % divisor) + remaining ÷= divisor + iszero(remaining % divisor) && return 0 + sign = -sign + end + end + # what is left is 1 or the one prime factor above the square root + return isone(remaining) ? sign : -sign +end + +""" + intersection_poincare_polynomial(M::QuiverModuliSpace) + +Compute the Poincaré polynomial of the intersection cohomology of the moduli space `M`. + +The algorithm is the one of +[[MR4000572](https://mathscinet.ams.org/mathscinet-getitem?mr=4000572)], which identifies +the Donaldson--Thomas invariants of the quiver with the intersection cohomology of +``M^{ss}_\\theta(Q, \\mathbf{d})``. It needs `M.theta` to be generic for the slope of +`M.d`, meaning that the antisymmetrized Euler form vanishes on the dimension vectors of +that slope, and it needs stable representations to exist; both are checked. + +The moduli space is smooth exactly when no proper subdimension vector has the slope of +`M.d`, and there intersection cohomology is ordinary cohomology, so this agrees with +[`poincare_polynomial`](@ref). + +The quiver need not be acyclic, but then the moduli space is affine rather than +projective and, as for [`poincare_polynomial`](@ref), the answer is the Poincaré +polynomial for cohomology with compact support. + +# Input + +- `M::QuiverModuliSpace`: a moduli space of representations of a quiver. + +# Output + +- the Poincaré polynomial of the intersection cohomology of the moduli space. + +# Examples + +The moduli space for the 3-Kronecker quiver and dimension vector `[2, 2]` is singular, +as `[1, 1]` has the same slope, and it has the intersection cohomology of ``\\mathbb{P}^5``: +```jldoctest +julia> Q = kronecker_quiver(3); + +julia> M = QuiverModuliSpace(Q, [2, 2]); + +julia> intersection_poincare_polynomial(M) +L^5 + L^4 + L^3 + L^2 + L + 1 +``` + +The dimension vector `[3, 3]` for the same quiver gives a singular 10-fold: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [3, 3]); + +julia> intersection_poincare_polynomial(M) +L^10 + L^9 + 2*L^8 + 2*L^7 + 2*L^6 + 2*L^5 + 2*L^4 + 2*L^3 + 2*L^2 + L + 1 +``` + +Reflection functors identify moduli spaces for different dimension vectors: +```jldoctest +julia> Q = kronecker_quiver(4); + +julia> M = QuiverModuliSpace(Q, [3, 3]); N = QuiverModuliSpace(Q, [3, 9]); + +julia> intersection_poincare_polynomial(M) == intersection_poincare_polynomial(N) +true +``` + +In the smooth case this is the ordinary Poincaré polynomial: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 3]); + +julia> intersection_poincare_polynomial(M) +L^6 + L^5 + 3*L^4 + 3*L^3 + 3*L^2 + L + 1 + +julia> intersection_poincare_polynomial(M) == poincare_polynomial(M) +true +``` + +The quiver with one vertex and `m` loops and the trivial stability parameter gives the +classical space of matrix invariants, `m`-tuples of operators on a `d`-dimensional vector +space up to simultaneous conjugation. It is affine of dimension ``(m-1)d^2+1``, singular +except for ``d = 1`` or ``m = d = 2``, and its intersection cohomology is worked out in +Theorem 8.2 of [[MR4000572](https://mathscinet.ams.org/mathscinet-getitem?mr=4000572)]: +```jldoctest +julia> intersection_poincare_polynomial(QuiverModuliSpace(loop_quiver(3), [1])) +L^3 + +julia> intersection_poincare_polynomial(QuiverModuliSpace(loop_quiver(2), [3])) +L^10 + +julia> intersection_poincare_polynomial(QuiverModuliSpace(loop_quiver(4), [2])) +L^13 + L^11 +``` + +There is nothing to compute if no representation of dimension vector `M.d` is stable: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(2), [2, 2]); + +julia> intersection_poincare_polynomial(M) +ERROR: ArgumentError: there are no stable representations of dimension vector [2, 2] +``` + +Nor is there anything to compute if the stability parameter is not generic for the slope +of `M.d`, which happens on a fake wall: +```jldoctest +julia> Q = Quiver([0 1 1 0; 0 0 1 0; 0 0 0 1; 0 0 0 0]); + +julia> intersection_poincare_polynomial(QuiverModuliSpace(Q, [3, 3, 4, 1])) +ERROR: ArgumentError: the stability parameter is not generic for the slope of [3, 3, 4, 1], so intersection cohomology is out of reach +``` +""" +function intersection_poincare_polynomial(M::QuiverModuliSpace) + M.condition == "semistable" || throw( + ArgumentError("intersection cohomology is computed for the semistable moduli space") + ) + Q, d, theta, denom = M.Q, M.d, M.theta, M.denom + mu = slope(d, theta, denom) + + # the dimension vectors of the slope of `d`, i.e. the part of the monoid + # ``\Lambda_\mu`` below `d`; the zero vector is left out and treated separately + lattice = filter( + e -> slope(e, theta, denom) == mu, all_subdimension_vectors(d; nonzero=true) + ) + all(euler_form(Q, e, f) == euler_form(Q, f, e) for e in lattice, f in lattice) || throw( + ArgumentError( + "the stability parameter is not generic for the slope of $(Vector(d)), " * + "so intersection cohomology is out of reach", + ), + ) + + R, ws = polynomial_ring(Singular.QQ, ["w"]) + w = ws[1] + F = fraction_field(R) + E = Singular.elem_type(F) + root = F(-w) # ``L^{1/2}``, which sits in odd degree + # substitute `image` for the variable of a univariate rational function + substitute(num, den, image) = F(num(image))//F(den(image)) + adams(x, n) = substitute(numerator(x), denominator(x), w^n) + # the motive lives in ``\mathbb{Q}(L)``; move it to the square root, ``L = w^2`` + to_square_root(m) = substitute( + Singular.n_transExt_to_spoly(numerator(m)), + Singular.n_transExt_to_spoly(denominator(m)), + w^2, + ) + + # the generating series, without its constant term + series = Dict{Vector{Int},E}( + e => power(root, euler_form(Q, e, e)) * to_square_root(motive(Q, e, theta, denom)) + for e in lattice + ) + + # the ordinary logarithm ``\log(1 + x) = \sum_k (-1)^{k-1}/k x^k``; the kth power is + # supported on sums of k nonzero dimension vectors, so the sum stops at ``|d|`` + logarithm = Dict{Vector{Int},E}() + term = series + for k in 1:sum(d) + isempty(term) && break + scale = F((-1)^(k - 1))//k + for (e, value) in term + logarithm[e] = get(logarithm, e, zero(F)) + scale * value + end + k == sum(d) && break + # multiply by `series`, dropping everything that is no longer below `d` + next = Dict{Vector{Int},E}() + for (e, left) in term, (f, right) in series + is_subdimension_vector(e + f, d) || continue + next[e + f] = get(next, e + f, zero(F)) + left * right + end + term = next + end + + # and the plethystic one, ``Log(1 + x) = \sum_n \mu(n)/n \psi^n(\log(1 + x))``; only + # those `n` with `n * e = d` for some `e` contribute, i.e. the divisors of `gcd(d)` + target = Vector{Int}(d) + total = get(logarithm, target, zero(F)) + common = gcd(d) + for k in 2:common + iszero(common % k) || continue + mobius = _mobius(k) + iszero(mobius) && continue + total += F(mobius)//k * adams(get(logarithm, target .÷ k, zero(F)), k) + end + + # ``DT_d = (L^{1/2} - L^{-1/2}) [Log Q]_{t^d}``, then ``E(IH^*) = L^{\dim/2} DT_d`` + result = power(root, 1 - euler_form(Q, d, d)) * (root - inv(root)) * total + iszero(result) && throw( + ArgumentError("there are no stable representations of dimension vector $(Vector(d))") + ) + isone(denominator(result)) || + throw(DomainError("intersection cohomology is not polynomial")) + + # intersection cohomology of these moduli spaces is concentrated in even degree, so the + # answer has to be a polynomial in `w^2`; that is a real check on the whole computation + S, Ls = polynomial_ring(Singular.QQ, ["L"]) + L = Ls[1] + invariant = numerator(result) + P = zero(S) + for (c, e) in + zip(Singular.coefficients(invariant), Singular.exponent_vectors(invariant)) + isodd(e[1]) && throw(DomainError("intersection cohomology in odd degree")) + P += S(c) * L^(e[1] ÷ 2) + end + return P +end + +""" + intersection_betti_numbers(M::QuiverModuliSpace) + +Compute the Betti numbers of the intersection cohomology of the moduli space `M`. + +See [`intersection_poincare_polynomial`](@ref) for the algorithm and its hypotheses. + +# Input + +- `M::QuiverModuliSpace`: a moduli space of representations of a quiver. + +# Output + +- a list of intersection Betti numbers of the moduli space, indexed by cohomological + degree ``0, \\dots, 2\\dim M``. The odd ones vanish. + +# Examples + +The singular moduli space for the 3-Kronecker quiver and dimension vector `[2, 2]` has +the intersection cohomology of ``\\mathbb{P}^5``: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 2]); + +julia> intersection_betti_numbers(M) +11-element Vector{Int64}: + 1 + 0 + 1 + 0 + 1 + 0 + 1 + 0 + 1 + 0 + 1 +``` + +Intersection cohomology of a projective variety still satisfies Poincaré duality, so the +Betti numbers of a singular moduli space are palindromic just as well: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(4), [2, 4]); + +julia> betti = intersection_betti_numbers(M); + +julia> betti == reverse(betti) +true + +julia> betti[1:2:end] +14-element Vector{Int64}: + 1 + 1 + 3 + 4 + 6 + 6 + 7 + 7 + 6 + 6 + 4 + 3 + 1 + 1 +``` + +In the smooth case these are the ordinary Betti numbers: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 3]); + +julia> intersection_betti_numbers(M) == betti_numbers(M) +true +``` +""" +function intersection_betti_numbers(M::QuiverModuliSpace) + P = intersection_poincare_polynomial(M) + # entry 2k + 1 is the coefficient of L^k in P, i.e. the intersection Betti number + # in cohomological degree 2k; all odd ones vanish + betti = zeros(Int, 2 * dimension(M) + 1) + for (c, e) in zip(Singular.coefficients(P), Singular.exponent_vectors(P)) + betti[2 * e[1] + 1] = Int(numerator(c)) + end + return betti +end + +""" + intersection_hodge_diamond(M::QuiverModuliSpace) + +Compute the Hodge diamond of the intersection cohomology of the moduli space `M`. + +See [`intersection_poincare_polynomial`](@ref) for the algorithm and its hypotheses. +The Hodge structure is of Hodge--Tate type, so the diamond is concentrated on the +diagonal. + +# Input + +- `M::QuiverModuliSpace`: a moduli space of representations of a quiver. + +# Output + +- the Hodge diamond of the intersection cohomology of the moduli space. + +# Examples + +The singular moduli space for the 3-Kronecker quiver and dimension vector `[2, 2]` has +the intersection cohomology of ``\\mathbb{P}^5``: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 2]); + +julia> intersection_hodge_diamond(M) +6×6 Matrix{Int64}: + 1 0 0 0 0 0 + 0 1 0 0 0 0 + 0 0 1 0 0 0 + 0 0 0 1 0 0 + 0 0 0 0 1 0 + 0 0 0 0 0 1 +``` + +A quiver on a wall, where the moduli space is a singular 5-fold: +```jldoctest +julia> Q = Quiver("1-2,1-3,2--3"); M = QuiverModuliSpace(Q, [2, 2, 2]); + +julia> intersection_hodge_diamond(M) +6×6 Matrix{Int64}: + 1 0 0 0 0 0 + 0 2 0 0 0 0 + 0 0 3 0 0 0 + 0 0 0 3 0 0 + 0 0 0 0 2 0 + 0 0 0 0 0 1 +``` + +In the smooth case this is the ordinary Hodge diamond: +```jldoctest +julia> M = QuiverModuliSpace(kronecker_quiver(3), [2, 3]); + +julia> intersection_hodge_diamond(M) == hodge_diamond(M) +true +``` +""" +function intersection_hodge_diamond(M::QuiverModuliSpace) + return Matrix{Int}(diagonal(intersection_betti_numbers(M)[1:2:end])) +end diff --git a/src/QuiverTools.jl b/src/QuiverTools.jl index 7186e68..51cc651 100644 --- a/src/QuiverTools.jl +++ b/src/QuiverTools.jl @@ -70,6 +70,11 @@ export is_nonempty, codimension_unstable_locus, dimension, is_smooth, # Hodge export hodge_diamond, hodge_polynomial, picard_rank, index, betti_numbers +# Intersection cohomology +export intersection_poincare_polynomial, + intersection_betti_numbers, + intersection_hodge_diamond + # Chow export chow_ring, motive, index, betti_numbers, poincare_polynomial, is_smooth, is_projective, diff --git a/test/runtests.jl b/test/runtests.jl index baacaa5..e47b0ed 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,7 +12,7 @@ using Test, QuiverTools, Documenter # activates and its doctests resolve `Oscar.*`, without pulling Oscar's exports into # scope (which would clash with QuiverTools names such as `index`, `todd_class`, ...). DocMeta.setdocmeta!(QuiverTools, :DocTestSetup, :(using QuiverTools; import Oscar)) -doctest(QuiverTools; manual=false, testset="Doctests") +doctest(QuiverTools; manual=true, testset="Doctests") @testset "strict sst" begin # proper-semistability @@ -180,3 +180,86 @@ end; @test string(poincare_polynomial(M)) == "L^6 + L^5 + 3*L^4 + 3*L^3 + 3*L^2 + L + 1" end; + +@testset "intersection cohomology" begin + # intersection cohomology agrees with ordinary cohomology on smooth moduli spaces, so + # route a few of those through the Meinhardt--Reineke branch on purpose + for (Q, d, theta) in [ + (kronecker_quiver(2), [1, 1], [1, -1]), + (kronecker_quiver(5), [1, 1], nothing), + (kronecker_quiver(4), [1, 2], [2, 1]), + (kronecker_quiver(5), [2, 3], nothing), + (Quiver([0 1 1; 0 0 2; 0 0 0]), [1, 1, 1], nothing), + ] + M = if isnothing(theta) + QuiverModuliSpace(Q, d) + else + QuiverModuliSpace(Q, d, theta) + end + @test intersection_betti_numbers(M) == betti_numbers(M) + end + + # reflection functors identify M(a, b) with M(b, m*b - a) for the m-Kronecker quiver, + # on dimension vectors whose moduli space is singular + for (m, d, e) in [ + (3, [2, 2], [2, 4]), + (3, [3, 3], [3, 6]), + (3, [2, 3], [3, 7]), + (4, [3, 3], [3, 9]), + (5, [2, 2], [2, 8]), + ] + Q = kronecker_quiver(m) + @test intersection_poincare_polynomial(QuiverModuliSpace(Q, d)) == + intersection_poincare_polynomial(QuiverModuliSpace(Q, e)) + end + + # intersection cohomology of a projective variety satisfies Poincaré duality + for (Q, d) in [ + (kronecker_quiver(3), [2, 2]), + (kronecker_quiver(4), [2, 4]), + (Quiver([0 1 1; 0 0 2; 0 0 0]), [2, 2, 2]), + ] + M = QuiverModuliSpace(Q, d) + betti = intersection_betti_numbers(M) + @test betti == reverse(betti) + @test all(b >= 0 for b in betti) + @test length(betti) == 2 * dimension(M) + 1 + end + + # a quiver need not be acyclic, but then the moduli space is affine rather than + # projective and the answer is compactly supported. These are the spaces of matrix + # invariants, whose intersection cohomology is Theorem 8.2 of [MR4000572]; for `d = 1` + # the moduli space is A^m, and for m = d = 2 it is A^5 because the five traces and + # determinants are independent. + for (m, d, expected) in [ + (2, 1, [2]), + (3, 1, [3]), + (2, 2, [5]), + (2, 3, [10]), + (2, 4, [17, 15]), + (3, 2, [9]), + (3, 3, [19, 17, 16]), + (4, 2, [13, 11]), + ] + M = QuiverModuliSpace(loop_quiver(m), [d]) + @test dimension(M) == (m - 1) * d^2 + 1 + betti = intersection_betti_numbers(M) + @test findall(!iszero, betti) .- 1 == sort(2 .* expected) + @test all(isone, betti[findall(!iszero, betti)]) + end + + # the Donaldson--Thomas invariant vanishes when nothing of dimension vector `d` is + # stable, which is an exact cancellation in the plethystic logarithm + for n in 2:4 + M = QuiverModuliSpace(kronecker_quiver(2), [n, n]) + @test_throws ArgumentError intersection_poincare_polynomial(M) + end + + # the theorem needs the stability parameter to be generic for the slope of `d` + M = QuiverModuliSpace(Quiver([0 1 1 0; 0 0 1 0; 0 0 0 1; 0 0 0 0]), [3, 3, 4, 1]) + @test_throws ArgumentError intersection_poincare_polynomial(M) + + # and it describes the semistable moduli space + M = QuiverModuliSpace(kronecker_quiver(3), [2, 3], [3, -2], "stable") + @test_throws ArgumentError intersection_poincare_polynomial(M) +end;