Quantum Schubert calculus
For homogeneous spaces, quantum Chevalley allows to compute ordinary small quantum Schubert structure constants from root data, without stable-map localization. Here we implemented the formula in order to reconstruct all ordinary quantum products.
julia> R = root_system(:A, 6);
julia> S = [2, 3, 4, 5, 6]; # Correspond to the variety P^6
julia> Q = quantum_schubert_context(R, S); # this stores only the data needed to the Quantum Chevalley
julia> length(Q.representatives) # number of vertices
7
julia> Q.novikov_degrees # one component of at most degree 7
1-element Vector{Int64}:
7
julia> Q.representatives # minimal length elements of the Weyl group of G/P
7-element Vector{WeylGroupElem}:
id
s1
s2 * s1
s3 * s2 * s1
s4 * s3 * s2 * s1
s5 * s4 * s3 * s2 * s1
s6 * s5 * s4 * s3 * s2 * s1
julia> Q.codimensions # the codimention of each representatives
7-element Vector{Int64}:
0
1
2
3
4
5
6
julia> Q.representatives[1]
id
julia> Q.representatives[2]
s1Let us compute the quantum product id * s1. The expected result is s1
julia> quantum_schubert_product(Q, 1, 2)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 1 entry:
(2, (0,)) => 1The result is a dictionary with entries of the form: (vertex, degree) => c. It means the following: The quantum product is the sum of the entries in the form: $c\cdot \sigma_{vertex}\cdot q^{degree}$, where $\sigma_{vertex}$ is the Schubert class given by the vertex. Thus $s_1 = 1 \sigma_{2} q^0$ as expected. Let us try to compute $s_1^9=\sigma_3 * \sigma_7=q=1\cdot\sigma_2\cdot q^1$:
julia> quantum_schubert_product(Q, 3, 7)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 1 entry:
(2, (1,)) => 1
julia> quantum_schubert_coefficient(Q, 1, 7, 2, 1)
1We can compute the entire multiplication table of a Schubert class, and store them in a file for later use.
julia> v = 2;
julia> products = all_quantum_schubert_products(Q, v) # compute all products of v
7-element Vector{Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, BigInt}}:
Dict((2, (0,)) => 1)
Dict((3, (0,)) => 1)
Dict((4, (0,)) => 1)
Dict((5, (0,)) => 1)
Dict((6, (0,)) => 1)
Dict((7, (0,)) => 1)
Dict((1, (1,)) => 1)
julia> products = serialize_quantum_schubert_products("filename.jls", Q, 2); # compute the product and store in a fileWe can have it in form of a matrix, use the following. Use at_q1=true to impose q=1.
julia> M = quantum_schubert_matrix(products; at_q1=false)
[0 0 0 0 0 0 q1]
[1 0 0 0 0 0 0]
[0 1 0 0 0 0 0]
[0 0 1 0 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 0 1 0 0]
[0 0 0 0 0 1 0]
julia> M = quantum_schubert_matrix(products; at_q1=true)
[0 0 0 0 0 0 1]
[1 0 0 0 0 0 0]
[0 1 0 0 0 0 0]
[0 0 1 0 0 0 0]
[0 0 0 1 0 0 0]
[0 0 0 0 1 0 0]
[0 0 0 0 0 1 0]API
GKMtools.QuantumSchubertContext — Type
Root data and a lazy cache for ordinary small quantum Schubert calculus. Construct with quantum_schubert_context. Basis indices are vertex indices; Novikov coordinates correspond to omitted_roots. Mutable caches are not thread-safe. Auxiliary equivariant values use QQ or GF(p), selected by the constructor.
GKMtools.quantum_schubert_context — Function
quantum_schubert_context(G; p=nothing, max_cache_entries=200_000)
quantum_schubert_context(R::RootSystem, levi_indices; p=nothing, max_cache_entries=200_000)Prepare sparse quantum Chevalley data and lazy associativity reconstruction. The graph overload preserves vertex numbering. The root-system overload enumerates minimal coset representatives without a GKM graph or the ambient Weyl group; its ordering is by length and word. levi_indices are the simple roots retained in the parabolic, as in generalized_gkm_flag.
Omit p for exact rational computation, or supply a prime to reconstruct directly over GF(p). The prime must exceed the basis size and admit a separating equivariant specialization; invalid choices throw ArgumentError. Each memo is bounded by max_cache_entries (zero disables memorization). This limits retained cache entries, not total RAM.
GKMtools.quantum_chevalley_product — Function
quantum_chevalley_product(Q, simple_root_index, u)Ordinary quantum product of sigma_u with the divisor for an omitted simple root. Returns a dictionary (vertex, degree_tuple) => coefficient over Q.field (QQ or GF(p)).
julia> R = root_system(:C, 3);
julia> S = [3];
julia> Q = quantum_schubert_context(R, S);
julia> Q.representatives[3]
s2
julia> quantum_chevalley_product(Q, 1, 3)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 2 entries:
(5, (0, 0)) => 1
(4, (0, 0)) => 1
julia> quantum_chevalley_product(Q, 2, 3)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 2 entries:
(4, (0, 0)) => 1
(6, (0, 0)) => 1GKMtools.quantum_schubert_coefficient — Function
quantum_schubert_coefficient(Q, u, v, w, degree=0)Return the ordinary coefficient of q^degree sigma_w in sigma_u ⋆ sigma_v. Degrees are nonnegative tuples ordered by Q.omitted_roots; an integer is accepted for Picard rank one (zero for any rank).
julia> R = root_system(:C, 3);
julia> Q = quantum_schubert_context(R);
julia> quantum_schubert_coefficient(Q, 7, 6, 8, (1, 0, 0))
1
julia> quantum_schubert_coefficient(Q, 7, 6, 22, (0, 0, 0))
2
julia> quantum_schubert_product(Q, 7, 6)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 4 entries:
(22, (0, 0, 0)) => 2
(8, (1, 0, 0)) => 1
(24, (0, 0, 0)) => 1
(9, (1, 0, 0)) => 1GKMtools.quantum_schubert_product — Function
quantum_schubert_product(Q, u, v; max_degree=nothing)Compute an ordinary quantum basis product as a sparse dictionary (output_vertex, degree_tuple) => coefficient over Q.field. By default all possible degrees are included. max_degree is an optional componentwise Novikov bound.
julia> R = root_system(:C, 3);
julia> S = [3];
julia> Q = quantum_schubert_context(R, S);
julia> quantum_schubert_product(Q, 5, 6)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 3 entries:
(13, (0, 0)) => 1
(12, (0, 0)) => 1
(14, (0, 0)) => 1
julia> quantum_schubert_product(Q, 6, 6)
Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem} with 3 entries:
(13, (0, 0)) => 2
(11, (0, 0)) => 2
(1, (0, 1)) => 1GKMtools.quantum_schubert_table — Function
quantum_schubert_table(Q; max_degree=nothing)Compute every ordinary basis product. The dictionary stores pairs (u,v) with u <= v, each mapped to the sparse output of quantum_schubert_product. This can be large; individual products share the same lazy reconstruction cache.
julia> R = root_system(:A, 1);
julia> Q = quantum_schubert_context(R);
julia> quantum_schubert_table(Q)
Dict{Tuple{Int64, Int64}, Dict{Tuple{Int64, Tuple{Vararg{Int64}}}, QQFieldElem}} with 3 entries:
(1, 2) => Dict((2, (0,))=>1)
(1, 1) => Dict((1, (0,))=>1)
(2, 2) => Dict((1, (1,))=>1)GKMtools.serialize_quantum_schubert_products — Function
serialize_quantum_schubert_products(path, Q, u; show_bar=false)Compute all ordinary quantum products sigma_u ⋆ sigma_v for fixed u, save them with Julia's Serialization.serialize, and return the saved vector.
The vector entry products[v] is a sparse dictionary (output_vertex, degree_tuple) => BigInt. Only this vector is stored: no graph, context, cache, representatives, or other metadata. Coefficients are native Julia integers, so loading does not require Oscar or GKMtools. Indices and degree coordinates retain the conventions of Q. For modular contexts, integers are residues in 0:Q.prime-1; the prime is not stored. Supply it when reconstructing a matrix from the saved vector.
Set show_bar=true to display progress over the second factor. All possible Novikov degrees are included. Computation finishes before the destination is opened; an existing file is overwritten.
Example
serialize_quantum_schubert_products("products-u3.jls", Q, 3)
using Serialization
products = deserialize("products-u3.jls")
products[2] # sigma_3 ⋆ sigma_2GKMtools.quantum_schubert_matrix — Function
quantum_schubert_matrix(Q, u; at_q1=false)
quantum_schubert_matrix(products::AbstractVector; at_q1=false, p=nothing)Return the matrix of quantum multiplication by sigma_u. Column v contains sigma_u ⋆ sigma_v: entry (w,v) is the sum of all coefficients c*q^d with output vertex w.
The first overload computes the complete product vector. The second accepts a vector loaded from serialize_quantum_schubert_products, without needing a context or graph. The matrix size is length(products). Supply p when loading modular residues: the result is over GF(p), or its Novikov polynomial ring. The context overload uses Q.prime automatically. The saved product vector contains no prime metadata.
By default the result is an Oscar matrix over QQ[q1,...,qr], with variables in the order of the degree tuples. Set at_q1=true to sum all Novikov degrees and return a matrix over QQ. An empty or entirely zero vector returns a zero matrix over the selected coefficient field, since no Novikov coordinates can be inferred.
Examples
julia> R = root_system(:C, 3);
julia> S = [1, 3];
julia> Q = quantum_schubert_context(R, S);
julia> Q.representatives[2]
s2
julia> M = quantum_schubert_matrix(Q, 2; at_q1=true)
[0 0 0 0 0 0 1 0 0 0 0 0]
[1 0 0 0 0 0 0 0 1 0 0 0]
[0 1 0 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0 1 0]
[0 0 1 1 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0 0 1]
[0 0 0 0 1 2 0 0 0 0 0 0]
[0 0 0 0 2 1 0 0 0 0 0 0]
[0 0 0 0 0 0 1 1 0 0 0 0]
[0 0 0 0 0 0 0 1 0 0 0 0]
[0 0 0 0 0 0 0 0 1 1 0 0]
[0 0 0 0 0 0 0 0 0 0 1 0]
julia> chi = characteristic_polynomial(M)
x^12 - 26*x^7 - 27*x^2
julia> eigenvalues_with_multiplicities(algebraic_closure(QQ), M)
11-element Vector{Tuple{QQBarFieldElem, Int64}}:
({a5: 1.93318}, 1)
({a1: 0}, 2)
({a1: -1.00000}, 1)
({a4: 0.809017 + 0.587785*im}, 1)
({a4: 0.809017 - 0.587785*im}, 1)
({a5: 0.597386 + 1.83857*im}, 1)
({a5: 0.597386 - 1.83857*im}, 1)
({a4: -0.309017 + 0.951057*im}, 1)
({a4: -0.309017 - 0.951057*im}, 1)
({a5: -1.56398 + 1.13630*im}, 1)
({a5: -1.56398 - 1.13630*im}, 1)Products without serialization
GKMtools.all_quantum_schubert_products — Function
all_quantum_schubert_products(Q, u; show_bar=false)Same as serialize_quantum_schubert_products, without serialization.