diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index 9f4ea57cbe04..f63aeaf29d8b 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -565,6 +565,20 @@ @Article{CMT04 doi = {10.1090/S0025-5718-03-01582-5} } +@Article{CS12, + author = {Collinucci, Andres and Savelli, Raffaele}, + title = {{On Flux Quantization in F-Theory}}, + journal = {JHEP}, + volume = {02}, + pages = {015}, + year = {2012}, + doi = {10.1007/JHEP02(2012)015}, + eprint = {1011.6388}, + archiveprefix = {arXiv}, + primaryclass = {hep-th}, + reportnumber = {MAD-TH-10-09, LMU-ASC-100-10} +} + @Book{CS99, author = {Conway, J. H. and Sloane, N. J. A.}, title = {Sphere packings, lattices and groups}, diff --git a/experimental/FTheoryTools/docs/src/g4.md b/experimental/FTheoryTools/docs/src/g4.md index ce69a5483f7a..fc3fa12e0e52 100644 --- a/experimental/FTheoryTools/docs/src/g4.md +++ b/experimental/FTheoryTools/docs/src/g4.md @@ -74,3 +74,20 @@ replace an involved algebraic cycle. ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true) basis_of_h22(v::NormalToricVariety; check::Bool = true) ``` + +Among the $G_4$-flux candidates, the physics is interested in the well-quantized fluxes. That is, +those cohomology classes which integrate to an integer against any other 2-cycle in the elliptic +4-fold $\widehat{Y}_4$. Even in theory, this is a hard task. In practice, one therefore focuses +on consistency checks. In the case at hand, we can integrate any ambient space $G_4$-flux candidate +against a pair of (algebraic cycles associated to) toric divisors. If for any two toric divisors, +the result is an integer, then this $G_4$-flux candidate passes a rather non-trivial and necessary +test. The following method identifies all $G_4$-flux ambient space candidates which pass this test. +Please note that this method may take a long time to execute for involved geometries $\widehat{Y}_4$. + +```@docs +well_quantized_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true) +``` +Similarly, we have a method for all vertical and well-quantized ambient space $G_4$-flux candidates: +```@docs +well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true) +``` diff --git a/experimental/FTheoryTools/src/FTheoryTools.jl b/experimental/FTheoryTools/src/FTheoryTools.jl index f345d829f8c4..0c40cde8be9c 100644 --- a/experimental/FTheoryTools/src/FTheoryTools.jl +++ b/experimental/FTheoryTools/src/FTheoryTools.jl @@ -29,6 +29,8 @@ include("LiteratureModels/create_index.jl") include("G4Fluxes/constructors.jl") include("G4Fluxes/attributes.jl") include("G4Fluxes/properties.jl") +include("G4Fluxes/auxiliary.jl") +include("G4Fluxes/special-intersection-theory.jl") include("G4Fluxes/special_attributes.jl") include("Serialization/tate_models.jl") diff --git a/experimental/FTheoryTools/src/G4Fluxes/auxiliary.jl b/experimental/FTheoryTools/src/G4Fluxes/auxiliary.jl new file mode 100644 index 000000000000..0f796d87d22e --- /dev/null +++ b/experimental/FTheoryTools/src/G4Fluxes/auxiliary.jl @@ -0,0 +1,292 @@ +@doc raw""" + basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} + +By virtue of Theorem 12.4.1 in [CLS11](@cite), one can compute a monomial +basis of $H^4(X, \mathbb{Q})$ for a simplicial, complete toric variety $X$ +by truncating its cohomology ring to degree $2$. Inspired by this, this +method identifies a basis of $H^{(2,2)}(X, \mathbb{Q})$ by multiplying +pairs of cohomology classes associated with toric coordinates. + +By definition, $H^{(2,2)}(X, \mathbb{Q})$ is a subset of $H^{4}(X, \mathbb{Q})$. +However, by Theorem 9.3.2 in [CLS11](@cite), for complete and simplicial +toric varieties and $p \neq q$ it holds $H^{(p,q)}(X, \mathbb{Q}) = 0$. It follows +that for such varieties $H^{(2,2)}(X, \mathbb{Q}) = H^4(X, \mathbb{Q})$ and the +vector space dimension of those spaces agrees with the Betti number $b_4(X)$. + +Note that it can be computationally very demanding to check if a toric variety +$X$ is complete (and simplicial). The optional argument `check` can be set +to `false` to skip these tests. + +# Examples +```jldoctest +julia> Y1 = hirzebruch_surface(NormalToricVariety, 2) +Normal toric variety + +julia> Y2 = hirzebruch_surface(NormalToricVariety, 2) +Normal toric variety + +julia> Y = Y1 * Y2 +Normal toric variety + +julia> h22_basis = basis_of_h22(Y, check = false) +6-element Vector{CohomologyClass}: + Cohomology class on a normal toric variety given by xx2*yx2 + Cohomology class on a normal toric variety given by xt2*yt2 + Cohomology class on a normal toric variety given by xx2*yt2 + Cohomology class on a normal toric variety given by xt2*yx2 + Cohomology class on a normal toric variety given by yx2^2 + Cohomology class on a normal toric variety given by xx2^2 + +julia> betti_number(Y, 4) == length(h22_basis) +true +``` +""" +function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} + + # (0) Some initial checks + if check + @req is_complete(v) "Computation of basis of H22 is currently only supported for complete toric varieties" + @req is_simplicial(v) "Computation of basis of H22 is currently only supported for simplicial toric varieties" + end + if dim(v) < 4 + set_attribute!(v, :basis_of_h22, Vector{CohomologyClass}()) + end + if has_attribute(v, :basis_of_h22) + return get_attribute(v, :basis_of_h22) + end + + # (1) Prepare some data of the variety + mnf = Oscar._minimal_nonfaces(v) + ignored_sets = Set([Tuple(sort(Vector{Int}(Polymake.row(mnf, i)))) for i in 1:Polymake.nrows(mnf)]) + + # (2) Prepare the linear relations + N_lin_rel, my_mat = rref(transpose(matrix(QQ, rays(v)))) + @req N_lin_rel == nrows(my_mat) "Cannot remove as many variables as there are linear relations - weird!" + bad_positions = [findfirst(!iszero, row) for row in eachrow(my_mat)] + lin_rels = Dict{Int, Vector{QQFieldElem}}() + for k in 1:nrows(my_mat) + my_relation = (-1) * my_mat[k, :] + my_relation[bad_positions[k]] = 0 + @req all(k -> k == 0, my_relation[bad_positions]) "Inconsistency!" + lin_rels[bad_positions[k]] = my_relation + end + + # (3) Prepare a list of those variables that we keep, a.k.a. a basis of H^(1,1) + good_positions = setdiff(1:n_rays(v), bad_positions) + n_good_positions = length(good_positions) + + # (4) Make a list of all quadratic elements in the cohomology ring, which are not generators of the SR-ideal. + N_filtered_quadratic_elements = 0 + dict_of_filtered_quadratic_elements = Dict{Tuple{Int64, Int64}, Int64}() + for k in 1:n_good_positions + for l in k:n_good_positions + my_tuple = (min(good_positions[k], good_positions[l]), max(good_positions[k], good_positions[l])) + if !(my_tuple in ignored_sets) + N_filtered_quadratic_elements += 1 + dict_of_filtered_quadratic_elements[my_tuple] = N_filtered_quadratic_elements + end + end + end + + # (5) We only care about the SR-ideal gens of degree 2. Above, we took care of all relations, + # (5) for which both variables are not replaced by one of the linear relations. So, let us identify + # (5) all remaining relations of the SR-ideal, and apply the linear relations to them. + remaining_relations = Vector{Vector{QQFieldElem}}() + for my_tuple in ignored_sets + + # The generator must have degree 2 and at least one variable is to be replaced + if length(my_tuple) == 2 && (my_tuple[1] in bad_positions || my_tuple[2] in bad_positions) + + # Represent first variable by list of coefficients, after plugging in the linear relation + var1 = zeros(QQ, ncols(my_mat)) + var1[my_tuple[1]] = 1 + if my_tuple[1] in bad_positions + var1 = lin_rels[my_tuple[1]] + end + + # Represent second variable by list of coefficients, after plugging in the linear relation + var2 = zeros(QQ, ncols(my_mat)) + var2[my_tuple[2]] = 1 + if my_tuple[2] in bad_positions + var2 = lin_rels[my_tuple[2]] + end + + # Compute the product of the two variables, which represents the new relation + prod = zeros(QQ, N_filtered_quadratic_elements) + for k in 1:length(var1) + if var1[k] != 0 + for l in 1:length(var2) + if var2[l] != 0 + my_tuple = (min(k, l), max(k, l)) + if haskey(dict_of_filtered_quadratic_elements, my_tuple) + prod[dict_of_filtered_quadratic_elements[my_tuple]] += var1[k] * var2[l] + end + end + end + end + end + + # Remember the result + push!(remaining_relations, prod) + + end + + end + + # (9) Identify variables that we can remove with the remaining relations + new_good_positions = 1:N_filtered_quadratic_elements + if length(remaining_relations) != 0 + remaining_relations_matrix = matrix(QQ, remaining_relations) + r, new_mat = rref(remaining_relations_matrix) + @req r == nrows(remaining_relations_matrix) "Cannot remove a variable via linear relations - weird!" + new_bad_positions = [findfirst(!iszero, row) for row in eachrow(new_mat)] + new_good_positions = setdiff(1:N_filtered_quadratic_elements, new_bad_positions) + end + + # (10) Return the basis elements in terms of cohomology classes + S = cohomology_ring(v, check = check) + c_ds = [k.f for k in gens(S)] + final_list_of_tuples = [] + for (key, value) in dict_of_filtered_quadratic_elements + if value in new_good_positions + push!(final_list_of_tuples, key) + end + end + basis_of_h22 = [cohomology_class(v, MPolyQuoRingElem(c_ds[my_tuple[1]]*c_ds[my_tuple[2]], S)) for my_tuple in final_list_of_tuples] + set_attribute!(v, :basis_of_h22, basis_of_h22) + set_attribute!(v, :basis_of_h22_indices, final_list_of_tuples) + return basis_of_h22 + +end + + +# The following is an internal function, that is being used to identify all well-quantized G4-fluxes. +# Let G4 in H^(2,2)(toric_ambient_space) a G4-flux ambient space candidate, i.e. the physically +# truly relevant quantity is the restriction of G4 to a hypersurface V(pt) in the toric_ambient_space. +# To tell if this candidate is well-quantized, we execute a necessary check, namely we verify that +# the integral of G4 * [pt] * [d1] * [d2] over X_Sigma is an integer for any two toric divisors d1, d2. +# While we wish to execute this test for any two toric divisors d1, d2 some pairs can be ignored. +# Say, because their intersection locus is trivial because of the SR-ideal, or because their intersection +# has empty intersection with the hypersurface. The following method identifies the remaining pairs of +# toric divisors d1, d2 that we must consider. + +function _ambient_space_divisor_pairs_to_be_considered(m::AbstractFTheoryModel)::Vector{Tuple{Int64, Int64}} + + if has_attribute(m, :_ambient_space_divisor_pairs_to_be_considered) + return get_attribute(m, :_ambient_space_divisor_pairs_to_be_considered) + end + + gS = gens(cox_ring(ambient_space(m))) + mnf = Oscar._minimal_nonfaces(ambient_space(m)) + ignored_sets = Set([Tuple(sort(Vector{Int}(Polymake.row(mnf, i)))) for i in 1:Polymake.nrows(mnf)]) + + list_of_elements = Vector{Tuple{Int64, Int64}}() + for k in 1:n_rays(ambient_space(m)) + for l in k:n_rays(ambient_space(m)) + + # V(x_k, x_l) = emptyset? + (k,l) in ignored_sets && continue + + # Simplify the hypersurface polynomial by setting relevant variables to zero. + # If all coefficients of this new polynomial add to sum, then we keep this generator. + new_p_hyper = divrem(hypersurface_equation(m), gS[k])[2] + if k != l + new_p_hyper = divrem(new_p_hyper, gS[l])[2] + end + if sum(coefficients(new_p_hyper)) == 0 + push!(list_of_elements, (k,l)) + continue + end + + # Determine remaining variables, after scaling "away" others. + remaining_vars_list = Set(1:length(gS)) + for my_exps in ignored_sets + len_my_exps = length(my_exps) + inter_len = count(idx -> idx in [k,l], my_exps) + if (len_my_exps == 2 && inter_len == 1) || (len_my_exps == 3 && inter_len == 2) + delete!(remaining_vars_list, my_exps[findfirst(idx -> !(idx in [k,l]), my_exps)]) + end + end + remaining_vars_list = collect(remaining_vars_list) + + # If one monomial of `new_p_hyper` has unset positions (a.k.a. new_p_hyper is not constant upon + # scaling the remaining variables), then keep this generator. + for exps in exponents(new_p_hyper) + if any(x -> x != 0, exps[remaining_vars_list]) + push!(list_of_elements, (k,l)) + break + end + end + + end + end + + # Remember this result as attribute and return the findings. + set_attribute!(m, :_ambient_space_divisor_pairs_to_be_considered, list_of_elements) + return list_of_elements + +end + + +# The following is an internal function, that is being used to identify all well-quantized and +# vertical G4-flux ambient candidates. For this, we look for pairs of (pushforwards of) base divisors, +# s.t. their common zero locus does not intersect the CY hypersurface trivially. +# This method makes a pre-selection of such base divisor pairs. "Pre" means that we execute a sufficient, +# but not necessary, check to tell if a pair of base divisors restricts trivially. + +function _ambient_space_base_divisor_pairs_to_be_considered(m::AbstractFTheoryModel)::Vector{Tuple{Int64, Int64}} + + if has_attribute(m, :_ambient_space_base_divisor_pairs_to_be_considered) + return get_attribute(m, :_ambient_space_base_divisor_pairs_to_be_considered) + end + + gS = gens(cox_ring(ambient_space(m))) + mnf = Oscar._minimal_nonfaces(ambient_space(m)) + ignored_sets = Set([Tuple(sort(Vector{Int}(Polymake.row(mnf, i)))) for i in 1:Polymake.nrows(mnf)]) + + list_of_elements = Vector{Tuple{Int64, Int64}}() + for k in 1:n_rays(base_space(m)) + for l in k:n_rays(base_space(m)) + + # V(x_k, x_l) = emptyset? + (k,l) in ignored_sets && continue + + # Simplify the hypersurface polynomial by setting relevant variables to zero. + # If all coefficients of this new polynomial add to sum, then we keep this generator. + new_p_hyper = divrem(hypersurface_equation(m), gS[k])[2] + if k != l + new_p_hyper = divrem(new_p_hyper, gS[l])[2] + end + if sum(coefficients(new_p_hyper)) == 0 + push!(list_of_elements, (k,l)) + continue + end + + # Determine remaining variables, after scaling "away" others. + remaining_vars_list = Set(1:length(gS)) + for my_exps in ignored_sets + len_my_exps = length(my_exps) + inter_len = count(idx -> idx in [k,l], my_exps) + if (len_my_exps == 2 && inter_len == 1) || (len_my_exps == 3 && inter_len == 2) + delete!(remaining_vars_list, my_exps[findfirst(idx -> !(idx in [k,l]), my_exps)]) + end + end + remaining_vars_list = collect(remaining_vars_list) + + # If one monomial of `new_p_hyper` has unset positions (a.k.a. new_p_hyper is not constant upon + # scaling the remaining variables), then keep this generator. + for exps in exponents(new_p_hyper) + if any(x -> x != 0, exps[remaining_vars_list]) + push!(list_of_elements, (k,l)) + break + end + end + + end + end + + # Remember this result as attribute and return the findings. + set_attribute!(m, :_ambient_space_base_divisor_pairs_to_be_considered, list_of_elements) + return list_of_elements + +end diff --git a/experimental/FTheoryTools/src/G4Fluxes/special-intersection-theory.jl b/experimental/FTheoryTools/src/G4Fluxes/special-intersection-theory.jl new file mode 100644 index 000000000000..2015c8b1c332 --- /dev/null +++ b/experimental/FTheoryTools/src/G4Fluxes/special-intersection-theory.jl @@ -0,0 +1,270 @@ +# --------------------------------------------------------------------------------------------------------- +# (1) Compute the intersection product of an algebraic cycle with a hypersurface. +# --------------------------------------------------------------------------------------------------------- + +function sophisticated_intersection_product(v::NormalToricVariety, indices::NTuple{4, Int64}, hypersurface_equation::MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}, inter_dict::Dict{NTuple{4, Int64}, ZZRingElem}, s_inter_dict::Dict{String, ZZRingElem}, data::NamedTuple) + + # (A) Have we computed this intersection number in the past? If so, just use that result... + indices = Tuple(sort(collect(indices))) + if haskey(inter_dict, indices) + return inter_dict[indices] + end + + # (B) Is the intersection, by virtue of the SR-ideal, trivial? + for sr_set in data.sr_ideal_pos + if is_subset(sr_set, indices) + inter_dict[indices] = ZZ(0) + return ZZ(0) + end + end + + # (C) Deal with self-intersection and should-never-happen case. + distinct_variables = Set(indices) + if length(distinct_variables) < 4 && length(distinct_variables) >= 1 + return intersection_from_equivalent_cycle(v, indices, hypersurface_equation, inter_dict, s_inter_dict, data) + end + if length(distinct_variables) == 0 + println("WEIRD! THIS SHOULD NEVER HAPPEN! INFORM THE AUTHORS!") + println("") + end + + + # (D) Deal with transverse intersection... + + # D.1 Work out the intersection locus in detail. + pt_reduced, gs_reduced, remaining_vars, reduced_scaling_relations = Oscar._reduce_hypersurface_equation(v, hypersurface_equation, indices, data) + + # D.2 If pt == 0, then we are not looking at a transverse intersection. So take an equivalent cycle and try again... + if is_zero(pt_reduced) + return intersection_from_equivalent_cycle(v, indices, hypersurface_equation, inter_dict, s_inter_dict, data) + end + + # D.3 If pt is constant and non-zero, then the intersection is trivial. + if is_constant(pt_reduced) && is_zero(pt_reduced) == false + inter_dict[indices] = ZZ(0) + return ZZ(0) + end + + # D.4 Helper function for the cases below + function has_one_and_rest_zero(vec) + return count(==(1), vec) == 1 && all(x -> x == 0 || x == 1, vec) + end + + # C.5 Cover a case that seems to appear frequently for our investigation: + # pt_reduced of the form a * x + b * y for non-zero number a,b and remaining variables x, y subject to a reduced SR generator x * y and scaling relation [1,1]. + # This will thus always give exactly one solution (x = 1, y = -a/b), and so the intersection number is one. + if length(gs_reduced) == 1 && length(remaining_vars) == 2 + mons_list = collect(monomials(pt_reduced)) + if length(mons_list) == 2 + if all(x -> x != 0, collect(coefficients(pt_reduced))) + exps_list = [collect(exponents(k))[1] for k in mons_list] + if has_one_and_rest_zero(exps_list[1]) && has_one_and_rest_zero(exps_list[2]) + if gs_reduced[1] == remaining_vars[1] * remaining_vars[2] + if reduced_scaling_relations == matrix(ZZ, [[1,1]]) + inter_dict[indices] = ZZ(1) + return ZZ(1) + end + end + end + end + end + end + + # C.6 Cover a case that seems to appear frequently for our investigation: + # pt_reduced of the form a * x for non-zero number a and remaining variables x, y subject to a reduced SR generator x * y and scaling relation [*, != 0]. + # This only gives the solution [0:1], so one intersection point. + if length(gs_reduced) == 1 && length(remaining_vars) == 2 + mons_list = collect(monomials(pt_reduced)) + if length(mons_list) == 1 && collect(coefficients(pt_reduced))[1] != 0 + list_of_exps = collect(exponents(mons_list[1]))[1] + number_of_zeros = count(==(0), list_of_exps) + if number_of_zeros == length(list_of_exps) - 1 + if gs_reduced[1] == remaining_vars[1] * remaining_vars[2] + if reduced_scaling_relations[1,1] != 0 && reduced_scaling_relations[1,2] != 0 + highest_power = list_of_exps[findfirst(x -> x > 0, list_of_exps)] + if highest_power == 1 + inter_dict[indices] = highest_power + return highest_power + end + end + end + end + end + end + + # C.7 Cover a case that seems to appear frequently for our investigation. It looks as follows: + # pt_reduced = -5700*w8*w10 + # remaining_vars = MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}[w8, w10] + # gs_reduced = MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}[w8*w10] + # reduced_scaling_relations = [1 1] + # This gives exactly two solutions, namely [0:1] and [1:0]. + if length(gs_reduced) == 1 && length(remaining_vars) == 2 + mons_list = collect(monomials(pt_reduced)) + if length(mons_list) == 1 && mons_list[1] == remaining_vars[1] * remaining_vars[2] + if gs_reduced[1] == remaining_vars[1] * remaining_vars[2] + if reduced_scaling_relations == matrix(ZZ, [[1,1]]) + inter_dict[indices] = 2 + return 2 + end + end + end + end + + # C.8 Check if this was covered in our special cases + if haskey(s_inter_dict, string([pt_reduced, gs_reduced, remaining_vars, reduced_scaling_relations])) + numb = s_inter_dict[string([pt_reduced, gs_reduced, remaining_vars, reduced_scaling_relations])] + inter_dict[indices] = numb + return numb + end + + # C.9 In all other cases, proceed via a rationally equivalent cycle + println("") + println("FOUND CASE THAT CANNOT YET BE DECIDED!") + println("$pt_reduced") + println("$remaining_vars") + println("$gs_reduced") + println("$indices") + println("$reduced_scaling_relations") + println("TRYING WITH EQUIVALENT CYCLE") + println("") + numb = intersection_from_equivalent_cycle(v, indices, hypersurface_equation, inter_dict, s_inter_dict, data) + s_inter_dict[string([pt_reduced, gs_reduced, remaining_vars, reduced_scaling_relations])] = numb + return numb + +end + + + +# --------------------------------------------------------------------------------------------------------- +# (2) Compute the intersection product from a rationally equivalent cycle. +# --------------------------------------------------------------------------------------------------------- + +function intersection_from_equivalent_cycle(v::NormalToricVariety, indices::NTuple{4, Int64}, hypersurface_equation::MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}, inter_dict::Dict{NTuple{4, Int64}, ZZRingElem}, s_inter_dict::Dict{String, ZZRingElem}, data::NamedTuple) + coeffs_list, tuple_list = Oscar._rationally_equivalent_cycle(v, indices, data) + intersect_numb = 0 + for k in 1:length(tuple_list) + if !is_zero(coeffs_list[k]) + intersect_numb += coeffs_list[k] * sophisticated_intersection_product(v, tuple_list[k], hypersurface_equation, inter_dict, s_inter_dict, data) + end + end + @req is_integer(intersect_numb) "Should have expected to find only integer intersection numbers, but found $intersect_numb for $indices" + inter_dict[indices] = ZZ(intersect_numb) + return ZZ(intersect_numb) +end + + + +# --------------------------------------------------------------------------------------------------------- +# (3) A function to reduce the hypersurface polynomial to {xi = 0} with i the entries of the tuple indices. +# --------------------------------------------------------------------------------------------------------- + +function _reduce_hypersurface_equation(v::NormalToricVariety, p_hyper::MPolyRingElem, indices::NTuple{4, Int64}, data::NamedTuple) + + # Set variables to zero in the hypersurface equation + vanishing_vars_pos = unique(indices) + new_p_hyper = divrem(p_hyper, data.gS[vanishing_vars_pos[1]])[2] + for m in 2:length(vanishing_vars_pos) + new_p_hyper = divrem(new_p_hyper, data.gS[vanishing_vars_pos[m]])[2] + end + + # Is the resulting polynomial constant? + if is_constant(new_p_hyper) + return [new_p_hyper, [], [], zero_matrix(ZZ, 0, 0)] + end + + # Identify the remaining variables + remaining_vars_pos = Set(1:length(data.gS)) + for my_exps in data.sr_ideal_pos + len_my_exps = length(my_exps) + inter_len = count(idx -> idx in vanishing_vars_pos, my_exps) + if len_my_exps == inter_len + 1 + delete!(remaining_vars_pos, my_exps[findfirst(idx -> !(idx in vanishing_vars_pos), my_exps)]) + end + end + set_to_one_list = sort([k for k in 1:length(data.gS) if k ∉ remaining_vars_pos]) + remaining_vars_pos = setdiff(collect(remaining_vars_pos), vanishing_vars_pos) + remaining_vars = [data.gS[k] for k in remaining_vars_pos] + + # Extract remaining Stanley-Reisner ideal relations + sr_reduced = Vector{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}}() + for k in 1:length(data.sr_ideal_pos) + if isdisjoint(set_to_one_list, data.sr_ideal_pos[k]) + new_gen = prod(data.gS[m] for m in data.sr_ideal_pos[k] if m ∉ vanishing_vars_pos) + if !(new_gen in sr_reduced) + push!(sr_reduced, new_gen) + end + end + end + + # Identify the remaining scaling relations + prepared_scaling_relations = zero_matrix(ZZ, length(data.scalings[1]), length(set_to_one_list) + length(remaining_vars_pos)) + for k in 1:(length(set_to_one_list) + length(remaining_vars_pos)) + col = k <= length(set_to_one_list) ? data.scalings[set_to_one_list[k]] : data.scalings[remaining_vars_pos[k - length(set_to_one_list)]] + for l in 1:length(col) + prepared_scaling_relations[l, k] = col[l] + end + end + prepared_scaling_relations = hnf(prepared_scaling_relations) + reduced_scaling_relations = prepared_scaling_relations[length(set_to_one_list) + 1: nrows(prepared_scaling_relations), length(set_to_one_list) + 1 : ncols(prepared_scaling_relations)] + + # Identify the final form of the reduced hypersurface equation, by setting all variables to one that we can + images = [k in remaining_vars_pos ? data.gS[k] : one(data.S) for k in 1:length(data.gS)] + pt_reduced = evaluate(new_p_hyper, images) + + # Return the result + return [pt_reduced, sr_reduced, remaining_vars, reduced_scaling_relations] +end + + + +# --------------------------------------------------------------------------------------------------------- +# (4) A function to find a rationally equivalent algebraic cycle. +# --------------------------------------------------------------------------------------------------------- + +function _rationally_equivalent_cycle(v::NormalToricVariety, indices::NTuple{4, Int64}, data::NamedTuple) + + # Identify positions of the single and triple variable + power_variable = indices[rand(1:length(indices))] + for k in Set(indices) + if count(==(k), indices) > 1 + power_variable = k + break + end + end + other_variables = collect(Set(filter(!=(power_variable), indices))) + pos_power_variable = findfirst(==(power_variable), indices) + + # Let us simplify the problem by extracting the entries in the columns of single_variables and double_variables of the linear relation matrix + simpler_matrix = data.linear_relations[[other_variables..., power_variable], :] + b = zero_matrix(QQ, length(other_variables) + 1, 1) + b[nrows(b), 1] = 1 + A = solve(simpler_matrix, b; side =:right) + + # Now form the relation in case... + employed_relation = -sum((data.linear_relations[:, k] .* A[k]) for k in 1:5) + employed_relation[power_variable] = 0 + + # Populate `coeffs` and `tuples` that form rationally equivalent algebraic cycle + coeffs = Vector{QQFieldElem}() + tuples = Vector{NTuple{4, Int64}}() + for k in 1:length(employed_relation) + if employed_relation[k] != 0 + + # Form the new tuple + new_tuple = (indices[1:pos_power_variable-1]..., k, indices[pos_power_variable+1:end]...) + new_tuple = Tuple(sort(collect(new_tuple))) + + pos = findfirst(==(new_tuple), tuples) + if pos !== nothing + coeffs[pos] += employed_relation[k] + else + push!(coeffs, employed_relation[k]) + push!(tuples, Tuple(new_tuple)) + end + + end + end + + return [coeffs, tuples] + +end diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index 49e20bc07bcd..5cbf14eb4840 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -1,164 +1,3 @@ -@doc raw""" - basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} - -By virtue of Theorem 12.4.1 in [CLS11](@cite), one can compute a monomial -basis of $H^4(X, \mathbb{Q})$ for a simplicial, complete toric variety $X$ -by truncating its cohomology ring to degree $2$. Inspired by this, this -method identifies a basis of $H^{(2,2)}(X, \mathbb{Q})$ by multiplying -pairs of cohomology classes associated with toric coordinates. - -By definition, $H^{(2,2)}(X, \mathbb{Q})$ is a subset of $H^{4}(X, \mathbb{Q})$. -However, by Theorem 9.3.2 in [CLS11](@cite), for complete and simplicial -toric varieties and $p \neq q$ it holds $H^{(p,q)}(X, \mathbb{Q}) = 0$. It follows -that for such varieties $H^{(2,2)}(X, \mathbb{Q}) = H^4(X, \mathbb{Q})$ and the -vector space dimension of those spaces agrees with the Betti number $b_4(X)$. - -Note that it can be computationally very demanding to check if a toric variety -$X$ is complete (and simplicial). The optional argument `check` can be set -to `false` to skip these tests. - -# Examples -```jldoctest -julia> Y1 = hirzebruch_surface(NormalToricVariety, 2) -Normal toric variety - -julia> Y2 = hirzebruch_surface(NormalToricVariety, 2) -Normal toric variety - -julia> Y = Y1 * Y2 -Normal toric variety - -julia> h22_basis = basis_of_h22(Y, check = false) -6-element Vector{CohomologyClass}: - Cohomology class on a normal toric variety given by xx2*yx2 - Cohomology class on a normal toric variety given by xt2*yt2 - Cohomology class on a normal toric variety given by xx2*yt2 - Cohomology class on a normal toric variety given by xt2*yx2 - Cohomology class on a normal toric variety given by yx2^2 - Cohomology class on a normal toric variety given by xx2^2 - -julia> betti_number(Y, 4) == length(h22_basis) -true -``` -""" -function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} - - # (0) Some initial checks - if check - @req is_complete(v) "Computation of basis of H22 is currently only supported for complete toric varieties" - @req is_simplicial(v) "Computation of basis of H22 is currently only supported for simplicial toric varieties" - end - if dim(v) < 4 - set_attribute!(v, :basis_of_h22, Vector{CohomologyClass}()) - end - if has_attribute(v, :basis_of_h22) - return get_attribute(v, :basis_of_h22) - end - - # (1) Prepare some data of the variety - mnf = Oscar._minimal_nonfaces(v) - ignored_sets = Set([Tuple(sort(Vector{Int}(Polymake.row(mnf, i)))) for i in 1:Polymake.nrows(mnf)]) - - # (2) Prepare the linear relations - N_lin_rel, my_mat = rref(transpose(matrix(QQ, rays(v)))) - @req N_lin_rel == nrows(my_mat) "Cannot remove as many variables as there are linear relations - weird!" - bad_positions = [findfirst(!iszero, row) for row in eachrow(my_mat)] - lin_rels = Dict{Int, Vector{QQFieldElem}}() - for k in 1:nrows(my_mat) - my_relation = (-1) * my_mat[k, :] - my_relation[bad_positions[k]] = 0 - @req all(k -> k == 0, my_relation[bad_positions]) "Inconsistency!" - lin_rels[bad_positions[k]] = my_relation - end - - # (3) Prepare a list of those variables that we keep, a.k.a. a basis of H^(1,1) - good_positions = setdiff(1:n_rays(v), bad_positions) - n_good_positions = length(good_positions) - - # (4) Make a list of all quadratic elements in the cohomology ring, which are not generators of the SR-ideal. - N_filtered_quadratic_elements = 0 - dict_of_filtered_quadratic_elements = Dict{Tuple{Int64, Int64}, Int64}() - for k in 1:n_good_positions - for l in k:n_good_positions - my_tuple = (min(good_positions[k], good_positions[l]), max(good_positions[k], good_positions[l])) - if !(my_tuple in ignored_sets) - N_filtered_quadratic_elements += 1 - dict_of_filtered_quadratic_elements[my_tuple] = N_filtered_quadratic_elements - end - end - end - - # (5) We only care about the SR-ideal gens of degree 2. Above, we took care of all relations, - # (5) for which both variables are not replaced by one of the linear relations. So, let us identify - # (5) all remaining relations of the SR-ideal, and apply the linear relations to them. - remaining_relations = Vector{Vector{QQFieldElem}}() - for my_tuple in ignored_sets - - # The generator must have degree 2 and at least one variable is to be replaced - if length(my_tuple) == 2 && (my_tuple[1] in bad_positions || my_tuple[2] in bad_positions) - - # Represent first variable by list of coefficients, after plugging in the linear relation - var1 = zeros(QQ, ncols(my_mat)) - var1[my_tuple[1]] = 1 - if my_tuple[1] in bad_positions - var1 = lin_rels[my_tuple[1]] - end - - # Represent second variable by list of coefficients, after plugging in the linear relation - var2 = zeros(QQ, ncols(my_mat)) - var2[my_tuple[2]] = 1 - if my_tuple[2] in bad_positions - var2 = lin_rels[my_tuple[2]] - end - - # Compute the product of the two variables, which represents the new relation - prod = zeros(QQ, N_filtered_quadratic_elements) - for k in 1:length(var1) - if var1[k] != 0 - for l in 1:length(var2) - if var2[l] != 0 - my_tuple = (min(k, l), max(k, l)) - if haskey(dict_of_filtered_quadratic_elements, my_tuple) - prod[dict_of_filtered_quadratic_elements[my_tuple]] += var1[k] * var2[l] - end - end - end - end - end - - # Remember the result - push!(remaining_relations, prod) - - end - - end - - # (9) Identify variables that we can remove with the remaining relations - new_good_positions = 1:N_filtered_quadratic_elements - if length(remaining_relations) != 0 - remaining_relations_matrix = matrix(QQ, remaining_relations) - r, new_mat = rref(remaining_relations_matrix) - @req r == nrows(remaining_relations_matrix) "Cannot remove a variable via linear relations - weird!" - new_bad_positions = [findfirst(!iszero, row) for row in eachrow(new_mat)] - new_good_positions = setdiff(1:N_filtered_quadratic_elements, new_bad_positions) - end - - # (10) Return the basis elements in terms of cohomology classes - S = cohomology_ring(v, check = check) - c_ds = [k.f for k in gens(S)] - final_list_of_tuples = [] - for (key, value) in dict_of_filtered_quadratic_elements - if value in new_good_positions - push!(final_list_of_tuples, key) - end - end - basis_of_h22 = [cohomology_class(v, MPolyQuoRingElem(c_ds[my_tuple[1]]*c_ds[my_tuple[2]], S)) for my_tuple in final_list_of_tuples] - set_attribute!(v, :basis_of_h22, basis_of_h22) - return basis_of_h22 - -end - - @doc raw""" ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Vector{CohomologyClass} @@ -210,6 +49,13 @@ function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool # Execute entry tests in computation of basis_of_h22. If any of these fail, no need to proceed. Hence, do this first. filtered_h22_basis = basis_of_h22(ambient_space(m), check = check) + # Each basis element is given by the vanishing of two homogeneous variables. We extract those indices... + filtered_h22_basis_indices_init = get_attribute(ambient_space(m), :basis_of_h22_indices) + + # It may happen that filtered_h22_basis_indices_init is encoded as Vector{Any}. But it is a Vector{Tuple{Int64, Int64}} + # Of course, this should be fixed more properly, but for now, the following works... + filtered_h22_basis_indices = [k for k in filtered_h22_basis_indices_init]::Vector{Tuple{Int64, Int64}} + # Prepare data of the toric ambient space gS = gens(cox_ring(ambient_space(m))) mnf = Oscar._minimal_nonfaces(ambient_space(m)) @@ -217,12 +63,9 @@ function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool # Filter out basis elements for a in length(filtered_h22_basis):-1:1 - - # Find non-zero exponent positions in the polynomial filtered_h22_basis[a] - exp_list = collect(exponents(polynomial(filtered_h22_basis[a]).f))[1] - vanishing_vars_pos = findall(!=(0), exp_list) # Simplify the hypersurface polynomial by setting relevant variables to zero + vanishing_vars_pos = [filtered_h22_basis_indices[a]...] new_pt = divrem(hypersurface_equation(m), gS[vanishing_vars_pos[1]])[2] if length(vanishing_vars_pos) == 2 new_pt = divrem(new_pt, gS[vanishing_vars_pos[2]])[2] @@ -254,11 +97,538 @@ function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool end if delete_it deleteat!(filtered_h22_basis, a) + deleteat!(filtered_h22_basis_indices, a) end end set_attribute!(m, :ambient_space_models_of_g4_fluxes, filtered_h22_basis) + set_attribute!(m, :ambient_space_models_of_g4_fluxes_indices, filtered_h22_basis_indices) return filtered_h22_basis end + + +@doc raw""" + well_quantized_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Tuple{QQMatrix, QQMatrix} + +Given an F-theory model $m$ defined as hypersurface in a simplicial and +complete toric base, this method computes a basis of all well-quantized +ambient space $G_4$-fluxes. The result of this operation is a tuple of two +matrices. The columns of the first matrix specify those (rational) combinations +of ambient space $G_4$-fluxes, of which one may only take $\mathbb{Z}$-linear +combinations without violating flux quantization. The columns of the second +matrix specify those (rational) combinations of ambient space $G_4$-fluxes, for +which any rational linear combination satisfies the flux quantization condition. + +Crucially, this method assumes that $c_2( \widehat{Y}_4)$ is even. Currently, no +check is conducted and no error raised. Use with care! + +Recall that this is relevant in so much as the quantization condition asks to +verify if the twist of the given $G_4$-flux by $1/2 \cdot c_2( \widehat{Y}_4)$ is +even. Recall also that it is known that for many F-theory models, $c_2( \widehat{Y}_4)$ +is an even class. For instance, this applies to all F-theory compactifications +on an elliptically fibered smooth Calabi-Yau 4-fold with a globally defined +Weierstrass model [CS12](@cite). For instance, this means that all of the +F-theory QSMs [CHLLT19](@cite) have an even $c_2( \widehat{Y}_4)$. + +It can be computationally very demanding to check if a toric variety +$X$ is complete (and simplicial). The optional argument `check` can be set +to `false` to skip these tests. + +# Examples +```jldoctest +julia> B3 = projective_space(NormalToricVariety, 3) +Normal toric variety + +julia> Kbar = anticanonical_divisor_class(B3) +Divisor class on a normal toric variety + +julia> t = literature_model(arxiv_id = "1109.3454", equation = "3.1", base_space = B3, defining_classes = Dict("w"=>Kbar)) +Construction over concrete base may lead to singularity enhancement. Consider computing singular_loci. However, this may take time! + +Global Tate model over a concrete base -- SU(5)xU(1) restricted Tate model based on arXiv paper 1109.3454 Eq. (3.1) + +julia> ambient_space_models_of_g4_fluxes(t, check = false); + +julia> res = well_quantized_ambient_space_models_of_g4_fluxes(t, check = false); + +julia> res[1] +[1//4 -3//16] +[ 0 1//144] + +julia> res[2] +2 by 0 empty matrix +``` + +Here is a more interesting example. + +```jldoctest; setup = :(Oscar.LazyArtifacts.ensure_artifact_installed("QSMDB", Oscar.LazyArtifacts.find_artifacts_toml(Oscar.oscardir))) +julia> qsm_model = literature_model(arxiv_id = "1903.00009", model_parameters = Dict("k" => 2021)) +Hypersurface model over a concrete base + +julia> g4_base = ambient_space_models_of_g4_fluxes(qsm_model, check = false); + +julia> length(g4_base) +37 + +julia> res = well_quantized_ambient_space_models_of_g4_fluxes(qsm_model, check = false); + +julia> size(res[1]) +(37, 37) + +julia> size(res[2]) +(37, 0) + +julia> M = res[1]; + +julia> g4_class = sum(M[i,j]*g4_base[i] for i in 1:length(g4_base) for j in 1:size(M,2)); + +julia> g4 = g4_flux(qsm_model, g4_class, check = false) +G4-flux candidate lacking elementary quantization checks + +julia> passes_elementary_quantization_checks(g4) +true +``` +""" +function well_quantized_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Tuple{QQMatrix, QQMatrix} + + # (1) Entry checks + @req base_space(m) isa NormalToricVariety "Computation of well-quantized G4-fluxes only supported for toric base and ambient spaces" + @req dim(ambient_space(m)) == 5 "Computation of well-quantized G4-fluxes only supported for 5-dimensional toric ambient spaces" + if check + @req is_complete(ambient_space(m)) "Computation of well-quantized G4-fluxes only supported for complete toric ambient spaces" + @req is_simplicial(ambient_space(m)) "Computation of well-quantized G4-fluxes only supported for simplicial toric ambient space" + end + if has_attribute(m, :well_quantized_ambient_space_models_of_g4_fluxes) + return get_attribute(m, :well_quantized_ambient_space_models_of_g4_fluxes) + end + + + # (2) Compute data, that is frequently used by the sophisticated intersection product below + S = cox_ring(ambient_space(m)) + gS = gens(cox_ring(ambient_space(m))) + linear_relations = matrix(QQ, matrix(ZZ, rays(ambient_space(m)))) + scalings = [c.coeff for c in S.d] + mnf = Oscar._minimal_nonfaces(ambient_space(m)) + sr_ideal_pos = [Vector{Int}(Polymake.row(mnf, i)) for i in 1:Polymake.nrows(mnf)] + data = ( + S = S, + gS = gS, + linear_relations = linear_relations, + scalings = scalings, + sr_ideal_pos = sr_ideal_pos + ) + + + # (3) Are intersection numbers known? + inter_dict = Dict{NTuple{4, Int64}, ZZRingElem}() + s_inter_dict = Dict{String, ZZRingElem}() + if has_attribute(m, :inter_dict) + inter_dict = get_attribute(m, :inter_dict) + end + if has_attribute(m, :s_inter_dict) + s_inter_dict = get_attribute(m, :s_inter_dict) + end + + + # (4) Obtain critical information - this may take significant time! + ambient_space_flux_candidates_basis = ambient_space_models_of_g4_fluxes(m, check = check) + ambient_space_flux_candidates_basis_indices = get_attribute(m, :ambient_space_models_of_g4_fluxes_indices) + list_of_divisor_pairs_to_be_considered = Oscar._ambient_space_divisor_pairs_to_be_considered(m) + + + # (5) Work out the relevant intersection numbers and organize them in a constraint_matrix. + constraint_matrix = Vector{Vector{QQFieldElem}}() + # I have prepared some functionality below, regarding the case that this matrix should have rational entries. + # However, I expect that this will not happen as long as the hypersurface in question is smooth. + if arxiv_doi(m) == "10.48550/arXiv.1511.03209" + + # Use special intersection theory for special F-theory model. This technology could be extended beyond this one use-case in the future. + for i in 1:length(ambient_space_flux_candidates_basis) + condition = Vector{ZZRingElem}() + for j in 1:length(list_of_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_divisor_pairs_to_be_considered[j]...])) + push!(condition, sophisticated_intersection_product(ambient_space(m), my_tuple, hypersurface_equation(m), inter_dict, s_inter_dict, data)) + end + push!(constraint_matrix, condition) + end + + else + + # Cover all other case with generic, but potentially painfully slow methodology. + tds = torusinvariant_prime_divisors(ambient_space(m)) + cds = [cohomology_class(tds[i]) for i in 1:length(tds)] + pt_class = cohomology_class(anticanonical_divisor_class(ambient_space(m))) + for i in 1:length(ambient_space_flux_candidates_basis) + condition = Vector{ZZRingElem}() + for j in 1:length(list_of_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_divisor_pairs_to_be_considered[j]...])) + if !haskey(inter_dict, my_tuple) + class = ambient_space_flux_candidates_basis[i] * cds[list_of_divisor_pairs_to_be_considered[j][1]] * cds[list_of_divisor_pairs_to_be_considered[j][2]] * pt_class + inter_dict[my_tuple] = integrate(class) + end + push!(condition, inter_dict[my_tuple]) + end + push!(constraint_matrix, condition) + end + + end + + # (6) Convert the intersection matrix to a ZZ matrix. If necessary, multiply it by a suitable integer. + # (6) Then compute its Smith normal form. + denom = lcm(unique(vcat([denominator.(k) for k in constraint_matrix]...))) + if denom != 1 + constraint_matrix = denom * constraint_matrix + end + C = transpose(matrix(ZZ, constraint_matrix)) + S, T, U = snf_with_transform(C) + + + # (7) Recall that we are seeking constraint_matrix * q = n, where q is a vector of rational numbers and n a vector of natural numbers. + # (7) Above, we multiplied the constraint matrix with the denominator if necessary. Thereby, this equation is equivalent to C * q = d * n. + # (7) Since T is an invertible matrix with integer entries, this system is equivalent to T * C * q = T * (d * n). + # (7) This in turn is equivalent to (T * C * U) * (U^-1 * q) = d * (T*n). + # (7) In other words, we have S * (U^(-1) * q) = d * (T*n). + # (7) Note that T*n are just other integers, which we may use to parametrize the space of solutions. Let us thus write: + # (7) So we have S * (U^-1) * q) = d * N, with N the newly chosen parametrization. Let tilde_q = U^(-1) * q. + r = rank(S) + @req all(k -> !is_zero(S[k,k]), 1:r) "Inconsistency in Smith normal form detected. Please inform the authors." + @req all(k -> is_zero(S[k,k]), r+1:min(nrows(S), ncols(S))) "Inconsistency in Smith normal form detected. Please inform the authors." + # (7) S is diagonal, and has non-zero entries at diagonal position 1 to r: S = (l1, ..., lr, 0, ..., 0). Therefore, every q_tilde + # (7) which solves the above is of the form q_tilde = (1/l1 * d * N1, ..., 1/lr * d * N_r, tilde_q_(r+1), ..., ), where + # (7) tilde_q_(r+1) etc. are unconstrained. We encode this solution in the following matrix. + S_prime = zero_matrix(QQ, ncols(S), ncols(S)) + for k in 1:min(nrows(S), ncols(S)) + if k <= r + S_prime[k,k] = denom//S[k,k] + else + S_prime[k,k] = 1 + end + end + # (7) To extract the solutions q from these solutions tilde_q, we multiply with the matrix U from the left. + solution_matrix = U * S_prime + + + # (8) Overall, we are allowed to take any Z-linear combinations of the first r columns of solution_matrix together with any + # (8) rational combination of its remaining columns. Those are exactly the G4-fluxes which pass the elementary quantization tests. + # (8) Note that the set of vectors for which we can allow any rational combination is isomorphic to the kernel of constraint_matrix. + # (8) Indeed, only this ensures that upon multiplication with any rational number, the result remains an integer. + res = (solution_matrix[:,1:r], solution_matrix[:,r+1:ncols(solution_matrix)]) + + + # (9) Remember computed data + set_attribute!(m, :well_quantized_ambient_space_models_of_g4_fluxes, res) + set_attribute!(m, :inter_dict, inter_dict) + set_attribute!(m, :s_inter_dict, s_inter_dict) + + + # (10) Finally, return the result + return res + +end + + +@doc raw""" + well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Tuple{QQMatrix, QQMatrix} + +Given an F-theory model $m$ defined as hypersurface in a simplicial and +complete toric base, this method computes a basis of all well-quantized +and vertical ambient space $G_4$-fluxes. The result of this operation is a +tuple of two matrices. The columns of the first matrix specify those (rational) +combinations of ambient space $G_4$-fluxes, of which one may only take +$\mathbb{Z}$-linear combinations without violating flux quantization. The columns +of the second matrix specify those (rational) combinations of ambient space +$G_4$-fluxes, for which any rational linear combination satisfies the flux +quantization condition. + +Crucially, this method assumes that $c_2( \widehat{Y}_4)$ is even. Currently, no +check is conducted and no error raised. Use with care! + +Recall that this is relevant in so much as the quantization condition asks to +verify if the twist of the given $G_4$-flux by $1/2 \cdot c_2( \widehat{Y}_4)$ is +even. Recall also that it is known that for many F-theory models, $c_2( \widehat{Y}_4)$ +is an even class. For instance, this applies to all F-theory compactifications +on an elliptically fibered smooth Calabi-Yau 4-fold with a globally defined +Weierstrass model [CS12](@cite). For instance, this means that all of the +F-theory QSMs [CHLLT19](@cite) have an even $c_2( \widehat{Y}_4)$. + +It can be computationally very demanding to check if a toric variety +$X$ is complete (and simplicial). The optional argument `check` can be set +to `false` to skip these tests. + +# Examples +```jldoctest +julia> B3 = projective_space(NormalToricVariety, 3) +Normal toric variety + +julia> Kbar = anticanonical_divisor_class(B3) +Divisor class on a normal toric variety + +julia> t = literature_model(arxiv_id = "1109.3454", equation = "3.1", base_space = B3, defining_classes = Dict("w"=>Kbar)) +Construction over concrete base may lead to singularity enhancement. Consider computing singular_loci. However, this may take time! + +Global Tate model over a concrete base -- SU(5)xU(1) restricted Tate model based on arXiv paper 1109.3454 Eq. (3.1) + +julia> res = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(t, check = false); + +julia> res[1] +2 by 0 empty matrix + +julia> res[2] +2 by 0 empty matrix +``` + +Here is a more interesting example, in which we verify with our software tool for one particular F-theory QSM, that the +choice of $G_4$-flux presented in [CHLLT19](@cite), is indeed vertical and satisfies the necessary conditions +for being well-quantized. + +```jldoctest; setup = :(Oscar.LazyArtifacts.ensure_artifact_installed("QSMDB", Oscar.LazyArtifacts.find_artifacts_toml(Oscar.oscardir))) +julia> qsm_model = literature_model(arxiv_id = "1903.00009", model_parameters = Dict("k" => 2021)) +Hypersurface model over a concrete base + +julia> res = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(qsm_model, check = false); + +julia> size(res[1]) +(37, 25) + +julia> size(res[2]) +(37, 0) + +julia> M=res[1]; + +julia> g4_base = ambient_space_models_of_g4_fluxes(qsm_model, check = false); + +julia> g4_classes = [sum(M[i,j]*g4_base[i] for i in 1:length(g4_base)) for j in 1:size(M,2)]; + +julia> length(g4_classes) == 25 +true + +julia> g4_classes[end] +Cohomology class on a normal toric variety given by 293//300*x4*e2 + 143//150*x4*u - 283//25*x4*e4 + 143//150*x4*e1 + 1643//300*x4*w - 599//150*x5*x8 - 7//150*x5*e2 - 7//75*x5*u - 7//50*x5*e4 - 7//75*x5*e1 - 89//300*x5*w + 896//75*x6*x7 + 1//20*x6*e2 + 1//10*x6*u + 2//5*x6*e4 + 1//10*x6*e1 - 1//5*x6*w - 599//75*x7*x8 - 7//75*x7*e2 - 14//75*x7*u - 7//25*x7*e4 - 14//75*x7*e1 - 89//150*x7*w + 208//75*x8^2 + 298//75*x8*x9 + 1//150*x8*e2 - 73//150*x8*u - 12//25*x8*e4 - 73//150*x8*e1 - 37//75*x8*w + 82//75*x9^2 - 7//150*x9*e2 - 7//75*x9*u + 9//25*x9*e4 - 7//75*x9*e1 - 41//75*x9*w + 11//30*e1*w + +julia> g4_list = [g4_flux(qsm_model, cl, check = false) for cl in g4_classes]; + +julia> all(k -> passes_elementary_quantization_checks(k), g4_list) +true + +julia> all(k -> passes_verticality_checks(k), g4_list) +true + +julia> c = [60, 51, 90, 0, 24, 51, -24, 45, 30, 0, -48, 90, -57, 60, 30, 15, 120, 0, -60, 0, -720, -420, -270, -60, -2190]; + +julia> qsm_g4_candidate = g4_flux(qsm_model, sum(c[i]*g4_classes[i] for i in 1:length(g4_classes)), check = false) +G4-flux candidate lacking elementary quantization checks + +julia> passes_elementary_quantization_checks(qsm_g4_candidate) +true + +julia> passes_verticality_checks(qsm_g4_candidate) +true +``` +""" +function well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Tuple{QQMatrix, QQMatrix} + + # (1) Entry checks + @req base_space(m) isa NormalToricVariety "Computation of well-quantized G4-fluxes only supported for toric base and ambient spaces" + @req dim(ambient_space(m)) == 5 "Computation of well-quantized G4-fluxes only supported for 5-dimensional toric ambient spaces" + if check + @req is_complete(ambient_space(m)) "Computation of well-quantized G4-fluxes only supported for complete toric ambient spaces" + @req is_simplicial(ambient_space(m)) "Computation of well-quantized G4-fluxes only supported for simplicial toric ambient space" + end + if has_attribute(m, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes) + return get_attribute(m, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes) + end + + + # (2) Compute data, that is frequently used by the sophisticated intersection product below + S = cox_ring(ambient_space(m)) + gS = gens(cox_ring(ambient_space(m))) + linear_relations = matrix(QQ, matrix(ZZ, rays(ambient_space(m)))) + scalings = [c.coeff for c in S.d] + mnf = Oscar._minimal_nonfaces(ambient_space(m)) + sr_ideal_pos = [Vector{Int}(Polymake.row(mnf, i)) for i in 1:Polymake.nrows(mnf)] + data = ( + S = S, + gS = gS, + linear_relations = linear_relations, + scalings = scalings, + sr_ideal_pos = sr_ideal_pos + ) + + + # (3) Are intersection numbers known? + inter_dict = Dict{NTuple{4, Int64}, ZZRingElem}() + s_inter_dict = Dict{String, ZZRingElem}() + if has_attribute(m, :inter_dict) + inter_dict = get_attribute(m, :inter_dict) + end + if has_attribute(m, :s_inter_dict) + s_inter_dict = get_attribute(m, :s_inter_dict) + end + + + # (4) Obtain critical information - this may take significant time! + ambient_space_flux_candidates_basis = ambient_space_models_of_g4_fluxes(m, check = check) + list_of_base_divisor_pairs_to_be_considered = Oscar._ambient_space_base_divisor_pairs_to_be_considered(m) + ambient_space_flux_candidates_basis_indices = get_attribute(m, :ambient_space_models_of_g4_fluxes_indices) + list_of_divisor_pairs_to_be_considered = Oscar._ambient_space_divisor_pairs_to_be_considered(m) + + + # (5) Work out the relevant intersection numbers to tell if a flux is vertical + vertical_constraint_matrix = Vector{Vector{QQFieldElem}}() + # I have prepared some functionality below, regarding the case that this matrix should have rational entries. + # However, I expect that this will not happen as long as the hypersurface in question is smooth. + if arxiv_doi(m) == "10.48550/arXiv.1511.03209" + + # Use special intersection theory for special F-theory model. This technology could be extended beyond this one use-case in the future. + for i in 1:length(ambient_space_flux_candidates_basis) + + condition = Vector{ZZRingElem}() + + # Compute against pairs of base divisors + for j in 1:length(list_of_base_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_base_divisor_pairs_to_be_considered[j]...])) + push!(condition, sophisticated_intersection_product(ambient_space(m), my_tuple, hypersurface_equation(m), inter_dict, s_inter_dict, data)) + end + + # Compute against zero section and base divisor + pos_zero_section = findfirst(x -> x == "z", string.(gS)) + for j in 1:n_rays(base_space(m)) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., [j, pos_zero_section]...])) + push!(condition, sophisticated_intersection_product(ambient_space(m), my_tuple, hypersurface_equation(m), inter_dict, s_inter_dict, data)) + end + + push!(vertical_constraint_matrix, condition) + + end + + else + + # Cover all other case with generic, but potentially painfully slow methodology. + tds = torusinvariant_prime_divisors(ambient_space(m)) + cds = [cohomology_class(tds[i]) for i in 1:length(tds)] + pt_class = cohomology_class(anticanonical_divisor_class(ambient_space(m))) + for i in 1:length(ambient_space_flux_candidates_basis) + + condition = Vector{ZZRingElem}() + + # Compute against pairs of base divisors + for j in 1:length(list_of_base_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_base_divisor_pairs_to_be_considered[j]...])) + if !haskey(inter_dict, my_tuple) + class = ambient_space_flux_candidates_basis[i] * cds[list_of_base_divisor_pairs_to_be_considered[j][1]] * cds[list_of_base_divisor_pairs_to_be_considered[j][2]] * pt_class + inter_dict[my_tuple] = integrate(class) + end + push!(condition, inter_dict[my_tuple]) + end + + # Compute against zero section and base divisor + zsc = zero_section_class(m) + pos_zero_section = findfirst(x -> x == string(polynomial(zsc)), string.([polynomial(x) for x in cds])) + @req pos_zero_section !== nothing && pos_zero_section >= 1 "Could not establish position of the zero section" + for j in 1:n_rays(base_space(m)) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., [j, pos_zero_section]...])) + if !haskey(inter_dict, my_tuple) + class = ambient_space_flux_candidates_basis[i] * cds[j] * zsc * pt_class + inter_dict[my_tuple] = integrate(class) + end + push!(condition, inter_dict[my_tuple]) + + end + + push!(vertical_constraint_matrix, condition) + + end + + end + + + # (6) Compute the vertical fluxes as the kernel of the vertical_constraint_matrix. + # (6) To later tell if those fluxes are properly quantized, we want to parametrize them with integer coefficient only. + denom = lcm(unique(vcat([denominator.(k) for k in vertical_constraint_matrix]...))) + if denom != 1 + vertical_constraint_matrix = denom * vertical_constraint_matrix + end + C_vertical = transpose(matrix(ZZ, vertical_constraint_matrix)) + vertical_fluxes = nullspace(C_vertical)[2] + + + # (7) Work out the relevant intersection numbers to tell if a flux is well quantized + quant_constraint_matrix = Vector{Vector{QQFieldElem}}() + # I have prepared some functionality below, regarding the case that this matrix should have rational entries. + # However, I expect that this will not happen as long as the hypersurface in question is smooth. + if arxiv_doi(m) == "10.48550/arXiv.1511.03209" + + # Use special intersection theory for special F-theory model. This technology could be extended beyond this one use-case in the future. + for i in 1:length(ambient_space_flux_candidates_basis) + condition = Vector{ZZRingElem}() + for j in 1:length(list_of_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_divisor_pairs_to_be_considered[j]...])) + push!(condition, sophisticated_intersection_product(ambient_space(m), my_tuple, hypersurface_equation(m), inter_dict, s_inter_dict, data)) + end + push!(quant_constraint_matrix, condition) + end + + else + + # Cover all other case with generic, but potentially painfully slow methodology. + tds = torusinvariant_prime_divisors(ambient_space(m)) + cds = [cohomology_class(tds[i]) for i in 1:length(tds)] + pt_class = cohomology_class(anticanonical_divisor_class(ambient_space(m))) + for i in 1:length(ambient_space_flux_candidates_basis) + condition = Vector{ZZRingElem}() + for j in 1:length(list_of_divisor_pairs_to_be_considered) + my_tuple = Tuple(sort([ambient_space_flux_candidates_basis_indices[i]..., list_of_divisor_pairs_to_be_considered[j]...])) + if !haskey(inter_dict, my_tuple) + class = ambient_space_flux_candidates_basis[i] * cds[list_of_divisor_pairs_to_be_considered[j][1]] * cds[list_of_divisor_pairs_to_be_considered[j][2]] * pt_class + inter_dict[my_tuple] = integrate(class) + end + push!(condition, inter_dict[my_tuple]) + end + push!(quant_constraint_matrix, condition) + end + + end + + # (8) Convert the quant_constraint_matrix to a ZZ matrix. If necessary, multiply it by a suitable integer. + denom = lcm(unique(vcat([denominator.(k) for k in quant_constraint_matrix]...))) + if denom != 1 + quant_constraint_matrix = denom * quant_constraint_matrix + end + C = transpose(matrix(ZZ, quant_constraint_matrix)) + + + # (9) Work out the well-quantized fluxes as linear combinations of the parametrization of the vertical fluxes + C2 = C * vertical_fluxes # This is a ZZ-matrix, since we parametrize vertical fluxes with integer coefficients! + S, T, U = snf_with_transform(C2) + r = rank(S) + @req all(k -> !is_zero(S[k,k]), 1:r) "Inconsistency in Smith normal form detected. Please inform the authors." + @req all(k -> is_zero(S[k,k]), r+1:min(nrows(S), ncols(S))) "Inconsistency in Smith normal form detected. Please inform the authors." + S_prime = zero_matrix(QQ, ncols(S), ncols(S)) + for k in 1:min(nrows(S), ncols(S)) + if k <= r + S_prime[k,k] = denom//S[k,k] + else + S_prime[k,k] = 1 + end + end + solution_matrix = U * S_prime + + + # (10) Finally, we need to re-express those in terms of the original bases. + # (10) Rather, we have res now expressed in terms of the basis of vertical fluxes... + sol_mat = vertical_fluxes * solution_matrix + res = (sol_mat[:,1:r], sol_mat[:,r+1:ncols(solution_matrix)]) + + + # (11) Remember computed data + set_attribute!(m, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes, res) + set_attribute!(m, :inter_dict, inter_dict) + set_attribute!(m, :s_inter_dict, s_inter_dict) + + + # (12) Finally, return the result + return res + +end diff --git a/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl b/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl index f2482c1ff8d7..ae432a8165bc 100644 --- a/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl +++ b/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl @@ -56,6 +56,9 @@ function save_object(s::SerializerState, h::HypersurfaceModel) attrs_dict[key] = value end end + + + # Save resolutions, if they are known. if has_resolutions(h) res = resolutions(h) resolution_loci = [k[1] for k in res] @@ -63,6 +66,47 @@ function save_object(s::SerializerState, h::HypersurfaceModel) attrs_dict[:resolution_loci] = resolution_loci attrs_dict[:exceptional_divisors] = exceptional_divisors end + + # Have intersection numbers been computed? + if has_attribute(h, :inter_dict) + attrs_dict[:inter_dict] = get_attribute(h, :inter_dict) + end + + # Have special intersections been remembered? + if has_attribute(h, :s_inter_dict) + attrs_dict[:s_inter_dict] = get_attribute(h, :s_inter_dict) + end + + # Do we know which pairs of ambient space divisors intersect non-trivially with the hypersurface? + if has_attribute(h, :_ambient_space_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_divisor_pairs_to_be_considered] = _ambient_space_divisor_pairs_to_be_considered(h) + end + + # Do we know which pairs of ambient space base divisors intersect non-trivially with the hypersurface? + if has_attribute(h, :_ambient_space_base_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_base_divisor_pairs_to_be_considered] = _ambient_space_base_divisor_pairs_to_be_considered(h) + end + + # Do we know ambient space models for g4-fluxes? + if has_attribute(h, :ambient_space_models_of_g4_fluxes_indices) + attrs_dict[:g4_flux_tuple_list] = get_attribute(h, :ambient_space_models_of_g4_fluxes_indices) + end + + # Do we know the well-quantized G4-fluxes + if has_attribute(h, :well_quantized_ambient_space_models_of_g4_fluxes) + res = well_quantized_ambient_space_models_of_g4_fluxes(h, check = false) + attrs_dict[:well_quantized_integral] = res[1] + attrs_dict[:well_quantized_rational] = res[2] + end + + # Do we know the well-quantized and vertical G4-fluxes + if has_attribute(h, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes) + res = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(h, check = false) + attrs_dict[:well_quantized_and_vertical_integral] = res[1] + attrs_dict[:well_quantized_and_vertical_rational] = res[2] + end + + # Save all of the above data... !isempty(attrs_dict) && save_typed_object(s, attrs_dict, :__attrs) end end @@ -80,18 +124,83 @@ function load_object(s::DeserializerState, ::Type{<:HypersurfaceModel}, params:: defining_classes = haskey(s, :defining_classes) ? load_typed_object(s, :defining_classes) : Dict{String, ToricDivisorClass}() model = HypersurfaceModel(explicit_model_sections, defining_equation_parametrization, defining_equation, base_space, amb_space, fiber_ambient_space) model.defining_classes = defining_classes + @req cox_ring(ambient_space(model)) == parent(hypersurface_equation(model)) "Hypersurface polynomial not in Cox ring of toric ambient space" + + # Set "generic" attributes attrs_data = haskey(s, :__attrs) ? load_typed_object(s, :__attrs) : Dict{Symbol, Any}() for (key, value) in attrs_data if (key != :resolution_loci) && (key != :exceptional_divisors) set_attribute!(model, Symbol(key), value) end end + + # Resolution loci known? If so, set them. if haskey(attrs_data, :resolution_loci) resolution_loci = attrs_data[:resolution_loci] exceptional_divisors = attrs_data[:exceptional_divisors] @req length(exceptional_divisors) == length(exceptional_divisors) "Inconsistency upon loading resolutions" set_attribute!(model, :resolutions, [[resolution_loci[i], exceptional_divisors[i]] for i in 1:length(resolution_loci)]) end - @req cox_ring(ambient_space(model)) == parent(hypersurface_equation(model)) "Hypersurface polynomial not in Cox ring of toric ambient space" + + # Some intersection numbers known? That is, is it known that the intersection of the toric divisors + # (i1, i2, i3, i4) with the hypersurface consists of k points (counted with multiplicity)? If so, set it. + # That is, we have a dictionary which assigns the tuple (i1, i2, i3, i4) - sorted in ascending order - the + # intersection number k. In general, k could be a rational number! Cf. orbifold singularities. + if haskey(attrs_data, :inter_dict) + set_attribute!(model, :inter_dict, attrs_data[:inter_dict]) + end + + # Some special intersection numbers known? If we intersect the toric divisors + # (i1, i2, i3, i4) with the hypersurface p, then we can set a number of variables to 1 + # by the scaling relations, which simplifies the hypersurface. The remaining variables + # are subject to a number of remaining SR-ideal generators and some remaining scaling + # relations. The special intersection algorithm remembers the intersection points of such + # a locus in a dictionary. The key in this dictionary is the string formed from + # (simplified_hypersurface, remaining variables, remaining scaling relations, remaining SR-generators) + # and its value is the number of intersection points. If such information is known, + # then set it. + if haskey(attrs_data, :s_inter_dict) + set_attribute!(model, :s_inter_dict, attrs_data[:s_inter_dict]) + end + + # For the computation off all well-quantized G4-fluxes we compute intersection numbers in the toric + # ambient space. Specifically, we intersect the ambient space G4-flux candidates (a product of algebraic cycles, + # each of which is associated to a toric divisor) with another pair of (algebraic cycles associated to) toric + # divisors and the hypersurface. Of course, only certain pairs of toric divisors restrict non-trivially + # to the CY-hypersurface in question. We make a selection of divisor pairs that (likely - we only make a naive + # test regarding a non-trivial restriction) restrict non-trivially to the hypersurface. If the list of those + # toric divisor pairs is known, set it. + if haskey(attrs_data, :_ambient_space_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_divisor_pairs_to_be_considered]) + end + if haskey(attrs_data, :_ambient_space_base_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_base_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_base_divisor_pairs_to_be_considered]) + end + + # Likewise, there are only so many ambient space G4-flux candidates. If those are known, set them. + # In the serialization, we remember only the integer tuples that tell us which toric divisors to consider, + # as this saves memory. If known, set this data and recompute the corresponding cohomology classes and + # set those classes as well. + if haskey(attrs_data, :g4_flux_tuple_list) + tuple_list = attrs_data[:g4_flux_tuple_list] + S = cohomology_ring(amb_space, check = false) + c_ds = [k.f for k in gens(S)] + ambient_space_models_of_g4_fluxes = [cohomology_class(amb_space, MPolyQuoRingElem(c_ds[my_tuple[1]]*c_ds[my_tuple[2]], S)) for my_tuple in tuple_list] + set_attribute!(model, :ambient_space_models_of_g4_fluxes, ambient_space_models_of_g4_fluxes) + set_attribute!(model, :ambient_space_models_of_g4_fluxes_indices, tuple_list) + end + + # Are the well-quanitzed G4-fluxes known? + if haskey(attrs_data, :well_quantized_integral) && haskey(attrs_data, :well_quantized_rational) + quant_tuple = (attrs_data[:well_quantized_integral], attrs_data[:well_quantized_rational]) + set_attribute!(model, :well_quantized_ambient_space_models_of_g4_fluxes, quant_tuple) + end + + # Are the well-quantized and vertical G4-fluxes known? + if haskey(attrs_data, :well_quantized_and_vertical_integral) && haskey(attrs_data, :well_quantized_and_vertical_rational) + quant_tuple = (attrs_data[:well_quantized_and_vertical_integral], attrs_data[:well_quantized_and_vertical_rational]) + set_attribute!(model, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes, quant_tuple) + end + return model end diff --git a/experimental/FTheoryTools/src/Serialization/tate_models.jl b/experimental/FTheoryTools/src/Serialization/tate_models.jl index 5fe46f4e7801..d55a4436ddb5 100644 --- a/experimental/FTheoryTools/src/Serialization/tate_models.jl +++ b/experimental/FTheoryTools/src/Serialization/tate_models.jl @@ -51,12 +51,15 @@ function save_object(s::SerializerState, gtm::GlobalTateModel) !isempty(data) && save_typed_object(s, data, key) end save_object(s, tate_polynomial(gtm), :tate_polynomial) + attrs_dict = Dict{Symbol, Any}() for (key, value) in gtm.__attrs if value isa String || value isa Vector{String} || value isa Bool attrs_dict[key] = value end end + + # Save resolutions, if they are known. if has_resolutions(gtm) res = resolutions(gtm) resolution_loci = [k[1] for k in res] @@ -64,8 +67,50 @@ function save_object(s::SerializerState, gtm::GlobalTateModel) attrs_dict[:resolution_loci] = resolution_loci attrs_dict[:exceptional_divisors] = exceptional_divisors end + + # Have intersection numbers been computed? + if has_attribute(gtm, :inter_dict) + attrs_dict[:inter_dict] = get_attribute(gtm, :inter_dict) + end + + # Have special intersections been remembered? + if has_attribute(gtm, :s_inter_dict) + attrs_dict[:s_inter_dict] = get_attribute(gtm, :s_inter_dict) + end + + # Do we know which pairs of ambient space divisors intersect non-trivially with the hypersurface? + if has_attribute(gtm, :_ambient_space_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_divisor_pairs_to_be_considered] = _ambient_space_divisor_pairs_to_be_considered(gtm) + end + + # Do we know which pairs of ambient space base divisors intersect non-trivially with the hypersurface? + if has_attribute(gtm, :_ambient_space_base_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_base_divisor_pairs_to_be_considered] = _ambient_space_base_divisor_pairs_to_be_considered(gtm) + end + + # Do we know ambient space models for g4-fluxes? + if has_attribute(gtm, :ambient_space_models_of_g4_fluxes_indices) + attrs_dict[:g4_flux_tuple_list] = get_attribute(gtm, :ambient_space_models_of_g4_fluxes_indices) + end + + # Do we know the well-quantized G4-fluxes + if has_attribute(gtm, :well_quantized_ambient_space_models_of_g4_fluxes) + res = well_quantized_ambient_space_models_of_g4_fluxes(gtm, check = false) + attrs_dict[:well_quantized_integral] = res[1] + attrs_dict[:well_quantized_rational] = res[2] + end + + # Do we know the well-quantized and vertical G4-fluxes + if has_attribute(gtm, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes) + res = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(gtm, check = false) + attrs_dict[:well_quantized_and_vertical_integral] = res[1] + attrs_dict[:well_quantized_and_vertical_rational] = res[2] + end + + # Save all of the above data... !isempty(attrs_dict) && save_typed_object(s, attrs_dict, :__attrs) end + end @@ -81,18 +126,82 @@ function load_object(s::DeserializerState, ::Type{<: GlobalTateModel}, params::T defining_classes = haskey(s, :defining_classes) ? load_typed_object(s, :defining_classes) : Dict{String, ToricDivisorClass}() model = GlobalTateModel(explicit_model_sections, defining_section_parametrization, pt, base_space, amb_space) model.defining_classes = defining_classes + @req cox_ring(ambient_space(model)) == parent(tate_polynomial(model)) "Tate polynomial not in Cox ring of toric ambient space" + + # Set "generic" attributes attrs_data = haskey(s, :__attrs) ? load_typed_object(s, :__attrs) : Dict{Symbol, Any}() for (key, value) in attrs_data if (key != :resolution_loci) && (key != :exceptional_divisors) set_attribute!(model, Symbol(key), value) end end + + # Resolution loci known? If so, set them. if haskey(attrs_data, :resolution_loci) resolution_loci = attrs_data[:resolution_loci] exceptional_divisors = attrs_data[:exceptional_divisors] @req length(exceptional_divisors) == length(exceptional_divisors) "Inconsistency upon loading resolutions" set_attribute!(model, :resolutions, [[resolution_loci[i], exceptional_divisors[i]] for i in 1:length(resolution_loci)]) end - @req cox_ring(ambient_space(model)) == parent(tate_polynomial(model)) "Tate polynomial not in Cox ring of toric ambient space" + + # Some intersection numbers known? That is, is it known that the intersection of the toric divisors + # (i1, i2, i3, i4) with the hypersurface consists of k points (counted with multiplicity)? If so, set it. + # That is, we have a dictionary which assigns the tuple (i1, i2, i3, i4) - sorted in ascending order - the + # intersection number k. In general, k could be a rational number! Cf. orbifold singularities. + if haskey(attrs_data, :inter_dict) + set_attribute!(model, :inter_dict, attrs_data[:inter_dict]) + end + + # Some special intersection numbers known? If we intersect the toric divisors + # (i1, i2, i3, i4) with the hypersurface p, then we can set a number of variables to 1 + # by the scaling relations, which simplifies the hypersurface. The remaining variables + # are subject to a number of remaining SR-ideal generators and some remaining scaling + # relations. The special intersection algorithm remembers the intersection points of such + # a locus in a dictionary. The key in this dictionary is the string formed from + # (simplified_hypersurface, remaining variables, remaining scaling relations, remaining SR-generators) + # and its value is the number of intersection points. If such information is known, + # then set it. + if haskey(attrs_data, :s_inter_dict) + set_attribute!(model, :s_inter_dict, attrs_data[:s_inter_dict]) + end + + # For the computation off all well-quantized G4-fluxes we compute intersection numbers in the toric + # ambient space. Specifically, we intersect the ambient space G4-flux candidates (a product of algebraic cycles, + # each of which is associated to a toric divisor) with another pair of (algebraic cycles associated to) toric + # divisors and the hypersurface. Of course, only certain pairs of toric divisors restrict non-trivially + # to the CY-hypersurface in question. We make a selection of divisor pairs that (likely - we only make a naive + # test regarding a non-trivial restriction) restrict non-trivially to the hypersurface. If the list of those + # toric divisor pairs is known, set it. + if haskey(attrs_data, :_ambient_space_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_divisor_pairs_to_be_considered]) + end + if haskey(attrs_data, :_ambient_space_base_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_base_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_base_divisor_pairs_to_be_considered]) + end + + # Likewise, there are only so many ambient space G4-flux candidates. If those are known, set them. + # In the serialization, we remember only the integer tuples that tell us which toric divisors to consider, + # as this saves memory. If known, recompute the corresponding cohomology classes and set the attribute. + if haskey(attrs_data, :g4_flux_tuple_list) + tuple_list = attrs_data[:g4_flux_tuple_list] + S = cohomology_ring(amb_space, check = false) + c_ds = [k.f for k in gens(S)] + ambient_space_models_of_g4_fluxes = [cohomology_class(amb_space, MPolyQuoRingElem(c_ds[my_tuple[1]]*c_ds[my_tuple[2]], S)) for my_tuple in tuple_list] + set_attribute!(model, :ambient_space_models_of_g4_fluxes, ambient_space_models_of_g4_fluxes) + set_attribute!(model, :ambient_space_models_of_g4_fluxes_indices, tuple_list) + end + + # Are the well-quanitzed G4-fluxes known? + if haskey(attrs_data, :well_quantized_integral) && haskey(attrs_data, :well_quantized_rational) + quant_tuple = (attrs_data[:well_quantized_integral], attrs_data[:well_quantized_rational]) + set_attribute!(model, :well_quantized_ambient_space_models_of_g4_fluxes, quant_tuple) + end + + # Are the well-quantized and vertical G4-fluxes known? + if haskey(attrs_data, :well_quantized_and_vertical_integral) && haskey(attrs_data, :well_quantized_and_vertical_rational) + quant_tuple = (attrs_data[:well_quantized_and_vertical_integral], attrs_data[:well_quantized_and_vertical_rational]) + set_attribute!(model, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes, quant_tuple) + end + return model end diff --git a/experimental/FTheoryTools/src/Serialization/weierstrass_models.jl b/experimental/FTheoryTools/src/Serialization/weierstrass_models.jl index 799ecbf7a60c..63b18b5ac484 100644 --- a/experimental/FTheoryTools/src/Serialization/weierstrass_models.jl +++ b/experimental/FTheoryTools/src/Serialization/weierstrass_models.jl @@ -57,6 +57,8 @@ function save_object(s::SerializerState, w::WeierstrassModel) attrs_dict[key] = value end end + + # Save resolutions, if they are known. if has_resolutions(w) res = resolutions(w) resolution_loci = [k[1] for k in res] @@ -64,6 +66,47 @@ function save_object(s::SerializerState, w::WeierstrassModel) attrs_dict[:resolution_loci] = resolution_loci attrs_dict[:exceptional_divisors] = exceptional_divisors end + + # Have intersection numbers been computed? + if has_attribute(w, :inter_dict) + attrs_dict[:inter_dict] = get_attribute(w, :inter_dict) + end + + # Have special intersections been remembered? + if has_attribute(w, :s_inter_dict) + attrs_dict[:s_inter_dict] = get_attribute(w, :s_inter_dict) + end + + # Do we know which pairs of ambient space divisors intersect non-trivially with the hypersurface? + if has_attribute(w, :_ambient_space_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_divisor_pairs_to_be_considered] = _ambient_space_divisor_pairs_to_be_considered(w) + end + + # Do we know which pairs of ambient space base divisors intersect non-trivially with the hypersurface? + if has_attribute(w, :_ambient_space_base_divisor_pairs_to_be_considered) + attrs_dict[:_ambient_space_base_divisor_pairs_to_be_considered] = _ambient_space_base_divisor_pairs_to_be_considered(w) + end + + # Do we know ambient space models for g4-fluxes? + if has_attribute(w, :ambient_space_models_of_g4_fluxes_indices) + attrs_dict[:g4_flux_tuple_list] = get_attribute(w, :ambient_space_models_of_g4_fluxes_indices) + end + + # Do we know the well-quantized G4-fluxes + if has_attribute(w, :well_quantized_ambient_space_models_of_g4_fluxes) + res = well_quantized_ambient_space_models_of_g4_fluxes(w, check = false) + attrs_dict[:well_quantized_integral] = res[1] + attrs_dict[:well_quantized_rational] = res[2] + end + + # Do we know the well-quantized and vertical G4-fluxes + if has_attribute(w, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes) + res = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(w, check = false) + attrs_dict[:well_quantized_and_vertical_integral] = res[1] + attrs_dict[:well_quantized_and_vertical_rational] = res[2] + end + + # Save all of the above data... !isempty(attrs_dict) && save_typed_object(s, attrs_dict, :__attrs) end end @@ -81,18 +124,83 @@ function load_object(s::DeserializerState, ::Type{<: WeierstrassModel}, params:: defining_classes = haskey(s, :defining_classes) ? load_typed_object(s, :defining_classes) : Dict{String, ToricDivisorClass}() model = WeierstrassModel(explicit_model_sections, defining_section_parametrization, pw, base_space, amb_space) model.defining_classes = defining_classes + @req cox_ring(ambient_space(model)) == parent(weierstrass_polynomial(model)) "Weierstrass polynomial not in Cox ring of toric ambient space" + + # Set "generic" attributes attrs_data = haskey(s, :__attrs) ? load_typed_object(s, :__attrs) : Dict{Symbol, Any}() for (key, value) in attrs_data if (key != :resolution_loci) && (key != :exceptional_divisors) set_attribute!(model, Symbol(key), value) end end + + # Resolution loci known? If so, set them. if haskey(attrs_data, :resolution_loci) resolution_loci = attrs_data[:resolution_loci] exceptional_divisors = attrs_data[:exceptional_divisors] @req length(exceptional_divisors) == length(exceptional_divisors) "Inconsistency upon loading resolutions" set_attribute!(model, :resolutions, [[resolution_loci[i], exceptional_divisors[i]] for i in 1:length(resolution_loci)]) end - @req cox_ring(ambient_space(model)) == parent(weierstrass_polynomial(model)) "Weierstrass polynomial not in Cox ring of toric ambient space" + + # Some intersection numbers known? That is, is it known that the intersection of the toric divisors + # (i1, i2, i3, i4) with the hypersurface consists of k points (counted with multiplicity)? If so, set it. + # That is, we have a dictionary which assigns the tuple (i1, i2, i3, i4) - sorted in ascending order - the + # intersection number k. In general, k could be a rational number! Cf. orbifold singularities. + if haskey(attrs_data, :inter_dict) + set_attribute!(model, :inter_dict, attrs_data[:inter_dict]) + end + + # Some special intersection numbers known? If we intersect the toric divisors + # (i1, i2, i3, i4) with the hypersurface p, then we can set a number of variables to 1 + # by the scaling relations, which simplifies the hypersurface. The remaining variables + # are subject to a number of remaining SR-ideal generators and some remaining scaling + # relations. The special intersection algorithm remembers the intersection points of such + # a locus in a dictionary. The key in this dictionary is the string formed from + # (simplified_hypersurface, remaining variables, remaining scaling relations, remaining SR-generators) + # and its value is the number of intersection points. If such information is known, + # then set it. + if haskey(attrs_data, :s_inter_dict) + set_attribute!(model, :s_inter_dict, attrs_data[:s_inter_dict]) + end + + # For the computation off all well-quantized G4-fluxes we compute intersection numbers in the toric + # ambient space. Specifically, we intersect the ambient space G4-flux candidates (a product of algebraic cycles, + # each of which is associated to a toric divisor) with another pair of (algebraic cycles associated to) toric + # divisors and the hypersurface. Of course, only certain pairs of toric divisors restrict non-trivially + # to the CY-hypersurface in question. We make a selection of divisor pairs that (likely - we only make a naive + # test regarding a non-trivial restriction) restrict non-trivially to the hypersurface. If the list of those + # toric divisor pairs is known, set it. + if haskey(attrs_data, :_ambient_space_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_divisor_pairs_to_be_considered]) + end + if haskey(attrs_data, :_ambient_space_base_divisor_pairs_to_be_considered) + set_attribute!(model, :_ambient_space_base_divisor_pairs_to_be_considered, attrs_data[:_ambient_space_base_divisor_pairs_to_be_considered]) + end + + # Likewise, there are only so many ambient space G4-flux candidates. If those are known, set them. + # In the serialization, we remember only the integer tuples that tell us which toric divisors to consider, + # as this saves memory. If known, set this data and recompute the corresponding cohomology classes and + # set those classes as well. + if haskey(attrs_data, :g4_flux_tuple_list) + tuple_list = attrs_data[:g4_flux_tuple_list] + S = cohomology_ring(amb_space, check = false) + c_ds = [k.f for k in gens(S)] + ambient_space_models_of_g4_fluxes = [cohomology_class(amb_space, MPolyQuoRingElem(c_ds[my_tuple[1]]*c_ds[my_tuple[2]], S)) for my_tuple in tuple_list] + set_attribute!(model, :ambient_space_models_of_g4_fluxes, ambient_space_models_of_g4_fluxes) + set_attribute!(model, :ambient_space_models_of_g4_fluxes_indices, tuple_list) + end + + # Are the well-quanitzed G4-fluxes known? + if haskey(attrs_data, :well_quantized_integral) && haskey(attrs_data, :well_quantized_rational) + quant_tuple = (attrs_data[:well_quantized_integral], attrs_data[:well_quantized_rational]) + set_attribute!(model, :well_quantized_ambient_space_models_of_g4_fluxes, quant_tuple) + end + + # Are the well-quantized and vertical G4-fluxes known? + if haskey(attrs_data, :well_quantized_and_vertical_integral) && haskey(attrs_data, :well_quantized_and_vertical_rational) + quant_tuple = (attrs_data[:well_quantized_and_vertical_integral], attrs_data[:well_quantized_and_vertical_rational]) + set_attribute!(model, :well_quantized_and_vertical_ambient_space_models_of_g4_fluxes, quant_tuple) + end + return model end diff --git a/experimental/FTheoryTools/src/exports.jl b/experimental/FTheoryTools/src/exports.jl index 6a3f2b2acd41..e57829e4a0ae 100644 --- a/experimental/FTheoryTools/src/exports.jl +++ b/experimental/FTheoryTools/src/exports.jl @@ -217,5 +217,7 @@ export weighted_resolution_generating_sections export weighted_resolution_zero_sections export weighted_resolutions export weights +export well_quantized_ambient_space_models_of_g4_fluxes +export well_quantized_and_vertical_ambient_space_models_of_g4_fluxes export zero_section export zero_section_class diff --git a/experimental/FTheoryTools/test/g4s.jl b/experimental/FTheoryTools/test/g4s.jl new file mode 100644 index 000000000000..8505590ca838 --- /dev/null +++ b/experimental/FTheoryTools/test/g4s.jl @@ -0,0 +1,45 @@ +############################################################## +# 1: Compute well-quantized fluxes and make consistency checks +############################################################## + +qsm_model = literature_model(arxiv_id = "1903.00009", model_parameters = Dict("k" => 2021)) +g4_base = ambient_space_models_of_g4_fluxes(qsm_model, check = false) +res = well_quantized_ambient_space_models_of_g4_fluxes(qsm_model, check = false) +M=res[1] +g4_classes = [sum(M[i,j]*g4_base[i] for i in 1:length(g4_base)) for j in 1:size(M,2)] +g4_list = [g4_flux(qsm_model, cl, check = false) for cl in g4_classes] +g4_dummy = g4_flux(qsm_model, sum(rand(-100:100)*g for g in g4_classes), check = false) + +@testset "Execute well-quantized tests" begin + @test length(g4_base) == 37 + @test nrows(res[1]) == 37 + @test ncols(res[1]) == 37 + @test nrows(res[2]) == 37 + @test ncols(res[2]) == 0 + @test all(k -> passes_elementary_quantization_checks(k), g4_list) == true + @test passes_elementary_quantization_checks(g4_dummy) +end + + + +############################################################## +# 2: Compute well-quantized and vertical fluxes +############################################################## + +res2 = well_quantized_and_vertical_ambient_space_models_of_g4_fluxes(qsm_model, check = false) +M2=res2[1] +g4_classes = [sum(M2[i,j]*g4_base[i] for i in 1:length(g4_base)) for j in 1:size(M2,2)] +g4_list = [g4_flux(qsm_model, cl, check = false) for cl in g4_classes] +g4_dummy = g4_flux(qsm_model, sum(rand(-100:100)*g for g in g4_classes), check = false) + +@testset "Execute well-quantized tests" begin + @test length(g4_base) == 37 + @test nrows(res2[1]) == 37 + @test ncols(res2[1]) == 25 + @test nrows(res2[2]) == 37 + @test ncols(res2[2]) == 0 + @test all(k -> passes_elementary_quantization_checks(k), g4_list) == true + @test all(k -> passes_verticality_checks(k), g4_list) == true + @test passes_elementary_quantization_checks(g4_dummy) + @test passes_verticality_checks(g4_dummy) +end