Skip to content

Commit

Permalink
Banff presentation rebased (oscar-system#3898)
Browse files Browse the repository at this point in the history
* Make kernel computation more coherent.

* Switch to pushforward in production for prime ideal sheaves.

* Improve heuristics for colength computations.

* Fix heuristic for pullback computations.

* Fix faulty production of ideals.

* Add algorithm keyword and tests.
  • Loading branch information
HechtiDerLachs committed Jul 1, 2024
1 parent 5d4e352 commit a760cbb
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 26 deletions.
42 changes: 32 additions & 10 deletions experimental/Schemes/src/IdealSheaves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,11 @@ function produce_object(F::AbsIdealSheaf, U::AbsAffineScheme)
end

### PrimeIdealSheafFromChart
function produce_object(F::PrimeIdealSheafFromChart, U2::AbsAffineScheme)
function produce_object(
F::PrimeIdealSheafFromChart, U2::AbsAffineScheme;
algorithm::Symbol=:pushforward # Either :pushforward or :pullback
# This determines how to extend through the gluings.
)
# Initialize some local variables
X = scheme(F)
OOX = OO(X)
Expand Down Expand Up @@ -1620,14 +1624,29 @@ function produce_object(F::PrimeIdealSheafFromChart, U2::AbsAffineScheme)
glue = default_covering(X)[W, V2]
f, g = gluing_morphisms(glue)
if glue isa SimpleGluing || (glue isa LazyGluing && first(gluing_domains(glue)) isa PrincipalOpenSubset)
complement_equation(codomain(g)) in F(W) && return ideal(OO(U2), one(OO(U2))) # We know the ideal is prime. No need to saturate!
I2 = F(codomain(g))
I = pullback(g)(I2)
I = ideal(OO(V2), lifted_numerator.(gens(I)))
I = _iterative_saturation(I, lifted_numerator(complement_equation(domain(g))))
result = OOX(V2, U2)(ideal(OO(V2), lifted_numerator.(gens(I))))
@hassert :IdealSheaves 1 is_one(result) || is_prime(result)
return result
if algorithm == :pushforward
complement_equation(codomain(g)) in F(W) && return ideal(OO(U2), one(OO(U2))) # We know the ideal is prime. No need to saturate!
pb_f = pullback(f)::AbsLocalizedRingHom
pb_f_res = restricted_map(pb_f)
@assert domain(pb_f_res) === ambient_coordinate_ring(V2)
Q = preimage(pb_f_res, F(domain(f)))
rest = OOX(V2, U2)
result = ideal(OO(U2), rest.(gens(Q)))
@hassert :IdealSheaves 1 !isone(Q)
@hassert :IdealSheaves 1 is_prime(result)
set_attribute!(result, :is_prime=>true)
return result
elseif algorithm == :pullback
I2 = F(codomain(g))
I = pullback(g)(I2)
I = ideal(OO(V2), lifted_numerator.(gens(I)))
I = _iterative_saturation(I, lifted_numerator(complement_equation(domain(g))))
result = OOX(V2, U2)(ideal(OO(V2), lifted_numerator.(gens(I))))
@hassert :IdealSheaves 1 is_one(result) || is_prime(result)
return result
else
error("algorithm not recognized")
end
else
Z = subscheme(W, F(W))
pZ = preimage(g, Z, check=false)
Expand Down Expand Up @@ -1684,7 +1703,9 @@ function produce_object(I::PullbackIdealSheaf, U::AbsAffineScheme)
if any(x->x===U, patches(dom))
f_loc = f_cov[U]
V = codomain(f_loc)
return pullback(f_loc)(original_ideal_sheaf(I)(V))
KK = original_ideal_sheaf(I)(V)
@assert base_ring(KK) === OO(V)
return pullback(f_loc)(KK)
end

# We are in a chart below a patch in the domain covering
Expand Down Expand Up @@ -1785,6 +1806,7 @@ function cheap_sub_ideal(II::PrimeIdealSheafFromChart, U2::AbsAffineScheme)
if has_ancestor(x->(x===U2), U)
iso = _flatten_open_subscheme(U, U2) # an embedding U -> V \subseteq U2
iso_inv = inverse(iso)
P = II(U)
pb_P = pullback(iso_inv)(P)
# avoids a saturation by discarding denominators but only produces a subideal
result = ideal(OO(U2), [g for g in OO(U2).(lifted_numerator.(gens(pb_P))) if !iszero(g)])
Expand Down
2 changes: 1 addition & 1 deletion experimental/Schemes/src/MorphismFromRationalFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ function _try_randomized_pullback(phi::MorphismFromRationalFunctions, I::AbsIdea
min_var = minimum([ngens(OO(V)) for V in all_V])
all_V = [V for V in all_V if ngens(OO(V)) == min_var]
deg_bound = minimum([maximum([total_degree(lifted_numerator(g)) for g in gens(I(V))]) for V in all_V])
all_V = [V for V in all_V if minimum([total_degree(lifted_numerator(g)) for g in gens(I(V))]) == deg_bound]
all_V = [V for V in all_V if maximum([total_degree(lifted_numerator(g)) for g in gens(I(V))]) == deg_bound]
V = first(all_V)

all_U = copy(affine_charts(X))
Expand Down
123 changes: 111 additions & 12 deletions experimental/Schemes/src/WeilDivisor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -439,16 +439,27 @@ function has_dimension_leq_zero(I::AbsIdealSheaf; covering::Covering=default_cov
return true
end

function has_dimension_leq_zero(I::SumIdealSheaf; covering::Covering=default_covering(scheme(I)))
function has_dimension_leq_zero(I::SumIdealSheaf;
covering::Covering=default_covering(scheme(I)),
use_decomposition_info::Bool=true
)
J = summands(I)
k = findfirst(x->x isa PrimeIdealSheafFromChart, J)
if k !== nothing
P = J[k]
U = original_chart(P)
if !has_dimension_leq_zero(cheap_sub_ideal(I, U))
has_dimension_leq_zero(I(U)) || return false
end
end

# k = findfirst(x->x isa PrimeIdealSheafFromChart, J)
# if k !== nothing
# P = J[k]
# U = original_chart(P)
# if has_decomposition_info(covering) && use_decomposition_info
# K = ideal(OO(U), OO(U).(decomposition_info(covering)[U]))
# if !has_dimension_leq_zero(cheap_sub_ideal(I, U) + K)
# has_dimension_leq_zero(I(U) + K) || return false
# end
# else
# if !has_dimension_leq_zero(cheap_sub_ideal(I, U))
# has_dimension_leq_zero(I(U)) || return false
# end
# end
# end

common_patches = keys(object_cache(first(J)))
for JJ in J[2:end]
Expand All @@ -461,8 +472,56 @@ function has_dimension_leq_zero(I::SumIdealSheaf; covering::Covering=default_cov

all_patches = patches(covering)
for U in all_patches
if !has_dimension_leq_zero(cheap_sub_ideal(I, U))
has_dimension_leq_zero(I(U)) || return false
patch_ok = false
# go through the object cache of the summands
if U in keys(object_cache(I))
i = I(U)
if has_decomposition_info(covering) && use_decomposition_info
K = ideal(OO(U), OO(U).(decomposition_info(covering)[U]))
has_dimension_leq_zero(K + i) && (patch_ok = true)
else
has_dimension_leq_zero(i) && (patch_ok = true)
end
patch_ok && continue
end

# Do the same for the summands
for j in summands(I)
if U in keys(object_cache(j))
j_loc = j(U)
if has_decomposition_info(covering) && use_decomposition_info
K = ideal(OO(U), OO(U).(decomposition_info(covering)[U]))
has_dimension_leq_zero(K + j_loc) && (patch_ok = true)
else
has_dimension_leq_zero(j_loc) && (patch_ok = true)
end
patch_ok && break
end
end
patch_ok && continue

# repeat with cheap sub-ideals
for j in summands(I)
j_cheap = cheap_sub_ideal(j, U)
if has_decomposition_info(covering) && use_decomposition_info
K = ideal(OO(U), OO(U).(decomposition_info(covering)[U]))
has_dimension_leq_zero(K + j_cheap) && (patch_ok = true)
else
has_dimension_leq_zero(j_cheap) && (patch_ok = true)
end
patch_ok && break
end
patch_ok && continue

if has_decomposition_info(covering) && use_decomposition_info
K = ideal(OO(U), OO(U).(decomposition_info(covering)[U]))
if !has_dimension_leq_zero(cheap_sub_ideal(I, U) + K)
has_dimension_leq_zero(I(U) + K) || return false
end
else
if !has_dimension_leq_zero(cheap_sub_ideal(I, U))
has_dimension_leq_zero(I(U)) || return false
end
end
end
return true
Expand All @@ -479,13 +538,53 @@ function _self_intersection(I::AbsIdealSheaf)
return get_attribute(I, :_self_intersection)::Int
end

function _is_known_to_be_one(I::AbsIdealSheaf, U::AbsAffineScheme;
dec_inf::Vector = elem_type(OO(U))[]
)
K = ideal(OO(U), dec_inf)
if U in keys(object_cache(I))
i = I(U)
has_attribute(i, :is_one) && get_attribute(i, :is_one) === true && return true
one(OO(U)) in gens(i) && return true
is_one(K + i) && return true
end
j = cheap_sub_ideal(I, U)
is_one(j + K) && return true
return false
end

function _is_known_to_be_one(I::SumIdealSheaf, U::AbsAffineScheme;
dec_inf::Vector = elem_type(OO(U))[]
)
K = ideal(OO(U), dec_inf)
for J in summands(I)
if U in keys(object_cache(J))
j = J(U)
has_attribute(j, :is_one) && get_attribute(j, :is_one) === true && return true
one(OO(U)) in gens(j) && return true
is_one(K + j) && return true
end
end
if U in keys(object_cache(I))
i = I(U)
has_attribute(i, :is_one) && get_attribute(i, :is_one) === true && return true
one(OO(U)) in gens(i) && return true
is_one(K + i) && return true
end
j = cheap_sub_ideal(I, U)
is_one(j + K) && return true
return false
end


function colength(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I)))
X = scheme(I)
all_patches = copy(patches(covering))
patches_done = AbsAffineScheme[]
patches_todo = AbsAffineScheme[]
for U in all_patches
if U in keys(object_cache(I)) && has_attribute(I(U), :is_one) && is_one(I(U))
dec_inf = (has_decomposition_info(covering) ? elem_type(OO(U))[OO(U)(g) for g in decomposition_info(covering)[U]] : elem_type(OO(U))[])
if _is_known_to_be_one(I, U; dec_inf)
push!(patches_done, U)
else
push!(patches_todo, U)
Expand Down
9 changes: 6 additions & 3 deletions src/Rings/mpolyquo-localizations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1369,15 +1369,18 @@ end
n = ngens(P)
imgs_y = t[r+1:(r+n)]
imgs_x = t[r+n+1:end]
I = ideal(A, vcat([one(A) - theta[i]*evaluate(den, imgs_y) for (i, den) in enumerate(denoms)], # Rabinowitsch relations
# Sometimes for unnecessarily complicated sets of generators for I the computation
# wouldn't finish. We try to pass to a `small_generating_set` to hopefully reduce the dependency
# on a particular set of generators.
J = ideal(A, vcat([one(A) - theta[i]*evaluate(den, imgs_y) for (i, den) in enumerate(denoms)], # Rabinowitsch relations
[theta[i]*evaluate(num, imgs_y) - imgs_x[i] for (i, num) in enumerate(nums)], # Graph relations
[evaluate(g, imgs_y) for g in gens(I)])) # codomain's modulus
[evaluate(g, imgs_y) for g in small_generating_set(I)])) # codomain's modulus
# We eliminate the Rabinowitsch variables first, the codomain variables second,
# and finally get to the domain variables. This elimination should be quicker
# than one which does not know the Rabinowitsch property.
oo = degrevlex(theta)*degrevlex(imgs_y)*degrevlex(imgs_x)
#oo = lex(theta)*lex(imgs_y)*lex(imgs_x)
gb = groebner_basis(I, ordering=oo)
gb = groebner_basis(J, ordering=oo)

# TODO: Speed up and use build context.
res_gens = elem_type(A)[f for f in gb if all(e->all(k->is_zero(e[k]), 1:(n+r)), exponents(f))]
Expand Down
11 changes: 11 additions & 0 deletions test/AlgebraicGeometry/Schemes/IdealSheaves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,14 @@ end
@test 0 in dim.(comp2)
end

@testset "production of ideals" begin
IP2 = projective_space(QQ, [:x, :y, :z])
P2 = covered_scheme(IP2)
U = first(affine_charts(P2))
y, z = gens(OO(U))
I = ideal(OO(U), y-z)
II = IdealSheaf(P2, U, I)
Oscar.produce_object(II, affine_charts(P2)[2]; algorithm=:pullback)
Oscar.produce_object(II, affine_charts(P2)[3]; algorithm=:pushforward)
end

0 comments on commit a760cbb

Please sign in to comment.