From 9ef11df0560d6ccb9af149faf0b0eeb956cad3fd Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Mon, 21 Oct 2024 12:08:29 +0200 Subject: [PATCH 01/84] booktests: merge updates from book repo (#4221) * Reimport examples from OSCAR book * booktest: skip some non-julia files vinberg_3 was removed a while ago (moved to vinberg_2) --------- Co-authored-by: Lars Kastner --- .../algebraic-geometry/exres.jlcon | 20 +-- .../hilbert-polynomial.jlcon | 4 +- test/book/cornerstones/groups/actions.jlcon | 13 +- test/book/cornerstones/groups/intro.jlcon | 8 +- .../number-theory/galoismod.jlcon | 4 +- .../{galoismod_1.jl => galoismod_1.jlcon} | 0 .../{galoismod_2.jl => galoismod_2.jlcon} | 0 .../cornerstones/number-theory/intro.jlcon | 2 +- .../number-theory/{sym_1.jl => sym_1.jlcon} | 0 .../{unit_log_plot.jl => unit_log_plot.jlcon} | 0 .../polyhedral-geometry/GKZ_orbits.jlcon | 3 +- .../polyhedral-geometry/platonic.jlcon | 3 +- test/book/ordered_examples.json | 139 +++++++++--------- .../p1xp1_cohomologies.jlcon | 2 +- .../p1xp1_vanishing_sets.jlcon | 2 +- .../SU5-2.jlcon | 15 -- .../expl_G23_tbl.jlcon | 24 +-- .../decker-schmitt-invariant-theory/H3.jlcon | 14 +- .../fang-fourier-monomial-bases/gap.jlcon | 2 + .../oscar-vs-gap.jlcon | 1 + .../sl7-cases.jlcon | 17 +++ .../kuehne-schroeter-matroids/ChowRings.jlcon | 30 ++-- test/book/test.jl | 5 +- 23 files changed, 155 insertions(+), 153 deletions(-) rename test/book/cornerstones/number-theory/{galoismod_1.jl => galoismod_1.jlcon} (100%) rename test/book/cornerstones/number-theory/{galoismod_2.jl => galoismod_2.jlcon} (100%) rename test/book/cornerstones/number-theory/{sym_1.jl => sym_1.jlcon} (100%) rename test/book/cornerstones/number-theory/{unit_log_plot.jl => unit_log_plot.jlcon} (100%) create mode 100644 test/book/specialized/fang-fourier-monomial-bases/gap.jlcon create mode 100644 test/book/specialized/fang-fourier-monomial-bases/oscar-vs-gap.jlcon create mode 100644 test/book/specialized/fang-fourier-monomial-bases/sl7-cases.jlcon diff --git a/test/book/cornerstones/algebraic-geometry/exres.jlcon b/test/book/cornerstones/algebraic-geometry/exres.jlcon index 087305294474..b6d52ae6d476 100644 --- a/test/book/cornerstones/algebraic-geometry/exres.jlcon +++ b/test/book/cornerstones/algebraic-geometry/exres.jlcon @@ -1,25 +1,25 @@ -julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]); +julia> S, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]); -julia> I = ideal([w^2-x*z,w*x-y*z,x^2-w*y,x*y-z^2,y^2-w*z]); +julia> J = ideal([w^2-x*z,w*x-y*z,x^2-w*y,x*y-z^2,y^2-w*z]); -julia> A, _ = quo(R, I); +julia> A, _ = quo(S, J); julia> FA = free_resolution(A) Free resolution of A -R^1 <---- R^5 <---- R^6 <---- R^2 <---- 0 +S^1 <---- S^5 <---- S^6 <---- S^2 <---- 0 0 1 2 3 4 julia> FA[1] -Graded free module R^5([-2]) of rank 5 over R +Graded free module S^5([-2]) of rank 5 over S julia> FA[2] -Graded free module R^5([-3]) + R^1([-4]) of rank 6 over R +Graded free module S^5([-3]) + S^1([-4]) of rank 6 over S julia> FA[3] -Graded free module R^1([-4]) + R^1([-5]) of rank 2 over R +Graded free module S^1([-4]) + S^1([-5]) of rank 2 over S julia> map(FA,1) -R^5 -> R^1 +S^5 -> S^1 e[1] -> (-w*z + y^2)*e[1] e[2] -> (x*y - z^2)*e[1] e[3] -> (-w*y + x^2)*e[1] @@ -28,7 +28,7 @@ e[5] -> (w^2 - x*z)*e[1] Homogeneous module homomorphism julia> map(FA,2) -R^6 -> R^5 +S^6 -> S^5 e[1] -> -x*e[1] + y*e[2] - z*e[4] e[2] -> w*e[1] - x*e[2] + y*e[3] + z*e[5] e[3] -> -w*e[3] + x*e[4] - y*e[5] @@ -38,7 +38,7 @@ e[6] -> (-w^2 + x*z)*e[1] + (-w*z + y^2)*e[5] Homogeneous module homomorphism julia> map(FA,3) -R^2 -> R^6 +S^2 -> S^6 e[1] -> -w*e[2] - y*e[3] + x*e[4] - e[6] e[2] -> (-w^2 + x*z)*e[1] + y*z*e[2] + z^2*e[3] - w*y*e[4] + (w*z - y^2)*e[5] + x*e[6] Homogeneous module homomorphism diff --git a/test/book/cornerstones/algebraic-geometry/hilbert-polynomial.jlcon b/test/book/cornerstones/algebraic-geometry/hilbert-polynomial.jlcon index f35655f922e3..3bba333557af 100644 --- a/test/book/cornerstones/algebraic-geometry/hilbert-polynomial.jlcon +++ b/test/book/cornerstones/algebraic-geometry/hilbert-polynomial.jlcon @@ -1,6 +1,6 @@ -julia> R, (w,x,y,z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]); +julia> S, (w,x,y,z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]); -julia> I = ideal(R, [x*w-y*z, y*w-(x-z)*(x-2*z)]); +julia> I = ideal(S, [x*w-y*z, y*w-(x-z)*(x-2*z)]); julia> Q = projective_scheme(I); diff --git a/test/book/cornerstones/groups/actions.jlcon b/test/book/cornerstones/groups/actions.jlcon index af50427d42b8..b76d6835384b 100644 --- a/test/book/cornerstones/groups/actions.jlcon +++ b/test/book/cornerstones/groups/actions.jlcon @@ -3,8 +3,7 @@ G-set of Alt(4) with seeds 1:4 -julia> G = dihedral_group(6) -Pc group of order 6 +julia> G = dihedral_group(6); julia> U = sub(G, [g for g in gens(G) if order(g) == 2])[1] Sub-pc group of order 2 @@ -37,7 +36,7 @@ Group homomorphism julia> phi(G[1]), phi(G[2]) ((2,3), (1,2,3)) -julia> function optimal_perm_rep(G) +julia> function optimal_transitive_perm_rep(G) is_natural_symmetric_group(G) && return hom(G,G,gens(G)) is_natural_alternating_group(G) && return hom(G,G,gens(G)) cand = [] # pairs (U,h) with U ≤ G and h a map G -> Sym(G/U) @@ -52,7 +51,7 @@ julia> function optimal_perm_rep(G) julia> U = dihedral_group(8) Pc group of order 8 -julia> optimal_perm_rep(U) +julia> optimal_transitive_perm_rep(U) Group homomorphism from pc group of order 8 to permutation group of degree 4 and order 8 @@ -65,8 +64,8 @@ Group homomorphism julia> permutation_group(U) Permutation group of degree 4 and order 8 -julia> for g in all_transitive_groups(degree => 3:9, !is_primitive) - h = image(optimal_perm_rep(g))[1] +julia> for g in all_transitive_groups(degree => 3:8, !is_primitive) + h = image(optimal_transitive_perm_rep(g))[1] if degree(h) < degree(g) id = transitive_group_identification(g) id_new = transitive_group_identification(h) @@ -81,5 +80,3 @@ julia> for g in all_transitive_groups(degree => 3:9, !is_primitive) (8, 13) => (6, 6) (8, 14) => (4, 5) (8, 24) => (6, 11) -(9, 4) => (6, 5) -(9, 8) => (6, 9) diff --git a/test/book/cornerstones/groups/intro.jlcon b/test/book/cornerstones/groups/intro.jlcon index 20e0e4f98c9a..4ae590df2d08 100644 --- a/test/book/cornerstones/groups/intro.jlcon +++ b/test/book/cornerstones/groups/intro.jlcon @@ -62,13 +62,7 @@ julia> pts = collect(orb) julia> visualize(convex_hull(pts)) -julia> R2 = free_module(K, 2) # the "euclidean" plane over K -Vector space of dimension 2 over QQBar - -julia> A = R2([0,1]) -(Root 0 of x, Root 1.00000 of x - 1) - -julia> pts = [A*mat_rot^i for i in 0:4]; +julia> R2 = vector_space(K, 2); # the "euclidean" plane over K julia> sigma_1 = hom(R2, R2, [-R2[1], R2[2]]) Module homomorphism diff --git a/test/book/cornerstones/number-theory/galoismod.jlcon b/test/book/cornerstones/number-theory/galoismod.jlcon index 64c37466576c..6a1b22c4ba50 100644 --- a/test/book/cornerstones/number-theory/galoismod.jlcon +++ b/test/book/cornerstones/number-theory/galoismod.jlcon @@ -52,9 +52,9 @@ true julia> V, f = galois_module(K); OK = ring_of_integers(K); M = f(OK); -julia> fl, c = is_free_with_basis(M); # the elements of c are a basis +julia> fl, c = is_free_with_basis(M); # the elements of c form a basis -julia> b = preimage(f, c[1]) # the element might different per session +julia> b = preimage(f, c[1]) # the element may be different per session a^3 julia> A, mA = automorphism_group(K); diff --git a/test/book/cornerstones/number-theory/galoismod_1.jl b/test/book/cornerstones/number-theory/galoismod_1.jlcon similarity index 100% rename from test/book/cornerstones/number-theory/galoismod_1.jl rename to test/book/cornerstones/number-theory/galoismod_1.jlcon diff --git a/test/book/cornerstones/number-theory/galoismod_2.jl b/test/book/cornerstones/number-theory/galoismod_2.jlcon similarity index 100% rename from test/book/cornerstones/number-theory/galoismod_2.jl rename to test/book/cornerstones/number-theory/galoismod_2.jlcon diff --git a/test/book/cornerstones/number-theory/intro.jlcon b/test/book/cornerstones/number-theory/intro.jlcon index 55237aeef016..b1a77a32dd84 100644 --- a/test/book/cornerstones/number-theory/intro.jlcon +++ b/test/book/cornerstones/number-theory/intro.jlcon @@ -94,4 +94,4 @@ julia> preimage(mU, -214841715*a - 3293461126) Abelian group element [1, 5] julia> -214841715*a - 3293461126 == (-1)^1 * (3a + 46)^5 -true \ No newline at end of file +true diff --git a/test/book/cornerstones/number-theory/sym_1.jl b/test/book/cornerstones/number-theory/sym_1.jlcon similarity index 100% rename from test/book/cornerstones/number-theory/sym_1.jl rename to test/book/cornerstones/number-theory/sym_1.jlcon diff --git a/test/book/cornerstones/number-theory/unit_log_plot.jl b/test/book/cornerstones/number-theory/unit_log_plot.jlcon similarity index 100% rename from test/book/cornerstones/number-theory/unit_log_plot.jl rename to test/book/cornerstones/number-theory/unit_log_plot.jlcon diff --git a/test/book/cornerstones/polyhedral-geometry/GKZ_orbits.jlcon b/test/book/cornerstones/polyhedral-geometry/GKZ_orbits.jlcon index 1524f987593e..6d47634cc605 100644 --- a/test/book/cornerstones/polyhedral-geometry/GKZ_orbits.jlcon +++ b/test/book/cornerstones/polyhedral-geometry/GKZ_orbits.jlcon @@ -15,8 +15,7 @@ julia> OrbitRepresentatives= julia> OrbitSizes = [length( filter(x->minimum(gset(G,permuted,[x]))==u, - GKZ_Vectors) - ) for u in OrbitRepresentatives]; + GKZ_Vectors)) for u in OrbitRepresentatives]; julia> show(OrbitSizes) [12, 24, 24, 8, 2, 4] diff --git a/test/book/cornerstones/polyhedral-geometry/platonic.jlcon b/test/book/cornerstones/polyhedral-geometry/platonic.jlcon index 5be3803224d1..f216bce0e5cd 100644 --- a/test/book/cornerstones/polyhedral-geometry/platonic.jlcon +++ b/test/book/cornerstones/polyhedral-geometry/platonic.jlcon @@ -1,4 +1,5 @@ -julia> [ f_vector(P) for P in [T, cube(3), cross_polytope(3), dodecahedron(), icosahedron()] ] +julia> [ f_vector(P) for P in [T, cube(3), cross_polytope(3), + dodecahedron(), icosahedron()] ] 5-element Vector{Vector{ZZRingElem}}: [4, 6, 4] [8, 12, 6] diff --git a/test/book/ordered_examples.json b/test/book/ordered_examples.json index 9100288ceb77..37c1e387f0a9 100644 --- a/test/book/ordered_examples.json +++ b/test/book/ordered_examples.json @@ -2,58 +2,58 @@ "_ns": { "Oscar": [ "https://github.com/oscar-system/Oscar.jl", - "1.0.0" + "1.0.4" ] }, "_type": { "name": "Dict", "params": { "key_type": "String", - "specialized/boehm-breuer-git-fans": { + "cornerstones/groups": { "name": "Vector", "params": "String" }, - "specialized/markwig-ristau-schleis-faithful-tropicalization": { + "specialized/boehm-breuer-git-fans": { "name": "Vector", "params": "String" }, - "specialized/holt-ren-tropical-geometry": { + "specialized/markwig-ristau-schleis-faithful-tropicalization": { "name": "Vector", "params": "String" }, - "specialized/bies-turner-string-theory-applications": { + "introduction/introduction": { "name": "Vector", "params": "String" }, - "specialized/aga-boehm-hoffmann-markwig-traore": { + "specialized/holt-ren-tropical-geometry": { "name": "Vector", "params": "String" }, - "specialized/joswig-kastner-lorenz-confirmable-workflows": { + "specialized/eder-mohr-ideal-theoretic": { "name": "Vector", "params": "String" }, - "cornerstones/number-theory": { + "specialized/bies-turner-string-theory-applications": { "name": "Vector", "params": "String" }, - "cornerstones/groups": { + "specialized/kuehne-schroeter-matroids": { "name": "Vector", "params": "String" }, - "introduction/introduction": { + "specialized/aga-boehm-hoffmann-markwig-traore": { "name": "Vector", "params": "String" }, - "specialized/eder-mohr-ideal-theoretic": { + "cornerstones/polyhedral-geometry": { "name": "Vector", "params": "String" }, - "specialized/kuehne-schroeter-matroids": { + "specialized/fang-fourier-monomial-bases": { "name": "Vector", "params": "String" }, - "cornerstones/polyhedral-geometry": { + "specialized/joswig-kastner-lorenz-confirmable-workflows": { "name": "Vector", "params": "String" }, @@ -61,15 +61,15 @@ "name": "Vector", "params": "String" }, - "specialized/fang-fourier-monomial-bases": { + "specialized/brandhorst-zach-fibration-hopping": { "name": "Vector", "params": "String" }, - "specialized/brandhorst-zach-fibration-hopping": { + "specialized/breuer-nebe-parker-orthogonal-discriminants": { "name": "Vector", "params": "String" }, - "specialized/breuer-nebe-parker-orthogonal-discriminants": { + "cornerstones/number-theory": { "name": "Vector", "params": "String" }, @@ -88,6 +88,16 @@ } }, "data": { + "cornerstones/groups": [ + "intro.jlcon", + "actions.jlcon", + "explSL25.jlcon", + "chars.jlcon", + "extensions.jlcon", + "cohomology.jlcon", + "reps.jlcon", + "genchar.jlcon" + ], "specialized/boehm-breuer-git-fans": [ "explG25_1.jlcon", "explG25_2.jlcon", @@ -107,6 +117,12 @@ "eliminate_xz.jlcon", "eliminate_yz.jlcon" ], + "introduction/introduction": [ + "julia.jlcon", + "julia-jit.jlcon", + "julia2.jlcon", + "julia3.jlcon" + ], "specialized/holt-ren-tropical-geometry": [ "semiring.jlcon", "matrixAndPoly.jlcon", @@ -127,50 +143,6 @@ "intersection.jlcon", "grc.jlcon" ], - "specialized/bies-turner-string-theory-applications": [ - "SU5.jlcon", - "SU5-2.jlcon" - ], - "specialized/aga-boehm-hoffmann-markwig-traore": [ - "graphname.jlcon", - "Caterpillar3.jlcon" - ], - "specialized/joswig-kastner-lorenz-confirmable-workflows": [ - "versioninfo.jlcon", - "polynomial-load.jlcon", - "snf.jlcon" - ], - "cornerstones/number-theory": [ - "intro.jlcon", - "intro_plot_lattice.jlcon", - "unit_plot.jlcon", - "unit_log_plot.jl", - "general.jlcon", - "embeddings.jlcon", - "intro4.jlcon", - "sym.jlcon", - "sym_1.jl", - "cohenlenstra.jlcon", - "galoismod.jlcon", - "galoismod_1.jl", - "galoismod_2.jl" - ], - "cornerstones/groups": [ - "intro.jlcon", - "actions.jlcon", - "explSL25.jlcon", - "chars.jlcon", - "extensions.jlcon", - "cohomology.jlcon", - "reps.jlcon", - "genchar.jlcon" - ], - "introduction/introduction": [ - "julia.jlcon", - "julia-jit.jlcon", - "julia2.jlcon", - "julia3.jlcon" - ], "specialized/eder-mohr-ideal-theoretic": [ "hilbert.jlcon", "gbeliminate.jlcon", @@ -181,11 +153,19 @@ "combinatorics.jlcon", "lcis.jlcon" ], + "specialized/bies-turner-string-theory-applications": [ + "SU5.jlcon", + "SU5-2.jlcon" + ], "specialized/kuehne-schroeter-matroids": [ "basics.jlcon", "realization_space.jlcon", "ChowRings.jlcon" ], + "specialized/aga-boehm-hoffmann-markwig-traore": [ + "graphname.jlcon", + "Caterpillar3.jlcon" + ], "cornerstones/polyhedral-geometry": [ "pentagon.jlcon", "lp.jlcon", @@ -210,6 +190,22 @@ "GKZ_orbits.jlcon", "ch-benchmark.jlcon" ], + "specialized/fang-fourier-monomial-bases": [ + "get-operators.jlcon", + "basis.jlcon", + "fflv.jlcon", + "string.jlcon", + "lusztig.jlcon", + "nz.jlcon", + "sl7-cases.jlcon", + "oscar-vs-gap.jlcon", + "gap.jlcon" + ], + "specialized/joswig-kastner-lorenz-confirmable-workflows": [ + "versioninfo.jlcon", + "polynomial-load.jlcon", + "snf.jlcon" + ], "specialized/rose-sturmfels-telen-tropical-implicitization": [ "gen_impl.jlcon", "hyperdet.jlcon", @@ -220,14 +216,6 @@ "chow_fan.jlcon", "chow_transl.jlcon" ], - "specialized/fang-fourier-monomial-bases": [ - "get-operators.jlcon", - "basis.jlcon", - "fflv.jlcon", - "string.jlcon", - "lusztig.jlcon", - "nz.jlcon" - ], "specialized/brandhorst-zach-fibration-hopping": [ "vinberg_1.jlcon", "vinberg_2.jlcon" @@ -244,6 +232,21 @@ "expl_od_odd.jlcon", "expl_plusminus.jlcon" ], + "cornerstones/number-theory": [ + "intro.jlcon", + "intro_plot_lattice.jlcon", + "unit_plot.jlcon", + "unit_log_plot.jlcon", + "general.jlcon", + "embeddings.jlcon", + "intro4.jlcon", + "sym.jlcon", + "sym_1.jlcon", + "cohenlenstra.jlcon", + "galoismod.jlcon", + "galoismod_1.jlcon", + "galoismod_2.jlcon" + ], "cornerstones/algebraic-geometry": [ "ex11.jlcon", "ex11dist.jlcon", diff --git a/test/book/specialized/bies-kastner-toric-geometry/p1xp1_cohomologies.jlcon b/test/book/specialized/bies-kastner-toric-geometry/p1xp1_cohomologies.jlcon index 2971bee5b265..c6da260e5df0 100644 --- a/test/book/specialized/bies-kastner-toric-geometry/p1xp1_cohomologies.jlcon +++ b/test/book/specialized/bies-kastner-toric-geometry/p1xp1_cohomologies.jlcon @@ -1,4 +1,4 @@ -julia> P1 = projective_space(NormalToricVariety,1) +julia> P1 = projective_space(NormalToricVariety, 1) Normal toric variety julia> l = toric_line_bundle(P1*P1, [-3,1]) diff --git a/test/book/specialized/bies-kastner-toric-geometry/p1xp1_vanishing_sets.jlcon b/test/book/specialized/bies-kastner-toric-geometry/p1xp1_vanishing_sets.jlcon index 4d412ab3c970..2cad9f47490e 100644 --- a/test/book/specialized/bies-kastner-toric-geometry/p1xp1_vanishing_sets.jlcon +++ b/test/book/specialized/bies-kastner-toric-geometry/p1xp1_vanishing_sets.jlcon @@ -1,4 +1,4 @@ -julia> P1 = projective_space(NormalToricVariety,1) +julia> P1 = projective_space(NormalToricVariety, 1) Normal toric variety julia> v0, v1, v2 = vanishing_sets(P1*P1) diff --git a/test/book/specialized/bies-turner-string-theory-applications/SU5-2.jlcon b/test/book/specialized/bies-turner-string-theory-applications/SU5-2.jlcon index 98cb9c604347..616ab616fe01 100644 --- a/test/book/specialized/bies-turner-string-theory-applications/SU5-2.jlcon +++ b/test/book/specialized/bies-turner-string-theory-applications/SU5-2.jlcon @@ -11,18 +11,3 @@ Global Tate model over a concrete base -- SU(5)xU(1) restricted Tate model based julia> t5 = resolve(t, 1) Partially resolved global Tate model over a concrete base -- SU(5)xU(1) restricted Tate model based on arXiv paper 1109.3454 Eq. (3.1) - -julia> cox_ring(ambient_space(t5)) -Multivariate polynomial ring in 12 variables over QQ graded by - x1 -> [1 0 0 0 0 0 0] - x2 -> [0 1 0 0 0 0 0] - x3 -> [0 1 0 0 0 0 0] - x4 -> [0 1 0 0 0 0 0] - x -> [0 0 1 0 0 0 0] - y -> [0 0 0 1 0 0 0] - z -> [0 0 0 0 1 0 0] - e1 -> [0 0 0 0 0 1 0] - e4 -> [0 0 0 0 0 0 1] - e2 -> [-1 -3 -1 1 -1 -1 0] - e3 -> [0 4 1 -1 1 0 -1] - s -> [2 6 -1 0 2 1 1] diff --git a/test/book/specialized/breuer-nebe-parker-orthogonal-discriminants/expl_G23_tbl.jlcon b/test/book/specialized/breuer-nebe-parker-orthogonal-discriminants/expl_G23_tbl.jlcon index 79c5af782859..8245714a6134 100644 --- a/test/book/specialized/breuer-nebe-parker-orthogonal-discriminants/expl_G23_tbl.jlcon +++ b/test/book/specialized/breuer-nebe-parker-orthogonal-discriminants/expl_G23_tbl.jlcon @@ -1,17 +1,17 @@ julia> Oscar.OrthogonalDiscriminants.show_with_ODs( -character_table("G2(3)", 2)) + character_table("G2(3)", 2)) G2(3)mod2 - 2 6 3 3 . 1 1 . . . . . . - 3 6 6 6 6 4 4 . 3 3 3 . . - 7 1 . . . . . 1 . . . . . - 13 1 . . . . . . . . . 1 1 - - 1a 3a 3b 3c 3d 3e 7a 9a 9b 9c 13a 13b - 2P 1a 3a 3b 3c 3d 3e 7a 9a 9c 9b 13b 13a - 3P 1a 1a 1a 1a 1a 1a 7a 3c 3c 3c 13a 13b - 7P 1a 3a 3b 3c 3d 3e 1a 9a 9b 9c 13b 13a - 13P 1a 3a 3b 3c 3d 3e 7a 9a 9b 9c 1a 1a + 2 6 3 3 . 1 1 . . . . . . + 3 6 6 6 6 4 4 . 3 3 3 . . + 7 1 . . . . . 1 . . . . . + 13 1 . . . . . . . . . 1 1 + + 1a 3a 3b 3c 3d 3e 7a 9a 9b 9c 13a 13b + 2P 1a 3a 3b 3c 3d 3e 7a 9a 9c 9b 13b 13a + 3P 1a 1a 1a 1a 1a 1a 7a 3c 3c 3c 13a 13b + 7P 1a 3a 3b 3c 3d 3e 1a 9a 9b 9c 13b 13a + 13P 1a 3a 3b 3c 3d 3e 7a 9a 9b 9c 1a 1a d OD 2 X_1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 X_2 1 O- + 14 5 5 -4 2 -1 . 2 -1 -1 1 1 @@ -29,4 +29,4 @@ X_12 1 O+ + 832 -32 -32 -5 4 4 -1 1 1 1 . . A = 3z_3 + 1 /A = -3z_3 - 2 B = -z_13^11 - z_13^8 - z_13^7 - z_13^6 - z_13^5 - z_13^2 - 1 -B* = z_13^11 + z_13^8 + z_13^7 + z_13^6 + z_13^5 + z_13^2 \ No newline at end of file +B* = z_13^11 + z_13^8 + z_13^7 + z_13^6 + z_13^5 + z_13^2 diff --git a/test/book/specialized/decker-schmitt-invariant-theory/H3.jlcon b/test/book/specialized/decker-schmitt-invariant-theory/H3.jlcon index 3c7ed37b97b5..8aa239d34316 100644 --- a/test/book/specialized/decker-schmitt-invariant-theory/H3.jlcon +++ b/test/book/specialized/decker-schmitt-invariant-theory/H3.jlcon @@ -47,22 +47,22 @@ julia> [[ kernel(reduce(hcat, [ K[coeff(p, x); coeff(p, y); coeff(p, z)] for p i 1-element Vector{Vector{AbstractAlgebra.Generic.MatSpaceElem{AbsSimpleNumFieldElem}}}: [[a+1 0 1], [-a 0 1], [-1 0 1], [0 a+1 1], [0 -a 1], [0 -1 1], [-a 1 0], [a+1 1 0], [-1 1 0]] -julia> T, t = polynomial_ring(QQ, "t") -(Univariate polynomial ring in t over QQ, t) +julia> S, s = QQ["t"]; T = fraction_field(S); t = T(s); julia> RR, (X, Y, Z) = graded_polynomial_ring(T, [ "X", "Y", "Z"]); julia> F = X^3+Y^3+Z^3; -julia> G = t*F+hessian(F); +julia> G = t*F+hessian(F) +t*X^3 + 216*X*Y*Z + t*Y^3 + t*Z^3 julia> L = syzygy_generators([hessian(G), F, hessian(F)]); julia> collect(coefficients(L[1])) -3-element Vector{QQPolyRingElem}: - 1 - 279936*t - -t^3 - 93312 +3-element Vector{AbstractAlgebra.Generic.FracFieldElem{QQPolyRingElem}}: + -1 + -279936*t + t^3 + 93312 julia> C, iC = center(H3); diff --git a/test/book/specialized/fang-fourier-monomial-bases/gap.jlcon b/test/book/specialized/fang-fourier-monomial-bases/gap.jlcon new file mode 100644 index 000000000000..d47d03cf8252 --- /dev/null +++ b/test/book/specialized/fang-fourier-monomial-bases/gap.jlcon @@ -0,0 +1,2 @@ +L:= SimpleLieAlgebra("A", 4, Rationals);; +V:= HighestWeightModule(L, [1,3,2,1]);; diff --git a/test/book/specialized/fang-fourier-monomial-bases/oscar-vs-gap.jlcon b/test/book/specialized/fang-fourier-monomial-bases/oscar-vs-gap.jlcon new file mode 100644 index 000000000000..cdf89abac0c2 --- /dev/null +++ b/test/book/specialized/fang-fourier-monomial-bases/oscar-vs-gap.jlcon @@ -0,0 +1 @@ +basis_lie_highest_weight(:A, 4, [1,3,2,1]); diff --git a/test/book/specialized/fang-fourier-monomial-bases/sl7-cases.jlcon b/test/book/specialized/fang-fourier-monomial-bases/sl7-cases.jlcon new file mode 100644 index 000000000000..d4c290bf899f --- /dev/null +++ b/test/book/specialized/fang-fourier-monomial-bases/sl7-cases.jlcon @@ -0,0 +1,17 @@ +[fundamentals] => 320 +[fundamentals, [1, 0, 1, 0, 0]] => 70 +[fundamentals, [0, 0, 1, 0, 1]] => 70 +[fundamentals, [0, 1, 0, 1, 0]] => 54 +[fundamentals, [1, 0, 0, 1, 0]] => 28 +[fundamentals, [0, 1, 0, 0, 1]] => 28 +[fundamentals, [1, 0, 0, 1, 0], [0, 1, 0, 1, 0]] => 46 +[fundamentals, [0, 1, 0, 1, 0], [0, 1, 0, 0, 1]] => 46 +[fundamentals, [1, 0, 1, 0, 0], [1, 0, 0, 1, 0]] => 30 +[fundamentals, [0, 1, 0, 0, 1], [0, 0, 1, 0, 1]] => 30 +[fundamentals, [1, 0, 1, 0, 0], [0, 1, 0, 0, 1]] => 8 +[fundamentals, [1, 0, 0, 1, 0], [0, 0, 1, 0, 1]] => 8 +[fundamentals, [1, 0, 1, 0, 0], [1, 0, 0, 1, 0], [0, 1, 0, 1, 0]] => 70 +[fundamentals, [0, 1, 0, 1, 0], [0, 1, 0, 0, 1], [0, 0, 1, 0, 1]] => 70 +[fundamentals, [1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 1, 0, 0, 1]] => 10 +[fundamentals, [1, 0, 1, 0, 0], [1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 1, 0, 0, 1]] => 10 +[fundamentals, [1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 1, 0, 0, 1], [0, 0, 1, 0, 1]] => 10 diff --git a/test/book/specialized/kuehne-schroeter-matroids/ChowRings.jlcon b/test/book/specialized/kuehne-schroeter-matroids/ChowRings.jlcon index 048441a4fb83..c2e3fe5d25c3 100644 --- a/test/book/specialized/kuehne-schroeter-matroids/ChowRings.jlcon +++ b/test/book/specialized/kuehne-schroeter-matroids/ChowRings.jlcon @@ -63,26 +63,26 @@ julia> RR, _ = graded_polynomial_ring(QQ,"y_#" => 1:length(basis_PD1)); julia> map = hom(RR,AA,basis_PD1); -julia> K = kernel(hom(RR,AA,[b^(rank(M)-2k)*b for b in basis_PD1])); +julia> K = kernel(hom(RR,AA,[b^(rank(M)-2k)*b3 for b3 in basis_PD1])); julia> basis_HR = [map(h) for h in gens(K) if degree(h).coeff==k*g.coeff] 7-element Vector{MPolyQuoRingElem{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}}}: - -x_{Edge(4, 3)} + 2*x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} - -x_{Edge(4, 3)} + 2*x_{Edge(2, 1),Edge(4, 1),Edge(4, 2)} - -x_{Edge(4, 3)} + 2*x_{Edge(2, 1),Edge(4, 3)} - -x_{Edge(4, 3)} + 2*x_{Edge(3, 1),Edge(4, 2)} - -x_{Edge(4, 3)} + 2*x_{Edge(3, 1),Edge(4, 1),Edge(4, 3)} - -x_{Edge(4, 3)} + 2*x_{Edge(3, 2),Edge(4, 1)} - -x_{Edge(4, 3)} + 2*x_{Edge(3, 2),Edge(4, 2),Edge(4, 3)} + x_{Edge(4, 3)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + x_{Edge(2, 1),Edge(4, 1),Edge(4, 2)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + 2*x_{Edge(2, 1),Edge(4, 3)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + 2*x_{Edge(3, 1),Edge(4, 2)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + x_{Edge(3, 1),Edge(4, 1),Edge(4, 3)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + 2*x_{Edge(3, 2),Edge(4, 1)} + -x_{Edge(2, 1),Edge(3, 1),Edge(3, 2)} + x_{Edge(3, 2),Edge(4, 2),Edge(4, 3)} julia> Mat3 = matrix(QQ,[[(-1)^k*vol_map(b1*b^(rank(M)-2k-1)*b2) for b1 in basis_HR] for b2 in basis_HR]) -[6 2 4 2 4 2 4] -[2 6 4 2 4 2 4] -[4 4 10 4 6 4 6] -[2 2 4 6 4 2 4] -[4 4 6 4 10 4 6] -[2 2 4 2 4 6 4] -[4 4 6 4 6 4 10] +[ 2 0 -2 0 -1 0 -1] +[ 0 2 1 1 1 1 1] +[-2 1 5 1 1 1 1] +[ 0 1 1 5 1 1 1] +[-1 1 1 1 2 1 1] +[ 0 1 1 1 1 5 1] +[-1 1 1 1 1 1 2] julia> is_positive_definite(matrix(ZZ,[ZZ(i) for i in Mat3])) true diff --git a/test/book/test.jl b/test/book/test.jl index 656d72b8b4d9..912b995283db 100644 --- a/test/book/test.jl +++ b/test/book/test.jl @@ -32,7 +32,10 @@ isdefined(Main, :FakeTerminals) || include(joinpath(pkgdir(REPL),"test","FakeTer # somewhat slow (~300s) "cornerstones/polyhedral-geometry/ch-benchmark.jlcon", - #"specialized/brandhorst-zach-fibration-hopping/vinberg_3.jlcon", + + # not a proper julia input file + "specialized/fang-fourier-monomial-bases/sl7-cases.jlcon", + "specialized/fang-fourier-monomial-bases/gap.jlcon", ] dispsize = (40, 130) From 3deb88b021ca74ae3300546b7fb79d125efe3811 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 21 Oct 2024 15:57:26 +0200 Subject: [PATCH 02/84] No type piracy: remove matrix*vector methods for Nemo types (#760) * No type piracy: remove matrix*vector methods for Nemo types * Adjut tests * Remove more problematic * methods Multiplying FreeModuleElem by a matrix group element returning a Vector{} is weird and shouldn't be done. While this could be fixed by adding a second transpose call, I don't think this kind of inefficient operation should be encouraged, that vector space simply is a right-module, end of story. In a similar vein, remove the three argument * method. --- src/Groups/matrices/forms.jl | 7 +-- src/Groups/matrices/matrix_manipulation.jl | 15 ------ test/Groups/conformance.jl | 2 +- test/Groups/operations.jl | 60 ++++++++++------------ 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/src/Groups/matrices/forms.jl b/src/Groups/matrices/forms.jl index 887df1bbbb42..ffe05230e273 100644 --- a/src/Groups/matrices/forms.jl +++ b/src/Groups/matrices/forms.jl @@ -367,15 +367,16 @@ function (f::SesquilinearForm{T})(v::AbstractAlgebra.Generic.FreeModuleElem{T},w @req f.descr!=:quadratic "Quadratic forms requires only one argument" if f.descr==:hermitian - return v*gram_matrix(f)*map( y->frobenius(y,div(degree(base_ring(w)),2)),w) + w = conjugate_transpose(w.v) else - return v*gram_matrix(f)*w + w = transpose(w.v) end + return v.v*gram_matrix(f)*w end function (f::SesquilinearForm{T})(v::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem @req f.descr==:quadratic "Sesquilinear forms requires two arguments" - return v*gram_matrix(f)*v + return v.v*gram_matrix(f)*transpose(v.v) end diff --git a/src/Groups/matrices/matrix_manipulation.jl b/src/Groups/matrices/matrix_manipulation.jl index 39220de07b16..5b62d9450631 100644 --- a/src/Groups/matrices/matrix_manipulation.jl +++ b/src/Groups/matrices/matrix_manipulation.jl @@ -170,17 +170,7 @@ end # ######################################################################## - -Base.:*(v::AbstractAlgebra.Generic.FreeModuleElem{T},x::MatElem{T}) where T <: RingElem = v.parent(v.v*x) -Base.:*(x::MatElem{T},u::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem = x*transpose(u.v) - -# evaluation of the form x into the vectors v and u -Base.:*(v::AbstractAlgebra.Generic.FreeModuleElem{T},x::MatElem{T},u::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem = (v.v*x*transpose(u.v))[1] - - Base.:*(v::AbstractAlgebra.Generic.FreeModuleElem{T},x::MatrixGroupElem{T}) where T <: RingElem = v.parent(v.v*matrix(x)) -Base.:*(x::MatrixGroupElem{T},u::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem = matrix(x)*transpose(u.v) - Base.:*(v::Vector{T}, x::MatrixGroupElem{T}) where T <: RingElem = v*matrix(x) Base.:*(x::MatrixGroupElem{T}, u::Vector{T}) where T <: RingElem = matrix(x)*u @@ -193,8 +183,3 @@ Base.:^(v::AbstractAlgebra.Generic.FreeModuleElem{T},x::MatrixGroupElem{T}) wher function Base.:^(V::AbstractAlgebra.Generic.Submodule{T}, x::MatrixGroupElem{T}) where T <: RingElem return sub(V.m, [v^x for v in V.gens])[1] end - -# evaluation of the form x into the vectors v and u -Base.:*(v::AbstractAlgebra.Generic.FreeModuleElem{T},x::MatrixGroupElem{T},u::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem = (v.v*matrix(x)*transpose(u.v))[1] - -map(f::Function, v::AbstractAlgebra.Generic.FreeModuleElem{T}) where T <: RingElem = v.parent(map(f,v.v)) diff --git a/test/Groups/conformance.jl b/test/Groups/conformance.jl index dbbc4ceed9f9..a37f33d86955 100644 --- a/test/Groups/conformance.jl +++ b/test/Groups/conformance.jl @@ -5,7 +5,7 @@ import Oscar.AbstractAlgebra: Group include(joinpath(dirname(pathof(AbstractAlgebra)), "..", "test", "Groups-conformance-tests.jl")) -@testset "GAPGroups_interface_conformance for $(G)" for G in L +@testset "GAPGroups_interface_conformance $G of type $(typeof(G))" for G in L test_Group_interface(G) test_GroupElem_interface(rand(G, 2)...) diff --git a/test/Groups/operations.jl b/test/Groups/operations.jl index f9b0b23adf0f..63b9666b31de 100644 --- a/test/Groups/operations.jl +++ b/test/Groups/operations.jl @@ -50,49 +50,50 @@ end end -@testset "Matrix manipulation" begin - F = GF(5, 1) - - I = identity_matrix(F,6) - x = matrix(F,2,2,[2,3,4,0]) - I1 = deepcopy(I) - I1[3:4,3:4] = x - @test I==identity_matrix(F,6) - I[3:4,3:4] = x - @test I==I1 - @test I[3:4,3:4]==x +@testset "Matrix manipulation" for F in (GF(5), GF(5,1)) + V = vector_space(F,6) - @test matrix([V[i] for i in 1:6])==identity_matrix(F,6) + I = identity_matrix(F,6) + + # test converting a list of vector space elements into a matrix + @test matrix([V[i] for i in 1:6]) == I + + # permutation matrix tests L = [1,4,6,2,3,5] - P = permutation_matrix(F,L) - @testset for i in 1:6 - @test V[i]*P == V[L[i]] - end p = symmetric_group(6)(L) - @test permutation_matrix(F,p)==permutation_matrix(F,L) - @test upper_triangular_matrix(F.([2,3,1,1,0,1]))==matrix(F,3,3,[2,3,1,0,1,0,0,0,1]) - @test lower_triangular_matrix(F.([2,3,1,1,0,1]))==matrix(F,3,3,[2,0,0,3,1,0,1,0,1]) + @test permutation_matrix(F,p) == permutation_matrix(F,L) + + # upper + lower triangular matrix constructor + @test upper_triangular_matrix(F.([1,2,3,4,5,6])) == matrix(F, [1 2 3; 0 4 5; 0 0 6]) + @test lower_triangular_matrix(F.([2,3,1,1,0,1])) == matrix(F, [2 0 0; 3 1 0; 1 0 1]) @test_throws ArgumentError lower_triangular_matrix(F.([2,3,1,1])) R,t=polynomial_ring(F,:t) f = t^4+2*t^3+4*t+1 - @test f(identity_matrix(F,6))==f(1)*identity_matrix(F,6) - @test_throws ArgumentError conjugate_transpose(x) + @test f(I)==f(1)*I + + @test_throws ArgumentError conjugate_transpose(I) + + P = permutation_matrix(F,L) @test is_symmetric(P+transpose(P)) @test is_skew_symmetric(P-transpose(P)) @test is_alternating(P-transpose(P)) F,z = finite_field(2,2) - x=matrix(F,4,4,[1,z,0,0,0,1,z^2,z,z,0,0,1,0,0,z+1,0]) + x=matrix(F,[1 z 0 0; 0 1 z^2 z; z 0 0 1; 0 0 z+1 0]) y=x+transpose(x) @test is_symmetric(y) @test is_hermitian(x+conjugate_transpose(x)) @test is_skew_symmetric(y) @test is_alternating(y) + @test conjugate_transpose(conjugate_transpose(x)) == x + y[1,1]=1 + @test is_symmetric(y) + @test is_hermitian(x+conjugate_transpose(x)) @test is_skew_symmetric(y) @test !is_alternating(y) - @test conjugate_transpose(x)==transpose(matrix(F,4,4,[1,z+1,0,0,0,1,z,z+1,z+1,0,0,1,0,0,z,0])) + @test conjugate_transpose(x)==transpose(matrix(F,[1 z+1 0 0; 0 1 z z+1; z+1 0 0 1; 0 0 z 0])) end @@ -109,19 +110,10 @@ end @test complement(V,W0)[1]==V @test complement(V,sub(V,gens(V))[1])[1]==W0 - v1=V([1,2,3,4,5]) - v2=V([1,6,0,5,2]) G = GL(5,F) - B=matrix(F,5,5,[1,2,3,1,0,4,5,2,0,1,3,2,5,4,0,1,6,4,3,5,2,0,4,1,1]) + B = rand(G) + v1 = rand(V) @test v1*B == V([ sum([v1[i]*B[i,j] for i in 1:5]) for j in 1:5 ]) - @test V(transpose(B*v2))==V([ sum([v2[i]*B[j,i] for i in 1:5]) for j in 1:5 ]) - B = G(B) - @test v1*B == V([ sum([v1[i]*B[i,j] for i in 1:5]) for j in 1:5 ]) - @test V(transpose(B*v2))==V([ sum([v2[i]*B[j,i] for i in 1:5]) for j in 1:5 ]) - @test map(x->x+1,v1)==V([2,3,4,5,6]) - - # see the discussion of pull/1368 and issues/872 - @test_throws MethodError v1*v2 end # from file matrices/stuff_field_gen.jl From 88b734c890d1db315077caf4955d8acaec043b2d Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Tue, 22 Oct 2024 12:46:58 +0200 Subject: [PATCH 03/84] Module quotients II (#4225) * Quotients for modules II * correction --- .../subquotients.md | 4 + src/Modules/UngradedModules/SubquoModule.jl | 79 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md index 71f38afd34a5..695bf907fe85 100644 --- a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md +++ b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md @@ -362,6 +362,10 @@ annihilator(N::SubquoModule{T}) where T quotient(M::SubquoModule{T}, N::SubquoModule{T}) where T ``` +```@docs +quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T +``` + ## Submodules and Quotients ```@docs diff --git a/src/Modules/UngradedModules/SubquoModule.jl b/src/Modules/UngradedModules/SubquoModule.jl index f644aeabe8bc..e44268dc784a 100644 --- a/src/Modules/UngradedModules/SubquoModule.jl +++ b/src/Modules/UngradedModules/SubquoModule.jl @@ -1778,6 +1778,85 @@ end (::Colon)(M::SubquoModule{T}, N::SubquoModule{T}) where T = quotient(M, N) +@doc raw""" + quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T + + +Return the quotient of `M` by `J`. + +Alternatively, use `M:J`. + +!!! note + By definition, $M:J = \{a \in A \mid Ja \subset M\}$. Here, $A$ is the + ambient module of $M$. + +# Examples + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> F = free_module(R, 1); + +julia> AM = R[x;]; + +julia> BM = R[x^2; y^3; z^4]; + +julia> M = SubquoModule(F, AM, BM) +Subquotient of Submodule with 1 generator +1 -> x*e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> J = ideal(R, [x, y, z])^2 +Ideal generated by + x^2 + x*y + x*z + y^2 + y*z + z^2 + +julia> L = quotient(M, J) +Subquotient of Submodule with 5 generators +1 -> x*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] +4 -> y*z^3*e[1] +5 -> y^2*z^2*e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> ambient_free_module(L) == ambient_free_module(M) +true + +``` +""" +function quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T + error("not implemented for the given types of modules.") +end + +(::Colon)(M::SubquoModule{T}, J::MPolyIdeal{T}) where T = quotient(M, N) + +function quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T <: MPolyRingElem + @assert base_ring(M) == base_ring(J) + F = ambient_free_module(M) + U = M.sub+M.quo + SgU = singular_generators(U.gens) + singular_assure(J) + SQ = Singular.quotient(SgU, J.gens.S) + MG = ModuleGens(F, SQ) + UF = SubModuleOfFreeModule(F, MG) + res = SubquoModule(UF) + res.quo = M.quo + return res +end + + + ######################################## @doc raw""" From 62cc619515d704ced414b21d0e1401f935efcefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 22 Oct 2024 17:00:10 +0200 Subject: [PATCH 04/84] Some fixes and performance improvements for `iso_oscar_gap(::LieAlgebra)` (#4227) * Make `_iso_oscar_gap(::AbstractLieAlgeba)` way more efficient * Move root system iso to its own function * Some type stability * Remove some unwanted iso caching --- experimental/LieAlgebras/src/iso_gap_oscar.jl | 8 +- experimental/LieAlgebras/src/iso_oscar_gap.jl | 78 +++++++++++-------- .../LieAlgebras/test/iso_gap_oscar-test.jl | 2 - .../LieAlgebras/test/iso_oscar_gap-test.jl | 1 - src/GAP/iso_oscar_gap.jl | 4 +- src/GAP/wrappers.jl | 1 + 6 files changed, 49 insertions(+), 45 deletions(-) diff --git a/experimental/LieAlgebras/src/iso_gap_oscar.jl b/experimental/LieAlgebras/src/iso_gap_oscar.jl index 7e5e259a3b86..f011aeb72694 100644 --- a/experimental/LieAlgebras/src/iso_gap_oscar.jl +++ b/experimental/LieAlgebras/src/iso_gap_oscar.jl @@ -26,9 +26,7 @@ function _iso_gap_oscar_abstract_lie_algebra( LO = _abstract_lie_algebra_from_GAP(LG, coeffs_iso, s) finv, f = _iso_oscar_gap_lie_algebra_functions(LO, LG, inv(coeffs_iso)) - iso = MapFromFunc(LG, LO, f, finv) - set_attribute!(LO, :iso_oscar_gap => inv(iso)) - return iso + return MapFromFunc(LG, LO, f, finv) end function _iso_gap_oscar_linear_lie_algebra( @@ -39,9 +37,7 @@ function _iso_gap_oscar_linear_lie_algebra( LO = _linear_lie_algebra_from_GAP(LG, coeffs_iso, s) finv, f = _iso_oscar_gap_lie_algebra_functions(LO, LG, inv(coeffs_iso)) - iso = MapFromFunc(LG, LO, f, finv) - set_attribute!(LO, :iso_oscar_gap => inv(iso)) - return iso + return MapFromFunc(LG, LO, f, finv) end function _abstract_lie_algebra_from_GAP( diff --git a/experimental/LieAlgebras/src/iso_oscar_gap.jl b/experimental/LieAlgebras/src/iso_oscar_gap.jl index 1b8aa40ac95e..b01734970c02 100644 --- a/experimental/LieAlgebras/src/iso_oscar_gap.jl +++ b/experimental/LieAlgebras/src/iso_oscar_gap.jl @@ -22,7 +22,7 @@ function _iso_oscar_gap_lie_algebra_functions( return (f, finv) end -function _iso_oscar_gap(LO::LinearLieAlgebra) +function _iso_oscar_gap(LO::LinearLieAlgebra; set_attributes::Bool=true) coeffs_iso = Oscar.iso_oscar_gap(coefficient_ring(LO)) LG = GAP.Globals.LieAlgebra( codomain(coeffs_iso), @@ -32,6 +32,12 @@ function _iso_oscar_gap(LO::LinearLieAlgebra) f, finv = _iso_oscar_gap_lie_algebra_functions(LO, LG, coeffs_iso) + if set_attributes && has_root_system(LO) + # we need to construct the root system in GAP as otherwise it may detect a different order of simple roots + RO = root_system(LO) + _iso_oscar_gap_set_root_system(LG, RO) + end + return MapFromFunc(LO, LG, f, finv) end @@ -41,17 +47,17 @@ function _iso_oscar_gap(LO::AbstractLieAlgebra; set_attributes::Bool=true) [ [ begin - pairs = filter(pair -> !iszero(last(pair)), collect(enumerate(_matrix(xi * xj)))) - (map(first, pairs), GAP.Obj[coeffs_iso(c) for c in map(last, pairs)]) - end for xj in basis(LO) - ] for xi in basis(LO) + pairs = collect(LO.struct_consts[i, j]) + (first.(pairs), GAP.Obj[coeffs_iso(c) for c in last.(pairs)]) + end for j in 1:dim(LO) + ] for i in 1:dim(LO) ] -1 coeffs_iso(zero(coefficient_ring(LO))) ] - LG = GAP.Globals.LieAlgebraByStructureConstants( - codomain(coeffs_iso), GAP.Obj(sc_table_G; recursive=true) + LG = GAPWrap.LieAlgebraByStructureConstants( + codomain(coeffs_iso), GapObj(sc_table_G; recursive=true) ) f, finv = _iso_oscar_gap_lie_algebra_functions(LO, LG, coeffs_iso) @@ -59,34 +65,38 @@ function _iso_oscar_gap(LO::AbstractLieAlgebra; set_attributes::Bool=true) if set_attributes && has_root_system(LO) # we need to construct the root system in GAP as otherwise it may detect a different order of simple roots RO = root_system(LO) - RG = GAP.Globals.Objectify( - GAP.Globals.NewType( - GAP.Globals.NewFamily(GAP.Obj("RootSystemFam"), GAP.Globals.IsObject), - GAP.evalstr("IsAttributeStoringRep and IsRootSystemFromLieAlgebra")), - GAP.GapObj(Dict{Symbol,Any}())) - GAP.Globals.SetUnderlyingLieAlgebra(RG, LG) - - cartan_trO = transpose(cartan_matrix(RO)) - transform_root(r::RootSpaceElem) = GAP.Obj(coefficients(r) * cartan_trO)[1] - GAP.Globals.SetPositiveRoots(RG, GAP.Obj(transform_root.(positive_roots(RO)))) - GAP.Globals.SetNegativeRoots(RG, GAP.Obj(transform_root.(negative_roots(RO)))) - GAP.Globals.SetSimpleSystem(RG, GAP.Obj(transform_root.(simple_roots(RO)))) - can_basisG = GAP.Globals.CanonicalBasis(LG) - pos_root_vectorsG = can_basisG[1:n_positive_roots(RO)] - neg_root_vectorsG = can_basisG[(n_positive_roots(RO) + 1):(2 * n_positive_roots(RO))] - csa_basisG = can_basisG[(2 * n_positive_roots(RO) + 1):end] - GAP.Globals.SetPositiveRootVectors(RG, pos_root_vectorsG) - GAP.Globals.SetNegativeRootVectors(RG, neg_root_vectorsG) - GAP.Globals.SetCanonicalGenerators( - RG, GAP.Obj([pos_root_vectorsG, neg_root_vectorsG, csa_basisG]) - ) - GAP.Globals.SetChevalleyBasis( - LG, GAP.Obj([pos_root_vectorsG, neg_root_vectorsG, csa_basisG]) - ) - - GAP.Globals.SetCartanMatrix(RG, GAP.Obj(cartan_trO)) - GAP.Globals.SetRootSystem(LG, RG) + _iso_oscar_gap_set_root_system(LG, RO) end return MapFromFunc(LO, LG, f, finv) end + +function _iso_oscar_gap_set_root_system(LG::GapObj, RO::RootSystem) + RG = GAP.Globals.Objectify( + GAP.Globals.NewType( + GAP.Globals.NewFamily(GAP.Obj("RootSystemFam"), GAP.Globals.IsObject), + GAP.evalstr("IsAttributeStoringRep and IsRootSystemFromLieAlgebra")), + GAP.GapObj(Dict{Symbol,Any}())) + GAP.Globals.SetUnderlyingLieAlgebra(RG, LG) + + cartan_trO = transpose(cartan_matrix(RO)) + transform_root(r::RootSpaceElem) = GAP.Obj(coefficients(r) * cartan_trO)[1] + GAP.Globals.SetPositiveRoots(RG, GAP.Obj(transform_root.(positive_roots(RO)))) + GAP.Globals.SetNegativeRoots(RG, GAP.Obj(transform_root.(negative_roots(RO)))) + GAP.Globals.SetSimpleSystem(RG, GAP.Obj(transform_root.(simple_roots(RO)))) + can_basisG = GAP.Globals.CanonicalBasis(LG) + pos_root_vectorsG = can_basisG[1:n_positive_roots(RO)] + neg_root_vectorsG = can_basisG[(n_positive_roots(RO) + 1):(2 * n_positive_roots(RO))] + csa_basisG = can_basisG[(2 * n_positive_roots(RO) + 1):end] + GAP.Globals.SetPositiveRootVectors(RG, pos_root_vectorsG) + GAP.Globals.SetNegativeRootVectors(RG, neg_root_vectorsG) + GAP.Globals.SetCanonicalGenerators( + RG, GAP.Obj([pos_root_vectorsG, neg_root_vectorsG, csa_basisG]) + ) + GAP.Globals.SetChevalleyBasis( + LG, GAP.Obj([pos_root_vectorsG, neg_root_vectorsG, csa_basisG]) + ) + + GAP.Globals.SetCartanMatrix(RG, GAP.Obj(cartan_trO)) + GAP.Globals.SetRootSystem(LG, RG) +end diff --git a/experimental/LieAlgebras/test/iso_gap_oscar-test.jl b/experimental/LieAlgebras/test/iso_gap_oscar-test.jl index 6870438dfc5a..206d5b2d3ba9 100644 --- a/experimental/LieAlgebras/test/iso_gap_oscar-test.jl +++ b/experimental/LieAlgebras/test/iso_gap_oscar-test.jl @@ -6,8 +6,6 @@ function test_iso_gap_oscar(LG, oscarT; num_random_tests::Int=10) LO = codomain(iso) @test LO isa oscarT - @test codomain(Oscar.iso_oscar_gap(LO)) === LG - @test iso === Oscar.iso_gap_oscar(LG) # test caching for _ in 1:num_random_tests diff --git a/experimental/LieAlgebras/test/iso_oscar_gap-test.jl b/experimental/LieAlgebras/test/iso_oscar_gap-test.jl index f01c34998365..06682e11a2bf 100644 --- a/experimental/LieAlgebras/test/iso_oscar_gap-test.jl +++ b/experimental/LieAlgebras/test/iso_oscar_gap-test.jl @@ -5,7 +5,6 @@ function test_iso_oscar_gap(LO::LieAlgebra; num_random_tests::Int=10) @test domain(iso) === LO LG = codomain(iso) @test GAPWrap.IsLieAlgebra(LG) - # @test codomain(Oscar.iso_gap_oscar(LG)) === LO @test iso === Oscar.iso_oscar_gap(LO) # test caching diff --git a/src/GAP/iso_oscar_gap.jl b/src/GAP/iso_oscar_gap.jl index 6cbf343306db..7d87de40db34 100644 --- a/src/GAP/iso_oscar_gap.jl +++ b/src/GAP/iso_oscar_gap.jl @@ -402,7 +402,7 @@ function _iso_oscar_gap(FO::QQAbField) end """ - Oscar.iso_oscar_gap(R) -> Map{T, GapObj} + Oscar.iso_oscar_gap(R::T) -> Map{T, GapObj} Return an isomorphism `f` with domain `R` and `codomain` a GAP object `S`. @@ -481,7 +481,7 @@ true structure is not fully supported in GAP will likely cause overhead at runtime. """ -@attr Map function iso_oscar_gap(F) +@attr Map{T, GapObj} function iso_oscar_gap(F::T) where T return _iso_oscar_gap(F) end diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index a12f1e309d6b..5cdfbc00cd2e 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -270,6 +270,7 @@ GAP.@wrap LargestMovedPoint(x::Any)::Int GAP.@wrap LeftActingDomain(x::GapObj)::GapObj GAP.@wrap LetterRepAssocWord(x::GapObj)::GapObj GAP.@wrap LibInfoCharacterTable(x::GapObj)::GapObj +GAP.@wrap LieAlgebraByStructureConstants(x::GapObj, y::GapObj)::GapObj GAP.@wrap LinearCharacters(x::GapObj)::GapObj GAP.@wrap LinearCombination(x::GapObj, y::GapObj)::GapObj GAP.@wrap ListPerm(x::GapObj)::GapObj From 7b49bb4bb8a5bf204c3d7474bc14f77c1e2ac9d3 Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Wed, 23 Oct 2024 10:20:45 +0200 Subject: [PATCH 05/84] Extend `is_invertible_with_inverse` to rings of type `MPolyQuoRing{<:MPolyDecRingElem}` (#4226) * Extend `is_invertible_with_inverse` to rings of type `MPolyQuoRing{<:MPolyDecRingElem}` * correction * Adressing remark by @joschmitt * correction * add extended function to docu * correction --- .../src/CommutativeAlgebra/affine_algebras.md | 4 ++ .../docs/src/AbstractBundles.md | 2 +- .../src/IntersectionTheory.jl | 2 + experimental/IntersectionTheory/src/Main.jl | 33 ++++++++++-- src/Rings/MPolyQuo.jl | 54 +++++++++++++++++++ 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/docs/src/CommutativeAlgebra/affine_algebras.md b/docs/src/CommutativeAlgebra/affine_algebras.md index ed54c6f60af0..9e65ec7002be 100644 --- a/docs/src/CommutativeAlgebra/affine_algebras.md +++ b/docs/src/CommutativeAlgebra/affine_algebras.md @@ -171,6 +171,10 @@ simplify(f::MPolyQuoRingElem) ==(f::MPolyQuoRingElem{T}, g::MPolyQuoRingElem{T}) where T ``` +```@docs +is_invertible_with_inverse(f::MPolyQuoRingElem) +``` + In the graded case, we additionally have: ```@docs diff --git a/experimental/IntersectionTheory/docs/src/AbstractBundles.md b/experimental/IntersectionTheory/docs/src/AbstractBundles.md index 8377ecfd9435..fbe2bf454387 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractBundles.md +++ b/experimental/IntersectionTheory/docs/src/AbstractBundles.md @@ -39,7 +39,7 @@ top_chern_class(F::AbstractBundle) ``` ```@docs -segre_class(F::AbstractBundle) +total_segre_class(F::AbstractBundle) ``` ```@docs diff --git a/experimental/IntersectionTheory/src/IntersectionTheory.jl b/experimental/IntersectionTheory/src/IntersectionTheory.jl index c2ae9b1886c4..6354f3e43a17 100644 --- a/experimental/IntersectionTheory/src/IntersectionTheory.jl +++ b/experimental/IntersectionTheory/src/IntersectionTheory.jl @@ -55,6 +55,7 @@ export pushforward export schubert_class export schubert_classes export schur_functor +export total_segre_class export segre_class export structure_map export tangent_bundle @@ -134,6 +135,7 @@ export pushforward export schubert_class export schubert_classes export schur_functor +export total_segre_class export segre_class export structure_map export tangent_bundle diff --git a/experimental/IntersectionTheory/src/Main.jl b/experimental/IntersectionTheory/src/Main.jl index d2a1dd5406ea..5f181a4637a6 100644 --- a/experimental/IntersectionTheory/src/Main.jl +++ b/experimental/IntersectionTheory/src/Main.jl @@ -90,6 +90,12 @@ AbstractBundle of rank 2 on AbstractVariety of dim 6 julia> total_chern_class(Q) c[1]^2 - c[1] - c[2] + 1 +julia> chern_class(Q, 1) +-c[1] + +julia> chern_class(Q, 2) +c[1]^2 - c[2] + ``` """ total_chern_class(F::AbstractBundle) = ( @@ -161,18 +167,37 @@ julia> top_chern_class(F) top_chern_class(F::AbstractBundle) = chern_class(F, F.rank) @doc raw""" - segre_class(F::AbstractBundle) + total_segre_class(F::AbstractBundle) Return the total Segre class of `F`. + +# Examples +```jldoctest +julia> G = abstract_grassmannian(3,5) +AbstractVariety of dim 6 + +julia> Q = tautological_bundles(G)[2] +AbstractBundle of rank 2 on AbstractVariety of dim 6 + +julia> C = total_chern_class(Q) +c[1]^2 - c[1] - c[2] + 1 + +julia> S = total_segre_class(Q) +c[1] + c[2] + c[3] + 1 + +julia> C*S +1 + +``` """ -segre_class(F::AbstractBundle) = inv(total_chern_class(F)) +total_segre_class(F::AbstractBundle) = inv(total_chern_class(F)) @doc raw""" segre_class(F::AbstractBundle, k::Int) -Retuen the `k`-th Segre class of `F`. +Return the `k`-th Segre class of `F`. """ -segre_class(F::AbstractBundle, k::Int) = segre_class(F)[k] +segre_class(F::AbstractBundle, k::Int) = total_segre_class(F)[k] @doc raw""" todd_class(F::AbstractBundle) diff --git a/src/Rings/MPolyQuo.jl b/src/Rings/MPolyQuo.jl index 79f407830116..a07fae42f54b 100644 --- a/src/Rings/MPolyQuo.jl +++ b/src/Rings/MPolyQuo.jl @@ -1100,6 +1100,33 @@ end one(Q::MPolyQuoRing) = Q(1) +@doc raw""" + is_invertible_with_inverse(f::MPolyQuoRingElem) + +If `f` is invertible with inverse `g`, say, return `(true, g)`. Otherwise, return `(false, f)`. + +# Examples + +```jldoctest +julia> R, c = polynomial_ring(QQ, :c => (1:3)); + +julia> R, c = grade(R, [1, 2, 3]); + +julia> I = ideal(R, [ -c[1]^3 + 2*c[1]*c[2] - c[3], c[1]^4 - 3*c[1]^2*c[2] + 2*c[1]*c[3] + c[2]^2,-c[1]^5 + 4*c[1]^3*c[2] - 3*c[1]^2*c[3] - 3*c[1]*c[2]^2 + 2*c[2]*c[3]]); + +julia> A, _ = quo(R, I); + +julia> f = A(c[1]^2 - c[1] - c[2] + 1) +c[1]^2 - c[1] - c[2] + 1 + +julia> tt, g = is_invertible_with_inverse(f) +(true, c[1] + c[2] + c[3] + 1) + +julia> f*g +1 + +``` +""" function is_invertible_with_inverse(a::MPolyQuoRingElem) # TODO: # Eventually, the code below should be replaced @@ -1111,6 +1138,11 @@ function is_invertible_with_inverse(a::MPolyQuoRingElem) Q = parent(a) J = oscar_groebner_basis(Q) J = vcat(J, [a.f]) + + if Q isa MPolyQuoRing{<:MPolyDecRingElem} + J = [x.f for x in J] + end + j, T = standard_basis_with_transformation_matrix(ideal(J)) if is_constant(j[1]) && is_unit(first(coefficients(j[1]))) @assert ncols(T) == 1 @@ -1121,6 +1153,28 @@ end is_unit(a::MPolyQuoRingElem) = is_invertible_with_inverse(a)[1] +@doc raw""" + inv(f::MPolyQuoRingElem) + +If `f` is invertible, return its inverse. Otherwise, throw an error. + +# Examples + +```jldoctest +julia> R, c = polynomial_ring(QQ, :c => (1:3)); + +julia> I = ideal(R, [ -c[1]^3 + 2*c[1]*c[2] - c[3], c[1]^4 - 3*c[1]^2*c[2] + 2*c[1]*c[3] + c[2]^2,-c[1]^5 + 4*c[1]^3*c[2] - 3*c[1]^2*c[3] - 3*c[1]*c[2]^2 + 2*c[2]*c[3]]); + +julia> A, _ = quo(R, I); + +julia> f = A(c[1]^2 - c[1] - c[2] + 1) +c[1]^2 - c[1] - c[2] + 1 + +julia> inv(f) +c[1] + c[2] + c[3] + 1 + +``` +""" function inv(a::MPolyQuoRingElem) fl, b = is_invertible_with_inverse(a) fl || error("Element not invertible") From d0adefe0b5465e7dc2986b5044b56428c424d9ec Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 23 Oct 2024 12:45:38 +0200 Subject: [PATCH 06/84] Add parent checks to PBW algebra arithmetics (#4231) --- src/Rings/PBWAlgebra.jl | 6 +++++- src/Rings/PBWAlgebraQuo.jl | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Rings/PBWAlgebra.jl b/src/Rings/PBWAlgebra.jl index 515989d88413..95456c792c24 100644 --- a/src/Rings/PBWAlgebra.jl +++ b/src/Rings/PBWAlgebra.jl @@ -266,14 +266,17 @@ function one(R::PBWAlgRing) end function Base.:(==)(a::PBWAlgElem, b::PBWAlgElem) + check_parent(a, b) return a.sdata == b.sdata end function Base.:+(a::PBWAlgElem, b::PBWAlgElem) + check_parent(a, b) return PBWAlgElem(parent(a), a.sdata + b.sdata) end function Base.:-(a::PBWAlgElem, b::PBWAlgElem) + check_parent(a, b) return PBWAlgElem(parent(a), a.sdata - b.sdata) end @@ -282,7 +285,8 @@ function Base.:-(a::PBWAlgElem) end function Base.:*(a::PBWAlgElem, b::PBWAlgElem) - return PBWAlgElem(parent(a), a.sdata*b.sdata) + check_parent(a, b) + return PBWAlgElem(parent(a), a.sdata * b.sdata) end function Base.:^(a::PBWAlgElem, b::Int) diff --git a/src/Rings/PBWAlgebraQuo.jl b/src/Rings/PBWAlgebraQuo.jl index a717e729c19d..60082c3a8369 100644 --- a/src/Rings/PBWAlgebraQuo.jl +++ b/src/Rings/PBWAlgebraQuo.jl @@ -169,12 +169,12 @@ function Base.:(==)(a::PBWAlgQuoElem, b::PBWAlgQuoElem) end function Base.:+(a::PBWAlgQuoElem, b::PBWAlgQuoElem) - @assert parent(a) == parent(b) + check_parent(a, b) return PBWAlgQuoElem(parent(a), a.data + b.data) end function Base.:-(a::PBWAlgQuoElem, b::PBWAlgQuoElem) - @assert parent(a) == parent(b) + check_parent(a, b) return PBWAlgQuoElem(parent(a), a.data - b.data) end @@ -183,7 +183,8 @@ function Base.:-(a::PBWAlgQuoElem) end function Base.:*(a::PBWAlgQuoElem, b::PBWAlgQuoElem) - return PBWAlgQuoElem(parent(a), a.data*b.data) + check_parent(a, b) + return PBWAlgQuoElem(parent(a), a.data * b.data) end function Base.:^(a::PBWAlgQuoElem, b::Int) From bcb3bd279f9f88c25daa9ca4c1e2b371c3e6f014 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 23 Oct 2024 13:37:24 +0200 Subject: [PATCH 07/84] show methods: lower case halfspaces and hyperplanes (#4232) --- .../Polyhedra/constructions.md | 4 ++-- src/Groups/matrices/MatGrp.jl | 2 +- src/PolyhedralGeometry/Cone/properties.jl | 4 ++-- src/PolyhedralGeometry/Polyhedron/properties.jl | 10 +++++----- .../Polyhedron/standard_constructions.jl | 16 ++++++++-------- .../polyhedral-geometry/pentagon.jlcon | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/src/PolyhedralGeometry/Polyhedra/constructions.md b/docs/src/PolyhedralGeometry/Polyhedra/constructions.md index 4eefdeffd57d..fac07af46b3a 100644 --- a/docs/src/PolyhedralGeometry/Polyhedra/constructions.md +++ b/docs/src/PolyhedralGeometry/Polyhedra/constructions.md @@ -26,13 +26,13 @@ julia> P = polyhedron(([-1 0; 1 0], [0,1]), ([0 1], [0])) Polyhedron in ambient dimension 2 julia> facets(P) -2-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^2 described by: +2-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^2 described by: -x_1 <= 0 x_1 <= 1 julia> affine_hull(P) -1-element SubObjectIterator{AffineHyperplane{QQFieldElem}} over the Hyperplanes of R^2 described by: +1-element SubObjectIterator{AffineHyperplane{QQFieldElem}} over the hyperplanes of R^2 described by: x_2 = 0 diff --git a/src/Groups/matrices/MatGrp.jl b/src/Groups/matrices/MatGrp.jl index c74f12f83a54..2c5ab5552258 100644 --- a/src/Groups/matrices/MatGrp.jl +++ b/src/Groups/matrices/MatGrp.jl @@ -559,7 +559,7 @@ compute_order(G::GAPGroup) = ZZRingElem(GAPWrap.Size(G.X)) function compute_order(G::MatrixGroup{T}) where {T <: Union{AbsSimpleNumFieldElem, QQFieldElem}} #= - - For a matrix group G over the Rationals or over a number field, + - For a matrix group G over the rationals or over a number field, the GAP group G.X does usually not store the flag `IsHandledByNiceMonomorphism`. - If we know a reasonable ("nice") faithful permutation action of `G` in advance, we can set this flag in `G.X` to true and store the action homomorphism in `G.X`, diff --git a/src/PolyhedralGeometry/Cone/properties.jl b/src/PolyhedralGeometry/Cone/properties.jl index 7a4d3f964f0f..9aaf989ef0cb 100644 --- a/src/PolyhedralGeometry/Cone/properties.jl +++ b/src/PolyhedralGeometry/Cone/properties.jl @@ -543,7 +543,7 @@ julia> c = positive_hull([1 0 0; 0 1 0; 1 1 1]) Polyhedral cone in ambient dimension 3 julia> f = facets(Halfspace, c) -3-element SubObjectIterator{LinearHalfspace{QQFieldElem}} over the Halfspaces of R^3 described by: +3-element SubObjectIterator{LinearHalfspace{QQFieldElem}} over the halfspaces of R^3 described by: -x_3 <= 0 -x_1 + x_3 <= 0 -x_2 + x_3 <= 0 @@ -616,7 +616,7 @@ $H = \{ (x_1, x_2, x_3) | x_3 = 0 \}$. julia> c = positive_hull([1 0 0; 0 1 0]); julia> linear_span(c) -1-element SubObjectIterator{LinearHyperplane{QQFieldElem}} over the Hyperplanes of R^3 described by: +1-element SubObjectIterator{LinearHyperplane{QQFieldElem}} over the hyperplanes of R^3 described by: x_3 = 0 ``` """ diff --git a/src/PolyhedralGeometry/Polyhedron/properties.jl b/src/PolyhedralGeometry/Polyhedron/properties.jl index 63ecdbbf44cb..a7ad3ae9656a 100644 --- a/src/PolyhedralGeometry/Polyhedron/properties.jl +++ b/src/PolyhedralGeometry/Polyhedron/properties.jl @@ -510,7 +510,7 @@ julia> facets(Polyhedron, C) Polytope in ambient dimension 3 julia> facets(Halfspace, C) -6-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^3 described by: +6-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^3 described by: -x_1 <= 1 x_1 <= 1 -x_2 <= 1 @@ -593,7 +593,7 @@ We can retrieve the six facets of the 3-dimensional cube this way: julia> C = cube(3); julia> facets(C) -6-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^3 described by: +6-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^3 described by: -x_1 <= 1 x_1 <= 1 -x_2 <= 1 @@ -998,7 +998,7 @@ $P = \{ (x_1, x_2, x_3, x_4) | x_3 = 2 ∧ x_4 = 5 \}$. julia> t = convex_hull([0 0 2 5; 1 0 2 5; 0 1 2 5]); julia> affine_hull(t) -2-element SubObjectIterator{AffineHyperplane{QQFieldElem}} over the Hyperplanes of R^4 described by: +2-element SubObjectIterator{AffineHyperplane{QQFieldElem}} over the hyperplanes of R^4 described by: x_3 = 2 x_4 = 5 ``` @@ -1831,7 +1831,7 @@ function Base.show(io::IO, H::SubObjectIterator{<:Halfspace}) print(io, "$s-element $t") if !isempty(H) n = length(normal_vector(H[1])) - print(io, " over the Halfspaces of R^$n described by:\n") + print(io, " over the halfspaces of R^$n described by:\n") if s < d print_constraints(io, H) else @@ -1854,7 +1854,7 @@ function Base.show(io::IO, H::SubObjectIterator{<:Hyperplane}) print(io, "$s-element $t") if !isempty(H) n = length(normal_vector(H[1])) - print(io, " over the Hyperplanes of R^$n described by:\n") + print(io, " over the hyperplanes of R^$n described by:\n") if s < d print_constraints(io, H) else diff --git a/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl b/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl index 9e18a71d8113..29e51665b074 100644 --- a/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl +++ b/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl @@ -618,7 +618,7 @@ julia> s = simplex(7) Polytope in ambient dimension 7 julia> facets(s) -8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^7 described by: +8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^7 described by: -x_1 <= 0 -x_2 <= 0 -x_3 <= 0 @@ -632,7 +632,7 @@ julia> t = simplex(7, 5) Polytope in ambient dimension 7 julia> facets(t) -8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^7 described by: +8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^7 described by: -x_1 <= 0 -x_2 <= 0 -x_3 <= 0 @@ -674,7 +674,7 @@ julia> C = cross_polytope(3) Polytope in ambient dimension 3 julia> facets(C) -8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^3 described by: +8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^3 described by: x_1 + x_2 + x_3 <= 1 -x_1 + x_2 + x_3 <= 1 x_1 - x_2 + x_3 <= 1 @@ -688,7 +688,7 @@ julia> D = cross_polytope(3, 2) Polytope in ambient dimension 3 julia> facets(D) -8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^3 described by: +8-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^3 described by: x_1 + x_2 + x_3 <= 2 -x_1 + x_2 + x_3 <= 2 x_1 - x_2 + x_3 <= 2 @@ -1408,7 +1408,7 @@ julia> vertices(A) [4, 1, 10, 6] julia> facets(A) -5-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^4 described by: +5-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^4 described by: -x_1 <= -1 -2*x_1 - 2*x_2 <= -10 -x_2 <= -1 @@ -1637,14 +1637,14 @@ julia> G = hypersimplex(3,4,no_facets=true) Polytope in ambient dimension 4 julia> facets(G) -4-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^4 described by: +4-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^4 described by: x_4 <= 1 x_3 <= 1 -x_1 - x_3 - x_4 <= -2 x_1 <= 1 julia> facets(H) -4-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^4 described by: +4-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^4 described by: x_4 <= 1 x_3 <= 1 -x_1 - x_3 - x_4 <= -2 @@ -2253,7 +2253,7 @@ julia> vertices(a) [0, 3, 4, 15, 16] julia> facets(a) -9-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^5 described by: +9-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^5 described by: x_1 - x_2 <= -1 x_1 - x_3 <= -4 x_1 - x_4 <= -9 diff --git a/test/book/cornerstones/polyhedral-geometry/pentagon.jlcon b/test/book/cornerstones/polyhedral-geometry/pentagon.jlcon index cf96a5f18ba5..e15935e9bb47 100644 --- a/test/book/cornerstones/polyhedral-geometry/pentagon.jlcon +++ b/test/book/cornerstones/polyhedral-geometry/pentagon.jlcon @@ -4,7 +4,7 @@ julia> P = convex_hull(points) Polyhedron in ambient dimension 2 julia> facets(P) -5-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the Halfspaces of R^2 described by: +5-element SubObjectIterator{AffineHalfspace{QQFieldElem}} over the halfspaces of R^2 described by: -x_1 <= 1 -x_1 + x_2 <= 1 x_1 - 2*x_2 <= 1 From ba9558f9e0aff6db32518b8e7d112a13e61f25b1 Mon Sep 17 00:00:00 2001 From: Marcel Salmon <125460956+Syz-MS@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:08:11 +0200 Subject: [PATCH 08/84] Tjurina module (#4234) --- experimental/Schemes/src/Schemes.jl | 1 + experimental/Schemes/src/Tjurina.jl | 93 +++++++++++++++++++++++ test/AlgebraicGeometry/Schemes/Tjurina.jl | 14 ++++ 3 files changed, 108 insertions(+) diff --git a/experimental/Schemes/src/Schemes.jl b/experimental/Schemes/src/Schemes.jl index 215ff86cd640..46573467e062 100644 --- a/experimental/Schemes/src/Schemes.jl +++ b/experimental/Schemes/src/Schemes.jl @@ -65,6 +65,7 @@ export is_finitely_determined export determinacy_bound export sharper_determinacy_bound export is_contact_equivalent +export tjurina_module # Deprecated after 0.15 diff --git a/experimental/Schemes/src/Tjurina.jl b/experimental/Schemes/src/Tjurina.jl index 368cdf553ecb..91e20575a4f3 100644 --- a/experimental/Schemes/src/Tjurina.jl +++ b/experimental/Schemes/src/Tjurina.jl @@ -738,3 +738,96 @@ function is_contact_equivalent(X::HypersurfaceGerm, Y::HypersurfaceGerm) g = defining_ideal(Y)[1] return is_contact_equivalent(f, g) end + + + + +################################################################################ + +##### Tjurina module ##### + +################################################################################ + + +@doc raw""" + tjurina_module(X::CompleteIntersectionGerm) + +Return the Tjurina module of the complete intersection germ `(X,p)` at the point `p`. +# Examples +```jldoctest +julia> R, (x,y,z) = QQ["x","y","z"]; + +julia> I = ideal(R, [x^2+y^2-z^2, x*y]); + +julia> X = CompleteIntersectionGerm(spec(quo(R, I)[1]), [0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal (x^2 + y^2 - z^2, x*y) + at complement of maximal ideal of point (0, 0, 0) + +julia> T = tjurina_module(X) +Subquotient of Submodule with 2 generators +1 -> e[1] +2 -> e[2] +by Submodule with 7 generators +1 -> 2*x*e[1] + y*e[2] +2 -> 2*y*e[1] + x*e[2] +3 -> -2*z*e[1] +4 -> (x^2 + y^2 - z^2)*e[1] +5 -> (x^2 + y^2 - z^2)*e[2] +6 -> x*y*e[1] +7 -> x*y*e[2] + +julia> vector_space_basis(T) +5-element Vector{Any}: + e[1] + e[2] + y*e[1] + y*e[2] + z*e[2] +``` +""" +function tjurina_module(X::CompleteIntersectionGerm) + I = defining_ideal(X) + k = ngens(I) + R = base_ring(I) + M = free_module(R, k) + J = jacobian_matrix(gens(I)) + S = sub(M,J)[1] + (I*M)[1] + return quo(M, S)[1] +end + + + +@doc raw""" + tjurina_number(X::CompleteIntersectionGerm) + +Return Tjurina number of the complete intersection germ `(X,p)` at the point `p`. +# Examples +```jldoctest +julia> R, (x,y,z) = QQ["x","y","z"]; + +julia> I = ideal(R, [x^2+y^2-z^2, x*y]); + +julia> X = CompleteIntersectionGerm(spec(quo(R, I)[1]), [0,0,0]) +Spectrum + of localization + of quotient + of multivariate polynomial ring in 3 variables x, y, z + over rational field + by ideal (x^2 + y^2 - z^2, x*y) + at complement of maximal ideal of point (0, 0, 0) + +julia> tjurina_number(X) +5 +``` +""" +function tjurina_number(X::CompleteIntersectionGerm) + d = vector_space_dimension(tjurina_module(X)) + return d == -1 ? PosInf() : d +end + + diff --git a/test/AlgebraicGeometry/Schemes/Tjurina.jl b/test/AlgebraicGeometry/Schemes/Tjurina.jl index af5b33ac2495..a9014b53b246 100644 --- a/test/AlgebraicGeometry/Schemes/Tjurina.jl +++ b/test/AlgebraicGeometry/Schemes/Tjurina.jl @@ -252,3 +252,17 @@ end @test !is_contact_equivalent(L(x^9+y^2+z^2), L(x^3+y^5)) end + + +@testset "Tjurina number complete intersection germ" begin + A = affine_space(QQ, 3) + R = coordinate_ring(A); + (x,y,z) = gens(R); + X = CompleteIntersectionGerm(spec(quo(R,ideal(R,[x^2+y^2+z^2, x^2+2*y^2+3*z^2]))[1]), [0,0,0]) + @test tjurina_number(X) == 5 + S = spec(quo(R,ideal(R,[x^5+y^6+z^7+x*y*z]))[1]) + @test tjurina_number(HypersurfaceGerm(S, [0,0,0])) == tjurina_number(CompleteIntersectionGerm(S, [0,0,0])) + Y = CompleteIntersectionGerm(spec(quo(R,ideal(R,[x^2+y^2, x^2+2*y^2]))[1]), [0,0,0]) + @test tjurina_number(Y) == PosInf() +end + From 73c88fa30e6d5dc536c638dda9a08d88cb308539 Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Thu, 24 Oct 2024 03:40:14 +0200 Subject: [PATCH 09/84] Ideal saturation: Resolving old question found in code (#4238) * Ideal saturation: Resolving old question found in code * Improvement, backwards compatibility * correction --- docs/src/CommutativeAlgebra/ideals.md | 2 +- src/Rings/mpoly-ideals.jl | 34 ++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/src/CommutativeAlgebra/ideals.md b/docs/src/CommutativeAlgebra/ideals.md index e04ad1a591c5..f7d1347fe6a0 100644 --- a/docs/src/CommutativeAlgebra/ideals.md +++ b/docs/src/CommutativeAlgebra/ideals.md @@ -134,7 +134,7 @@ Given two ideals $I, J$ of a ring $R$, the saturation of $I$ with respect to $J$ $I:J^{\infty} = \bigl\{ f \in R \:\big|\: f J^k \!\subset I {\text{ for some }}k\geq 1 \bigr\} = \textstyle{\bigcup\limits_{k=1}^{\infty} (I:J^k)}.$ ```@docs -saturation(I::MPolyIdeal{T}, J::MPolyIdeal{T}) where T +saturation(I::MPolyIdeal{T}, J::MPolyIdeal{T}; iteration::Bool=false) where T saturation_with_index(I::MPolyIdeal{T}, J::MPolyIdeal{T}) where T ``` diff --git a/src/Rings/mpoly-ideals.jl b/src/Rings/mpoly-ideals.jl index 27b717b3d013..629681cc028c 100644 --- a/src/Rings/mpoly-ideals.jl +++ b/src/Rings/mpoly-ideals.jl @@ -302,11 +302,16 @@ end # saturation ####################################################### @doc raw""" - saturation(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T + saturation(I::MPolyIdeal{T}, + J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I))); + iteration::Bool=false) where T Return the saturation of `I` with respect to `J`. + If the second ideal `J` is not given, the ideal generated by the generators (variables) of `base_ring(I)` is used. +If `iteration` is set to `true`, the saturation is done by carrying out successive ideal quotient computations as in the definition of saturation. Otherwise, a more sophisticated Gröbner basis approach is used which is typically faster. Applying the two approaches may lead to different generating sets of the saturation. + # Examples ```jldoctest julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]) @@ -338,27 +343,33 @@ julia> K = saturation(I) Ideal generated by z x*y + ``` """ -function saturation(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T - K, _ = Singular.saturation(singular_generators(I), singular_generators(J)) +function saturation(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I))); iteration::Bool=false) where T + if iteration + K, _ = Singular.saturation(singular_generators(I), singular_generators(J)) + else + K, _ = Singular.saturation2(singular_generators(I), singular_generators(J)) + end return MPolyIdeal(base_ring(I), K) end -# the following is corresponding to saturation2 from Singular -# TODO: think about how to use use this properly/automatically -function _saturation2(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T - K, _ = Singular.saturation2(singular_generators(I), singular_generators(J)) - return MPolyIdeal(base_ring(I), K) -end +_saturation2(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T = saturation(I, J) +# kept for backwards compatibility ####################################################### @doc raw""" - saturation_with_index(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T + saturation_with_index(I::MPolyIdeal{T}, + J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T + +Return $I:J^{\infty}$ together with the smallest integer $m$ such that $I:J^m = I:J^{\infty}$ (saturation index). -Return $I:J^{\infty}$ together with the smallest integer $m$ such that $I:J^m = I:J^{\infty}$. If the second ideal `J` is not given, the ideal generated by the generators (variables) of `base_ring(I)` is used. +!!! note + If the saturation index is not needed, we recommend to use `saturation(I, J)` which is typically faster. + # Examples ```jldoctest julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]) @@ -386,6 +397,7 @@ julia> K, m = saturation_with_index(I, J) julia> K, m = saturation_with_index(I) (Ideal (z, x*y), 2) + ``` """ function saturation_with_index(I::MPolyIdeal{T}, J::MPolyIdeal{T} = ideal(base_ring(I), gens(base_ring(I)))) where T From 31ca31b2a4d62b185e407db885e88533b79592a5 Mon Sep 17 00:00:00 2001 From: alexej-jordan <58329349+alexej-jordan@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:16:23 +0200 Subject: [PATCH 10/84] Improve stability of tests in PolyhedralGeometry (#4197) * introduced helper functions for stable tests in polyhedral geometry, applied to test/polyhedron.jl * increased stability for cone tests * increased stability for extended tests * increased stability for polyhedral_fan tests * increased stability for scalar_types tests * increased stability for polyhedral_complex tests * increased stability for subdivision_of_points tests * improved == for RayVector; forced RayVector != PointVector * fixed comparison between RayVectors; comparison between PointVector and RayVector now throws an error * fixed formatting for iterators.jl * added and adjusted commented test in cone.jl * increased code coverage for changes in iterators.jl * improved comparison between RayVectors * re-added test for matrix(ZZ, rays(Cone6)) * adjusted test for weights in subdivision_of_points.jl * improved comparison between RayVectors and added additional tests --- src/PolyhedralGeometry/iterators.jl | 21 +++++ test/PolyhedralGeometry/cone.jl | 82 ++++++++----------- test/PolyhedralGeometry/extended.jl | 14 ++-- test/PolyhedralGeometry/polyhedral_complex.jl | 30 +++---- test/PolyhedralGeometry/polyhedral_fan.jl | 30 ++++--- test/PolyhedralGeometry/polyhedron.jl | 24 ++---- test/PolyhedralGeometry/scalar_types.jl | 2 +- test/PolyhedralGeometry/setup_tests.jl | 24 ++++++ .../subdivision_of_points.jl | 10 +-- test/PolyhedralGeometry/types.jl | 19 ++++- 10 files changed, 139 insertions(+), 117 deletions(-) diff --git a/src/PolyhedralGeometry/iterators.jl b/src/PolyhedralGeometry/iterators.jl index 93f972e27575..f87b07a8d9d6 100644 --- a/src/PolyhedralGeometry/iterators.jl +++ b/src/PolyhedralGeometry/iterators.jl @@ -85,6 +85,27 @@ Return a `RayVector` resembling a ray from the origin through the point whose co """ ray_vector +function Base.:(==)(x::RayVector, y::RayVector) + ix = findfirst(!is_zero, x) + iy = findfirst(!is_zero, y) + ix == iy || return false + isnothing(ix) && return true + sign(x[ix]) == sign(y[iy]) || return false + return y[iy] * x.p == x[ix] * y.p +end + +function Base.:(==)(x::RayVector, y::AbstractVector) + ry = ray_vector(coefficient_field(x), y) + return x == ry +end + +Base.:(==)(x::AbstractVector, y::RayVector) = y == x + +Base.:(==)(::PointVector, ::RayVector) = + throw(ArgumentError("Cannot compare PointVector to RayVector")) +Base.:(==)(::RayVector, ::PointVector) = + throw(ArgumentError("Cannot compare PointVector to RayVector")) + ################################################################################ ######## Halfspaces and Hyperplanes ################################################################################ diff --git a/test/PolyhedralGeometry/cone.jl b/test/PolyhedralGeometry/cone.jl index 780870c10d2a..ba97410c6449 100644 --- a/test/PolyhedralGeometry/cone.jl +++ b/test/PolyhedralGeometry/cone.jl @@ -28,43 +28,38 @@ if T == QQFieldElem @test hilbert_basis(Cone1) isa SubObjectIterator{PointVector{ZZRingElem}} @test length(hilbert_basis(Cone1)) == 2 - @test hilbert_basis(Cone1) == [[1, 0], [0, 1]] - @test generator_matrix(hilbert_basis(Cone1)) == matrix(QQ, [1 0; 0 1]) + @test issetequal(hilbert_basis(Cone1), ray_vector.(Ref(ZZ), [[1, 0], [0, 1]])) + @test generator_matrix(hilbert_basis(Cone1)) == _oscar_matrix_from_property(ZZ, hilbert_basis(Cone1)) end @test n_rays(Cone1) == 2 @test rays(RayVector{T}, Cone1) isa SubObjectIterator{RayVector{T}} @test rays(Cone1) isa SubObjectIterator{RayVector{T}} @test rays(RayVector, Cone1) isa SubObjectIterator{RayVector{T}} - @test vector_matrix(rays(Cone1)) == matrix(f, [1 0; 0 1]) + @test issetequal(rays(Cone1), ray_vector.(Ref(f), [[1, 0], [0, 1]])) + @test vector_matrix(rays(Cone1)) == _oscar_matrix_from_property(f, rays(Cone1)) if T == QQFieldElem - @test matrix(QQ, rays(Cone1)) == matrix(QQ, [1 0; 0 1]) - @test matrix(ZZ, rays(Cone6)) == matrix(ZZ, [2 3; 2 5]) + @test matrix(QQ, rays(Cone1)) == _oscar_matrix_from_property(f, rays(Cone1)) + let r = rays(Cone6) + m = matrix(ZZ, r[1] == [1//3, 1//2] ? [2 3; 2 5] : [2 5; 2 3]) + @test matrix(ZZ, rays(Cone6)) == m + end end @test length(rays(Cone1)) == 2 - @test rays(Cone1) == [[1, 0], [0, 1]] - for S in [LinearHalfspace{T}, Cone{T}] + for S in [LinearHalfspace{T}, + Cone{T}] @test facets(S, Cone1) isa SubObjectIterator{S} @test length(facets(S, Cone1)) == 2 - if T == QQFieldElem - @test linear_inequality_matrix(facets(S, Cone1)) == matrix(QQ, [-1 0; 0 -1]) - @test Oscar.linear_matrix_for_polymake(facets(S, Cone1)) == [-1 0; 0 -1] - @test ray_indices(facets(S, Cone1)) == IncidenceMatrix([[2], [1]]) - @test IncidenceMatrix(facets(S, Cone1)) == IncidenceMatrix([[2], [1]]) - if S == LinearHalfspace{T} - @test facets(S, Cone1) == linear_halfspace.([f], [[-1, 0], [0, -1]]) - end + if S == LinearHalfspace{T} + @test issetequal(facets(S, Cone1), linear_halfspace.(Ref(f), [[-1, 0], [0, -1]])) else - @test linear_inequality_matrix(facets(S, Cone1)) == matrix(f, [0 -1; -1 0]) - @test Oscar.linear_matrix_for_polymake(facets(S, Cone1)) == [0 -1; -1 0] - @test ray_indices(facets(S, Cone1)) == IncidenceMatrix([[1], [2]]) - @test IncidenceMatrix(facets(S, Cone1)) == IncidenceMatrix([[1], [2]]) - if S == LinearHalfspace{T} - @test facets(S, Cone1) == linear_halfspace.([f], [[0, -1], [-1, 0]]) - end + @test issetequal(facets(S, Cone1), positive_hull.(Ref(f), [[1 0], [0 1]])) end + @test linear_inequality_matrix(facets(S, Cone1)) == _oscar_matrix_from_property(f, facets(S, Cone1)) + @test Oscar.linear_matrix_for_polymake(facets(S, Cone1)) == _polymake_matrix_from_property(facets(S, Cone1)) + @test _check_im_perm_rows(ray_indices(facets(S, Cone1)), [[1], [2]]) + @test _check_im_perm_rows(IncidenceMatrix(facets(S, Cone1)), [[1], [2]]) end - @test facets(IncidenceMatrix, Cone1) == - IncidenceMatrix(T == QQFieldElem ? [[2], [1]] : [[1], [2]]) + @test _check_im_perm_rows(facets(IncidenceMatrix, Cone1), [[1], [2]]) @test facets(Halfspace, Cone1) isa SubObjectIterator{LinearHalfspace{T}} @test facets(Cone1) isa SubObjectIterator{LinearHalfspace{T}} @test linear_span(Cone4) isa SubObjectIterator{LinearHyperplane{T}} @@ -92,43 +87,32 @@ end @test length(lineality_space(Cone2)) == 1 @test lineality_space(Cone2) == [L[1, :]] - @test vector_matrix(rays(Cone4)) == matrix(f, R) + @test vector_matrix(rays(Cone4)) == _oscar_matrix_from_property(f, rays(Cone4)) @test codim(Cone4) == 1 @test codim(Cone3) == 0 @test faces(Cone2, 2) isa SubObjectIterator{Cone{T}} @test length(faces(Cone2, 2)) == 2 @test faces(Cone4, 1) isa SubObjectIterator{Cone{T}} @test length(faces(Cone4, 1)) == 2 - if T == QQFieldElem - @test faces(Cone2, 2) == positive_hull.(T, [[0 0 1], [1 0 0]], [[0 1 0]]) - @test ray_indices(faces(Cone2, 2)) == IncidenceMatrix([[2], [1]]) - @test IncidenceMatrix(faces(Cone2, 2)) == IncidenceMatrix([[2], [1]]) - @test faces(IncidenceMatrix, Cone2, 2) == IncidenceMatrix([[2], [1]]) - @test faces(Cone4, 1) == positive_hull.(T, [[0 0 1], [1 0 0]]) - @test ray_indices(faces(Cone4, 1)) == IncidenceMatrix([[2], [1]]) - @test IncidenceMatrix(faces(Cone4, 1)) == IncidenceMatrix([[2], [1]]) - @test faces(IncidenceMatrix, Cone4, 1) == IncidenceMatrix([[2], [1]]) - else - @test faces(Cone2, 2) == positive_hull.([f], [[1 0 0], [0 0 1]], [[0 1 0]]) - @test ray_indices(faces(Cone2, 2)) == IncidenceMatrix([[1], [2]]) - @test IncidenceMatrix(faces(Cone2, 2)) == IncidenceMatrix([[1], [2]]) - @test faces(IncidenceMatrix, Cone2, 2) == IncidenceMatrix([[1], [2]]) - @test faces(Cone4, 1) == positive_hull.([f], [[1 0 0], [0 0 1]]) - @test ray_indices(faces(Cone4, 1)) == IncidenceMatrix([[1], [2]]) - @test IncidenceMatrix(faces(Cone4, 1)) == IncidenceMatrix([[1], [2]]) - @test faces(IncidenceMatrix, Cone4, 1) == IncidenceMatrix([[1], [2]]) - end - @test IncidenceMatrix(faces(Cone5, 1)) == IncidenceMatrix([[1], [2], [3], [4]]) + @test issetequal(faces(Cone2, 2), positive_hull.(Ref(f), [[1 0 0], [0 0 1]], [[0 1 0]])) + @test _check_im_perm_rows(ray_indices(faces(Cone2, 2)), [[1], [2]]) + @test _check_im_perm_rows(IncidenceMatrix(faces(Cone2, 2)), [[1], [2]]) + @test _check_im_perm_rows(faces(IncidenceMatrix, Cone2, 2), [[1], [2]]) + @test issetequal(faces(Cone4, 1), positive_hull.(Ref(f), [[0 0 1], [1 0 0]])) + @test _check_im_perm_rows(ray_indices(faces(Cone4, 1)), [[1], [2]]) + @test _check_im_perm_rows(IncidenceMatrix(faces(Cone4, 1)), [[1], [2]]) + @test _check_im_perm_rows(faces(IncidenceMatrix, Cone4, 1), [[1], [2]]) + @test _check_im_perm_rows(IncidenceMatrix(faces(Cone5, 1)), [[1], [2], [3], [4]]) @test isnothing(faces(Cone2, 1)) @test f_vector(Cone5) == [4, 4] @test f_vector(Cone2) == [0, 2] @test lineality_dim(Cone5) == 0 @test lineality_dim(Cone2) == 1 - @test facet_degrees(Cone5)[1] == 2 - @test facet_degrees(Cone6)[1] == 1 - @test ray_degrees(Cone5)[1] == 2 - @test ray_degrees(Cone6)[1] == 1 + @test facet_degrees(Cone5) == fill(2, 4) + @test facet_degrees(Cone6) == fill(1, 2) + @test ray_degrees(Cone5) == fill(2, 4) + @test ray_degrees(Cone6) == fill(1, 2) @test n_facets(Cone5) == 4 @test relative_interior_point(Cone1) == f.([1//2, 1//2]) diff --git a/test/PolyhedralGeometry/extended.jl b/test/PolyhedralGeometry/extended.jl index 2fcea73e3aca..10de0db2606e 100644 --- a/test/PolyhedralGeometry/extended.jl +++ b/test/PolyhedralGeometry/extended.jl @@ -83,29 +83,25 @@ f = sum([x; 1])^2 + x[1]^4 * x[2] * 3 newt = newton_polytope(f) @test dim(newt) == 2 - @test point_matrix(vertices(newt)) == matrix(QQ, [4 1; 2 0; 0 2; 0 0]) + @test issetequal(vertices(newt), point_vector.(Ref(QQ), [[4, 1], [2, 0], [0, 2], [0, 0]])) end @testset "Construct from QQFieldElem" begin A = zeros(Oscar.QQ, 3, 2) A[1, 1] = 1 A[3, 2] = 4 - @test point_matrix(vertices(convex_hull(A))) == matrix(QQ, [1 0; 0 0; 0 4]) + @test issetequal(vertices(convex_hull(A)), point_vector.(Ref(QQ), [[1, 0], [0, 0], [0, 4]])) - lhs, rhs = halfspace_matrix_pair(facets(polyhedron(A, [1, 2, -3]))) - @test lhs == matrix(QQ, [1 0; 0 4]) - @test rhs == [1, -3] + @test issetequal(facets(polyhedron(A, [1, 2, -3])), [affine_halfspace(QQ, [1, 0], 1), affine_halfspace(QQ, [0, 4], -3)]) end @testset "Construct from ZZRingElem" begin A = zeros(Oscar.ZZ, 3, 2) A[1, 1] = 1 A[3, 2] = 4 - @test point_matrix(vertices(convex_hull(A))) == matrix(QQ, [1 0; 0 0; 0 4]) + @test issetequal(vertices(convex_hull(A)), point_vector.(Ref(QQ), [[1, 0], [0, 0], [0, 4]])) - lhs, rhs = halfspace_matrix_pair(facets(polyhedron(A, [1, 2, -3]))) - @test lhs == matrix(QQ, [1 0; 0 4]) - @test rhs == [1, -3] + @test issetequal(facets(polyhedron(A, [1, 2, -3])), [affine_halfspace(QQ, [1, 0], 1), affine_halfspace(QQ, [0, 4], -3)]) end @testset "SubObjectIterator/Matrix compatibility" begin diff --git a/test/PolyhedralGeometry/polyhedral_complex.jl b/test/PolyhedralGeometry/polyhedral_complex.jl index 821eb959d87c..45aaa618e7ad 100644 --- a/test/PolyhedralGeometry/polyhedral_complex.jl +++ b/test/PolyhedralGeometry/polyhedral_complex.jl @@ -34,44 +34,36 @@ # test constructor with re-arranged arguments let PCF2 = polyhedral_complex(f, -P[1:3, :], I[:, 1:3], -P[4:4, :], I[:, 4:4]) - @test vertices(PCF) == vertices(PCF2) - @test rays(PCF) == rays(PCF2) - @test IncidenceMatrix(maximal_polyhedra(PCF)) == - IncidenceMatrix(maximal_polyhedra(PCF2)) + @test issetequal(vertices(PCF), vertices(PCF2)) + @test issetequal(rays(PCF), rays(PCF2)) + @test issetequal(maximal_polyhedra(PCF), maximal_polyhedra(PCF2)) end @testset "core functionality" begin @test ambient_dim(PC) == 2 @test vertices(PC) isa SubObjectIterator{PointVector{T}} @test length(vertices(PC)) == 4 - @test point_matrix(vertices(PC)) == matrix(f, P) - @test vertices(PC) == [[0, 0], [1, 0], [0, 1], [1, 1]] + @test issetequal(vertices(PC), point_vector.(Ref(f), [[0, 0], [1, 0], [0, 1], [1, 1]])) + @test point_matrix(vertices(PC)) == _oscar_matrix_from_property(f, vertices(PC)) @test rays(PCF) isa SubObjectIterator{RayVector{T}} @test length(rays(PCF)) == 1 @test rays(PCF) == [[-1, -1]] @test vector_matrix(rays(PCF)) == matrix(f, [-1 -1]) @test vertices_and_rays(PCFL) isa SubObjectIterator{Union{RayVector{T},PointVector{T}}} @test length(vertices_and_rays(PCFL)) == 4 - @test vertices_and_rays(PCFL) == [[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0]] - @test typeof.(vertices_and_rays(PCFL)) == - [PointVector{T}, PointVector{T}, PointVector{T}, RayVector{T}] - @test vector_matrix(vertices_and_rays(PCFL)) == matrix(f, P2) + @test issetequal(vertices_and_rays(PCFL), [point_vector(f, [0, 0, 0]), point_vector(f, [1, 0, 0]), point_vector(f, [0, 1, 0]), ray_vector(f, [1, 1, 0])]) + @test vector_matrix(vertices_and_rays(PCFL)) == _oscar_matrix_from_property(f, vertices_and_rays(PCFL)) @test maximal_polyhedra(PC) isa SubObjectIterator{Polyhedron{T}} @test length(maximal_polyhedra(PC)) == 2 - @test maximal_polyhedra(PC) == convex_hull.([f], [P[1:3, :], P[[2, 4], :]]) + @test issetequal(maximal_polyhedra(PC), convex_hull.([f], [P[1:3, :], P[[2, 4], :]])) @test number_of_maximal_polyhedra(PC) == 2 @test is_simplicial(PC) @test !is_pure(PCL) @test dim(PCL) == 3 @test polyhedra_of_dim(PC, 1) isa SubObjectIterator{Polyhedron{T}} @test length(polyhedra_of_dim(PC, 1)) == 4 - if T == QQFieldElem - @test polyhedra_of_dim(PC, 1) == - convex_hull.(T, [P[[2, 4], :], P[[1, 3], :], P[[1, 2], :], P[[2, 3], :]]) - else - @test polyhedra_of_dim(PC, 1) == - convex_hull.([f], [P[[2, 4], :], P[[1, 3], :], P[[2, 3], :], P[[1, 2], :]]) - end + @test issetequal(polyhedra_of_dim(PC, 1), + convex_hull.(Ref(f), [P[[2, 4], :], P[[1, 3], :], P[[2, 3], :], P[[1, 2], :]])) @test lineality_space(PCL) isa SubObjectIterator{RayVector{T}} @test length(lineality_space(PCL)) == 1 @test lineality_space(PCL) == [L[:]] @@ -125,7 +117,7 @@ @test dim(F) == dim(PC) @test ambient_dim(F) == ambient_dim(PC) @test lineality_dim(F) == lineality_dim(PC) - @test matrix(f, rays(F)) == matrix(f, rays(PC)) + @test issetequal(rays(F), rays(PC)) @test n_maximal_cones(F) == n_maximal_polyhedra(PC) vrep = PolyhedralComplex{T}( Polymake.fan.PolyhedralComplex(; diff --git a/test/PolyhedralGeometry/polyhedral_fan.jl b/test/PolyhedralGeometry/polyhedral_fan.jl index 8e1c02e3f081..e012d5615d7b 100644 --- a/test/PolyhedralGeometry/polyhedral_fan.jl +++ b/test/PolyhedralGeometry/polyhedral_fan.jl @@ -28,10 +28,10 @@ if T == QQFieldElem @test is_smooth(NFsquare) end - @test vector_matrix(rays(NFsquare)) == matrix(f, [1 0; -1 0; 0 1; 0 -1]) @test rays(NFsquare) isa SubObjectIterator{RayVector{T}} @test length(rays(NFsquare)) == 4 - @test rays(NFsquare) == [[1, 0], [-1, 0], [0, 1], [0, -1]] + @test issetequal(rays(NFsquare), ray_vector.(Ref(f), [[1, 0], [-1, 0], [0, 1], [0, -1]])) + @test vector_matrix(rays(NFsquare)) == _oscar_matrix_from_property(f, rays(NFsquare)) @test is_regular(NFsquare) @test is_complete(NFsquare) @test !is_complete(F0) @@ -45,25 +45,23 @@ @test length(RMLF2[:rays_modulo_lineality]) == 2 @test maximal_cones(F1) isa SubObjectIterator{Cone{T}} @test dim.(maximal_cones(F1)) == [2, 2] - @test ray_indices(maximal_cones(F1)) == incidence1 - @test IncidenceMatrix(maximal_cones(F1)) == incidence1 - @test maximal_cones(IncidenceMatrix, F1) == incidence1 + @test _check_im_perm_rows(ray_indices(maximal_cones(F1)), incidence1) + @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F1)), incidence1) + @test _check_im_perm_rows(maximal_cones(IncidenceMatrix, F1), incidence1) @test number_of_maximal_cones(F1) == 2 @test lineality_space(F2) isa SubObjectIterator{RayVector{T}} - @test generator_matrix(lineality_space(F2)) == matrix(f, L) - if T == QQFieldElem - @test matrix(QQ, lineality_space(F2)) == matrix(QQ, L) - end @test length(lineality_space(F2)) == 1 @test lineality_space(F2) == [L[:]] + @test generator_matrix(lineality_space(F2)) == matrix(f, L) + @test matrix(f, lineality_space(F2)) == matrix(f, L) @test cones(F2, 2) isa SubObjectIterator{Cone{T}} @test size(cones(F2, 2)) == (2,) @test lineality_space(cones(F2, 2)[1]) == [[0, 1, 0]] @test rays.(cones(F2, 2)) == [[], []] @test isnothing(cones(F2, 1)) - @test ray_indices(cones(F1, 2)) == incidence1 - @test IncidenceMatrix(cones(F1, 2)) == incidence1 - @test cones(IncidenceMatrix, F1, 2) == incidence1 + @test _check_im_perm_rows(ray_indices(cones(F1, 2)), incidence1) + @test _check_im_perm_rows(IncidenceMatrix(cones(F1, 2)), incidence1) + @test _check_im_perm_rows(cones(IncidenceMatrix, F1, 2), incidence1) II = ray_indices(maximal_cones(NFsquare)) NF0 = polyhedral_fan(II, rays(NFsquare)) @@ -75,16 +73,16 @@ end @test f_vector(NFsquare) == [4, 4] @test rays(F1NR) == collect(eachrow(I3)) - @test ray_indices(maximal_cones(F1NR)) == incidence1 - @test IncidenceMatrix(maximal_cones(F1NR)) == incidence1 + @test _check_im_perm_rows(ray_indices(maximal_cones(F1NR)), incidence1) + @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F1NR)), incidence1) @test n_rays(F2NR) == 0 @test lineality_dim(F2NR) == 1 RMLF2NR = rays_modulo_lineality(F2NR) @test length(RMLF2NR[:rays_modulo_lineality]) == 2 @test RMLF2NR[:rays_modulo_lineality] == collect(eachrow(R)) @test lineality_space(F2NR) == collect(eachrow(L)) - @test ray_indices(maximal_cones(F2NR)) == incidence2 - @test IncidenceMatrix(maximal_cones(F2NR)) == incidence2 + @test _check_im_perm_rows(ray_indices(maximal_cones(F2NR)), incidence2) + @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F2NR)), incidence2) C = positive_hull(f, identity_matrix(ZZ, 0)) pf = polyhedral_fan(C) diff --git a/test/PolyhedralGeometry/polyhedron.jl b/test/PolyhedralGeometry/polyhedron.jl index 1445276d7405..405e7a31cbfb 100644 --- a/test/PolyhedralGeometry/polyhedron.jl +++ b/test/PolyhedralGeometry/polyhedron.jl @@ -37,14 +37,6 @@ @test nrows(Oscar.pm_object(pf3).INEQUALITIES) == 4 @test n_vertices(pf3) == 1 - function _check_im_perm_rows(inc::IncidenceMatrix, o) - oinc = IncidenceMatrix(o) - nr = nrows(inc) - nr == nrows(oinc) && - issetequal(Polymake.row.(Ref(inc), 1:nr), - Polymake.row.(Ref(oinc), 1:nr)) - end - @testset "core functionality" begin @test matrix(f, rays(Q1)) * v == T[f(2)] @test issetequal(matrix(f, vertices(Q1)) * v, T[f(1), f(0), f(1)]) @@ -55,20 +47,20 @@ @test n_vertices(Q0) == 3 @test n_vertices.(faces(Q0, 1)) == [2, 2, 2] @test lattice_points(Q0) isa SubObjectIterator{PointVector{ZZRingElem}} - @test point_matrix(lattice_points(Q0)) isa MatElem{ZZRingElem} - @test matrix(ZZ, lattice_points(Q0)) isa MatElem{ZZRingElem} @test length(lattice_points(Q0)) == 3 @test issetequal(lattice_points(Q0), point_vector.(Ref(ZZ), [[0, 0], [0, 1], [1, 0]])) + @test point_matrix(lattice_points(Q0)) == _oscar_matrix_from_property(ZZ, lattice_points(Q0)) + @test matrix(ZZ, lattice_points(Q0)) == _oscar_matrix_from_property(ZZ, lattice_points(Q0)) @test interior_lattice_points(square) isa SubObjectIterator{PointVector{ZZRingElem}} @test point_matrix(interior_lattice_points(square)) == matrix(ZZ, [0 0]) @test matrix(ZZ, interior_lattice_points(square)) == matrix(ZZ, [0 0]) @test length(interior_lattice_points(square)) == 1 @test interior_lattice_points(square) == [[0, 0]] @test boundary_lattice_points(square) isa SubObjectIterator{PointVector{ZZRingElem}} - @test point_matrix(boundary_lattice_points(square)) isa MatElem{ZZRingElem} @test length(boundary_lattice_points(square)) == 8 @test issetequal(boundary_lattice_points(square), point_vector.(Ref(ZZ), [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]])) + @test point_matrix(boundary_lattice_points(square)) == _oscar_matrix_from_property(ZZ, boundary_lattice_points(square)) if T == QQFieldElem @test is_smooth(Q0) @test is_normal(Q0) @@ -111,8 +103,8 @@ @test rays(RayVector, Pos) isa SubObjectIterator{RayVector{T}} @test rays(Pos) isa SubObjectIterator{RayVector{T}} @test length(rays(Pos)) == 3 - @test vector_matrix(rays(Pos)) isa MatElem{T} @test issetequal(rays(Pos), ray_vector.(Ref(f), [[1, 0, 0], [0, 1, 0], [0, 0, 1]])) + @test vector_matrix(rays(Pos)) == _oscar_matrix_from_property(f, rays(Pos)) @test lineality_space(L) isa SubObjectIterator{RayVector{T}} @test generator_matrix(lineality_space(L)) == matrix(f, [0 0 1]) if T == QQFieldElem @@ -151,7 +143,7 @@ v = vertices(minkowski_sum(Q0, square)) @test length(v) == 5 @test issetequal(v, point_vector.(Ref(f), [[2, -1], [2, 1], [-1, -1], [-1, 2], [1, 2]])) - @test point_matrix(v) isa MatElem{T} + @test point_matrix(v) == _oscar_matrix_from_property(f, v) let S = Pair{Matrix{T},T} @test issetequal(facets(S, Pos), S.([f.([-1 0 0]), f.([0 -1 0]), f.([0 0 -1])], [f(0)])) end @@ -190,13 +182,11 @@ @test facets(Pos) isa SubObjectIterator{AffineHalfspace{T}} @test facets(Halfspace, Pos) isa SubObjectIterator{AffineHalfspace{T}} @test affine_hull(point) isa SubObjectIterator{AffineHyperplane{T}} - @test affine_equation_matrix(affine_hull(point)) isa MatElem{T} - @test size(affine_equation_matrix(affine_hull(point))) == (3, 4) - @test Oscar.affine_matrix_for_polymake(affine_hull(point)) isa Polymake.Matrix{Oscar._scalar_type_to_polymake(T)} - @test size(Oscar.affine_matrix_for_polymake(affine_hull(point))) == (3, 4) @test length(affine_hull(point)) == 3 @test issetequal(affine_hull(point), [hyperplane(f, [1 0 0], 0), hyperplane(f, [0 1 0], 1), hyperplane(f, [0 0 1], 0)]) + @test affine_equation_matrix(affine_hull(point)) == _oscar_matrix_from_property(f, affine_hull(point)) + @test Oscar.affine_matrix_for_polymake(affine_hull(point)) == _polymake_matrix_from_property(affine_hull(point)) @test n_facets(square) == 4 @test lineality_dim(Q0) == 0 @test n_rays(Q1) == 1 diff --git a/test/PolyhedralGeometry/scalar_types.jl b/test/PolyhedralGeometry/scalar_types.jl index 8e67d92d98e6..802b1794a306 100644 --- a/test/PolyhedralGeometry/scalar_types.jl +++ b/test/PolyhedralGeometry/scalar_types.jl @@ -49,7 +49,7 @@ let pc = polyhedral_complex( E, IncidenceMatrix(facets(sd)), vertices(sd); non_redundant=true ) - @test maximal_polyhedra(pc) == faces(sd, 2) + @test issetequal(maximal_polyhedra(pc), faces(sd, 2)) end let c = convex_hull(E, permutedims([0]), permutedims([r])) ms = product(sd, c) diff --git a/test/PolyhedralGeometry/setup_tests.jl b/test/PolyhedralGeometry/setup_tests.jl index d7087e22adaa..6d2ba9757c99 100644 --- a/test/PolyhedralGeometry/setup_tests.jl +++ b/test/PolyhedralGeometry/setup_tests.jl @@ -7,3 +7,27 @@ if !isdefined(Main, :_prepare_scalar_types) return [(f, elem_type(f)) for f in (QQ, K, KK)] end end + +function _check_im_perm_rows(inc::IncidenceMatrix, o) + oinc = IncidenceMatrix(o) + nr, nc = size(inc) + (nr, nc) == size(oinc) && + issetequal(Polymake.row.(Ref(inc), 1:nr), + Polymake.row.(Ref(oinc), 1:nr)) +end + +_matrix_from_property(b::SubObjectIterator{<:Union{LinearHalfspace, LinearHyperplane}}) = permutedims(hcat([normal_vector(be) for be in b]...)) + +_matrix_from_property(b::SubObjectIterator{<:Union{AffineHalfspace, AffineHyperplane}}) = permutedims(hcat([vcat(-negbias(be), normal_vector(be)) for be in b]...)) + +# only used for cones that are linear halfspaces +_matrix_from_property(b::SubObjectIterator{Cone{T}}) where T = _matrix_from_property(SubObjectIterator{LinearHalfspace{T}}(b.Obj, b.Acc, b.n)) + +_matrix_from_property(b::SubObjectIterator) = permutedims(hcat(b...)) + +_oscar_matrix_from_property(a, b::SubObjectIterator) = matrix(a, _matrix_from_property(b)) + +function _polymake_matrix_from_property(b::SubObjectIterator) + m = _matrix_from_property(b) + return Polymake.Matrix{Oscar._scalar_type_to_polymake(eltype(m))}(m) +end diff --git a/test/PolyhedralGeometry/subdivision_of_points.jl b/test/PolyhedralGeometry/subdivision_of_points.jl index 6d4c241386ef..2608f0e46f6f 100644 --- a/test/PolyhedralGeometry/subdivision_of_points.jl +++ b/test/PolyhedralGeometry/subdivision_of_points.jl @@ -14,9 +14,9 @@ SubdivisionOfPoints @testset "alternative inputs" begin - @test issetequal(collect(maximal_cells(square_by_incidence)), - collect(maximal_cells(square_by_weights))) - @test min_weights(square_by_cells) == min_weights(square_by_weights) + @test issetequal(maximal_cells(square_by_incidence), + maximal_cells(square_by_weights)) + @test issetequal(square_max_cells, maximal_cells(subdivision_of_points(C,min_weights(square_by_cells)))) end moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2] @@ -45,10 +45,10 @@ @test min_weights(SOP1) == [0, 0, 0, 1, 1, 1] @test dim(C1) == 6 @test dim(CMOAE) == 4 - @test moaeimnonreg0 == maximal_cells(IncidenceMatrix, MOAE) + @test _check_im_perm_rows(moaeimnonreg0, maximal_cells(IncidenceMatrix, MOAE)) @test number_of_points(MOAE) == 6 @test length(points(MOAE)) == 6 - @test collect(points(MOAE))[3] == [0, 0, 4] + @test [0, 0, 4] in points(MOAE) @test gkz_vector(fulldim_MOAE) == [9, 9, 9, 7, 7, 7] end end diff --git a/test/PolyhedralGeometry/types.jl b/test/PolyhedralGeometry/types.jl index af613877f754..f54e31a349b7 100644 --- a/test/PolyhedralGeometry/types.jl +++ b/test/PolyhedralGeometry/types.jl @@ -14,7 +14,7 @@ (ENF, _) = _prepare_scalar_types()[2] - @testset "$T" for (T, fun) in ((PointVector, point_vector), (RayVector, ray_vector)) + @testset "$T" for (T, fun, other) in ((PointVector, point_vector, ray_vector), (RayVector, ray_vector, point_vector)) @test fun(a) isa T{QQFieldElem} for f in (ZZ, QQ, ENF) @@ -57,6 +57,10 @@ @test *(g(3), A) isa T @test *(g(3), A) == 3 * a + + let Ao = other(g, a) + @test_throws ArgumentError A == Ao + end end for op in [+, -] @@ -69,6 +73,19 @@ @test 3 * A isa T{U} @test 3 * A == 3 * a + if T == RayVector + @test 5 * A == A + @test fun(f, 5 * a) == A + @test A == fun(f, 5 * a) + @test 5 * a == A + @test vcat(a, a) != A + @test -5 * A != A + @test fun(f, -5 * a) != A + @test A != fun(f, -5 * a) + @test A == 5 * a + @test A != vcat(a, a) + end + if f != ENF let h = Int Ah = h.(A) From ab11326ef768b04ffc083e4d767d20a59effc8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 24 Oct 2024 11:29:21 +0200 Subject: [PATCH 11/84] Add dedicated printing for empty combinatorial objects (#4236) --- src/Combinatorics/EnumerativeCombinatorics/compositions.jl | 7 ++++++- src/Combinatorics/EnumerativeCombinatorics/partitions.jl | 7 ++++++- .../EnumerativeCombinatorics/weak_compositions.jl | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Combinatorics/EnumerativeCombinatorics/compositions.jl b/src/Combinatorics/EnumerativeCombinatorics/compositions.jl index acfb64371452..5cec5e2ac336 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/compositions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/compositions.jl @@ -40,7 +40,12 @@ function composition(parts::Vector{T}; check::Bool = true) where {T <: IntegerUn end function Base.show(io::IO, ::MIME"text/plain", C::Composition) - print(io, data(C)) + c = data(C) + if isempty(c) + print(io, "Empty composition") + return + end + print(io, c) end ################################################################################ diff --git a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl index a833e0f47d23..167a46a32e99 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl @@ -72,7 +72,12 @@ end data(P::Partition) = P.p function Base.show(io::IO, ::MIME"text/plain", P::Partition) - print(io, data(P)) + p = data(P) + if isempty(p) + print(io, "Empty partition") + return + end + print(io, p) end ################################################################################ diff --git a/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl b/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl index 04b00ee7f72c..dadcd07e977c 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl @@ -32,7 +32,12 @@ function weak_composition(parts::Vector{T}; check::Bool = true) where {T <: Inte end function Base.show(io::IO, ::MIME"text/plain", C::WeakComposition) - print(io, data(C)) + c = data(C) + if isempty(c) + print(io, "Empty weak composition") + return + end + print(io, c) end ################################################################################ From 5c30f3abbd2d61759c500dde62f4c0fe50c54ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 24 Oct 2024 11:29:53 +0200 Subject: [PATCH 12/84] Improve performance of `bracket` for `AbstractLieAlgebraElem` (#4228) --- .../LieAlgebras/src/AbstractLieAlgebra.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index 9fa34cbfe69a..1052aa7c43ff 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -68,12 +68,15 @@ function bracket( ) where {C<:FieldElem} check_parent(x, y) L = parent(x) - mat = sum( - cxi * cyj * _struct_consts(L)[i, j] for (i, cxi) in enumerate(coefficients(x)), - (j, cyj) in enumerate(coefficients(y)); - init=sparse_row(coefficient_ring(L)), - ) - return L(mat) + vec = sparse_row(coefficient_ring(L)) + for (i, cxi) in enumerate(coefficients(x)) + iszero(cxi) && continue + for (j, cyj) in enumerate(coefficients(y)) + iszero(cyj) && continue + Hecke.add_scaled_row!(_struct_consts(L)[i, j], vec, cxi * cyj) + end + end + return L(vec) end ############################################################################### From eaa8972f657ccc3ae4e3c55495ebe3227a2dd429 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Thu, 24 Oct 2024 22:23:25 +0200 Subject: [PATCH 13/84] Add `characteristic` for multivariate quotient rings over fields (#4241) --- src/Rings/MPolyQuo.jl | 8 ++++++++ test/Rings/MPolyQuo.jl | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/Rings/MPolyQuo.jl b/src/Rings/MPolyQuo.jl index a07fae42f54b..fe521d51dba1 100644 --- a/src/Rings/MPolyQuo.jl +++ b/src/Rings/MPolyQuo.jl @@ -79,6 +79,14 @@ oscar_origin_ring(Q::MPolyQuoRing) = base_ring(Q) default_ordering(Q::MPolyQuoRing) = default_ordering(base_ring(Q)) +# Only for fields for now because of things like char(ZZ[x, y]/<2>) = 2 +function characteristic(Q::MPolyQuoRing{<:MPolyRingElem{T}}) where {T <: FieldElement} + if is_zero(one(Q)) + return 1 + end + return characteristic(coefficient_ring(Q)) +end + ############################################################################## # # Quotient ring elements diff --git a/test/Rings/MPolyQuo.jl b/test/Rings/MPolyQuo.jl index 2d38f5ecfd29..31c18c00ecd3 100644 --- a/test/Rings/MPolyQuo.jl +++ b/test/Rings/MPolyQuo.jl @@ -36,6 +36,12 @@ B,q = quo(R, x^3+y,y^2; ordering=lex(R)) @test A.I == B.I + + @test characteristic(quo(R, ideal(x))[1]) == 0 + @test characteristic(quo(R, ideal(R, 1))[1]) == 1 + + S, (s, t) = GF(5)[:s, :t] + @test characteristic(quo(S, ideal([s, t]))[1]) == 5 end @testset "MpolyQuo.manipulation" begin From 3923b8bbec8005c5406766885855b4fe302825d0 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Thu, 24 Oct 2024 12:50:23 +0200 Subject: [PATCH 14/84] [ToricVarieties] Assert coordinate names from cox ring --- .../FTheoryTools/src/AbstractFTheoryModels/attributes.jl | 2 +- .../ToricVarieties/NormalToricVarieties/attributes.jl | 5 ++++- test/Serialization/ToricGeometry.jl | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/experimental/FTheoryTools/src/AbstractFTheoryModels/attributes.jl b/experimental/FTheoryTools/src/AbstractFTheoryModels/attributes.jl index 85896d9f251a..494b360cf663 100644 --- a/experimental/FTheoryTools/src/AbstractFTheoryModels/attributes.jl +++ b/experimental/FTheoryTools/src/AbstractFTheoryModels/attributes.jl @@ -1052,7 +1052,7 @@ julia> qsm_model = literature_model(arxiv_id = "1903.00009", model_parameters = Hypersurface model over a concrete base julia> zero_section_class(qsm_model) -Cohomology class on a normal toric variety given by x32 + 2*x33 + 3*x34 + x35 - x36 +Cohomology class on a normal toric variety given by e2 + 2*u + 3*e4 + e1 - w ``` """ function zero_section_class(m::AbstractFTheoryModel) diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl index 8af312aa7b72..aa33e0997f56 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl @@ -210,7 +210,10 @@ julia> coordinate_names(antv) ``` """ @attr Vector{String} function coordinate_names(v::NormalToricVarietyType) - return ["x$(i)" for i in 1:torsion_free_rank(torusinvariant_weil_divisor_group(v))] + if has_attribute(v, :cox_ring) + return string.(gens(cox_ring(v))) + end + return ["x$(i)" for i in 1:torsion_free_rank(torusinvariant_weil_divisor_group(v))] end diff --git a/test/Serialization/ToricGeometry.jl b/test/Serialization/ToricGeometry.jl index 05c965bb572e..9701264e9abe 100644 --- a/test/Serialization/ToricGeometry.jl +++ b/test/Serialization/ToricGeometry.jl @@ -8,16 +8,19 @@ test_save_load_roundtrip(path, pp; with_attrs=false, check_func=!check) do loaded @test rays(pp) == rays(loaded) @test ray_indices(maximal_cones(pp)) == ray_indices(maximal_cones(loaded)) + @test coordinate_names(pp) == coordinate_names(loaded) end test_save_load_roundtrip(path, pp; with_attrs=true, check_func=check) do loaded @test rays(pp) == rays(loaded) @test ray_indices(maximal_cones(pp)) == ray_indices(maximal_cones(loaded)) + @test coordinate_names(pp) == coordinate_names(loaded) end test_save_load_roundtrip(path, pp; check_func=check) do loaded @test rays(pp) == rays(loaded) @test ray_indices(maximal_cones(pp)) == ray_indices(maximal_cones(loaded)) + @test coordinate_names(pp) == coordinate_names(loaded) end end From 7d82325386788b9ee3b30b6f99bcf139ab332b96 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Thu, 24 Oct 2024 14:21:39 +0200 Subject: [PATCH 15/84] [ToricVarieties] Avoid unnessary allocations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Göttgens --- .../ToricVarieties/NormalToricVarieties/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl index aa33e0997f56..e54115514227 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/attributes.jl @@ -211,7 +211,7 @@ julia> coordinate_names(antv) """ @attr Vector{String} function coordinate_names(v::NormalToricVarietyType) if has_attribute(v, :cox_ring) - return string.(gens(cox_ring(v))) + return string.(symbols(cox_ring(v))) end return ["x$(i)" for i in 1:torsion_free_rank(torusinvariant_weil_divisor_group(v))] end From 16517cf64700607f87a642867486c51d1f255f96 Mon Sep 17 00:00:00 2001 From: ederc Date: Sat, 26 Oct 2024 09:51:42 +0200 Subject: [PATCH 16/84] =?UTF-8?q?Adds=20algorithm=20option=20:modular=20fo?= =?UTF-8?q?r=20Gr=C3=B6bner=20basis=20computations=20(#4246)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rings/groebner.jl | 14 +++++++++++++- test/Rings/groebner.jl | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Rings/groebner.jl b/src/Rings/groebner.jl index 1a0feb6626d6..a2ba4d40f879 100644 --- a/src/Rings/groebner.jl +++ b/src/Rings/groebner.jl @@ -123,6 +123,7 @@ Return a standard basis of `I` with respect to `ordering`. The keyword `algorithm` can be set to - `:buchberger` (implementation of Buchberger's algorithm in *Singular*), +- `:modular` (implementation of multi-modular approach, if applicable), - `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package), - `:fglm` (implementation of the FGLM algorithm in *Singular*), - `:hc` (implementation of Buchberger's algorithm in *Singular* trying to first compute the highest corner modulo some prime), and @@ -163,6 +164,16 @@ function standard_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_orde elseif complete_reduction == true I.gb[ordering] = _compute_standard_basis(I.gb[ordering], ordering, complete_reduction) end + elseif algorithm == :modular + if is_f4_applicable(I, ordering) + # since msolve v0.7.0 is most of the time more efficient + # to compute a reduced GB by default + groebner_basis_f4(I, complete_reduction=true) + elseif base_ring(I) isa QQMPolyRing + groebner_basis_modular(I, ordering=ordering) + else + error("Modular option not applicable in this setting.") + end elseif algorithm == :fglm _compute_groebner_basis_using_fglm(I, ordering) elseif algorithm == :hc @@ -217,6 +228,7 @@ If `ordering` is global, return a Gröbner basis of `I` with respect to `orderin The keyword `algorithm` can be set to - `:buchberger` (implementation of Buchberger's algorithm in *Singular*), +- `:modular` (implementation of multi-modular approach, if applicable), - `:hilbert` (implementation of a Hilbert driven Gröbner basis computation in *Singular*), - `:fglm` (implementation of the FGLM algorithm in *Singular*), and - `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package). @@ -331,7 +343,7 @@ function is_f4_applicable(I::MPolyIdeal, ordering::MonomialOrdering) && ((coefficient_ring(I) isa FqField && absolute_degree(coefficient_ring(I)) == 1 && characteristic(coefficient_ring(I)) < 2^31) - || coefficient_ring(I) == QQ)) + || base_ring(I) isa QQMPolyRing)) end @doc raw""" diff --git a/test/Rings/groebner.jl b/test/Rings/groebner.jl index fee6c2dc9f05..53d7b4d96c18 100644 --- a/test/Rings/groebner.jl +++ b/test/Rings/groebner.jl @@ -11,6 +11,22 @@ @test leading_ideal(I, ordering=degrevlex(gens(R))) == ideal(R,[x*y^2, x^4, y^5]) @test leading_ideal(I) == ideal(R,[x*y^2, x^4, y^5]) @test leading_ideal(I, ordering=lex(gens(R))) == ideal(R,[y^7, x*y^2, x^3]) + + # algorithm = :modular option + R = @polynomial_ring(QQ, [:x, :y]) + I = ideal(R, [x^2+y,y*x-1]) + # uses f4 in msolve/AlgebraicSolving + groebner_basis(I, ordering=degrevlex(R), algorithm=:modular) + @test gens(I.gb[degrevlex(R)]) == QQMPolyRingElem[x + y^2, x*y - 1, x^2 + y] + # uses multi-modular implementation in Oscar applying Singular finite field + # computations + groebner_basis(I, ordering=lex(R), algorithm=:modular) + @test gens(I.gb[lex(R)]) == QQMPolyRingElem[y^3 + 1, x + y^2] + T = @polynomial_ring(QQ, :t) + R = @polynomial_ring(T, [:x, :y]) + I = ideal(R, [x^2+y,y*x-1]) + @test_throws ErrorException groebner_basis(I, ordering=lex(R), algorithm=:modular) + # issue 3665 kt,t = polynomial_ring(GF(2),:t) Ft = fraction_field(kt) From 020e83a2e33e4754a65217363d998ee05fc42e2d Mon Sep 17 00:00:00 2001 From: alexej-jordan <58329349+alexej-jordan@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:51:20 +0100 Subject: [PATCH 17/84] Introduce snake case function `incidence_matrix` (#4245) * introduced incidence_matrix and added docs * replaced IncidenceMatrix with incidence_matrix in docstrings * replaced IncidenceMatrix with incidence_matrix in tests * added method incidence_matrix(::SubObjectIterator) * adjusted IncidenceMatrix to incidence_matrix in test/book * added tests for new methods of incidence_matrix * applied suggestions for incidence_matrix from github * formatted PolyhedralGeometry/helpers.jl * keep IncidenceMatrix call in booktests --------- Co-authored-by: Benjamin Lorenz --- docs/src/PolyhedralGeometry/intro.md | 8 +- .../Schemes/src/ToricBlowups/constructors.jl | 2 +- .../AlgebraicCycles/special_attributes.jl | 2 +- .../CohomologyClasses/methods.jl | 2 +- .../NormalToricVarieties/constructors.jl | 2 +- .../PolyhedralComplex/constructors.jl | 4 +- .../PolyhedralComplex/properties.jl | 44 +++---- .../standard_constructions.jl | 4 +- .../PolyhedralFan/constructors.jl | 4 +- .../PolyhedralFan/properties.jl | 18 +-- .../SubdivisionOfPoints/constructors.jl | 2 +- .../SubdivisionOfPoints/functions.jl | 2 +- .../SubdivisionOfPoints/properties.jl | 6 +- src/PolyhedralGeometry/helpers.jl | 111 +++++++++++++++++- src/PolyhedralGeometry/iterators.jl | 2 + src/PolyhedralGeometry/triangulations.jl | 2 +- src/TropicalGeometry/curve.jl | 16 +-- src/TropicalGeometry/variety.jl | 2 +- .../ToricVarieties/affine_normal_varieties.jl | 2 +- .../ToricVarieties/direct_products.jl | 2 +- .../ToricVarieties/intersection_numbers.jl | 4 +- .../line_bundle_cohomologies.jl | 2 +- .../ToricVarieties/subvarieties.jl | 2 +- .../ToricVarieties/toric_divisor_classes.jl | 2 +- .../ToricVarieties/toric_schemes.jl | 2 +- test/Combinatorics/Graph.jl | 2 +- test/Combinatorics/SimplicialComplexes.jl | 2 +- test/PolyhedralGeometry/cone.jl | 8 +- test/PolyhedralGeometry/extended.jl | 4 +- test/PolyhedralGeometry/group.jl | 2 +- test/PolyhedralGeometry/lineality.jl | 2 +- test/PolyhedralGeometry/polyhedral_complex.jl | 6 +- test/PolyhedralGeometry/polyhedral_fan.jl | 16 +-- test/PolyhedralGeometry/polyhedron.jl | 26 ++-- test/PolyhedralGeometry/scalar_types.jl | 2 +- test/PolyhedralGeometry/setup_tests.jl | 2 +- .../subdivision_of_points.jl | 6 +- test/PolyhedralGeometry/types.jl | 11 +- test/Serialization/PolyhedralGeometry.jl | 4 +- test/Serialization/TropicalGeometry.jl | 2 +- 40 files changed, 232 insertions(+), 112 deletions(-) diff --git a/docs/src/PolyhedralGeometry/intro.md b/docs/src/PolyhedralGeometry/intro.md index f11eeb31b51e..e9e383063254 100644 --- a/docs/src/PolyhedralGeometry/intro.md +++ b/docs/src/PolyhedralGeometry/intro.md @@ -159,7 +159,13 @@ Some methods will require input or return output in form of an `IncidenceMatrix` IncidenceMatrix ``` -From the example it can be seen that this type supports `julia`'s matrix functionality. There are also functions to retrieve specific rows or columns as a `Set` over the non-zero indices. +The unique nature of the `IncidenceMatrix` allows for different ways of construction: + +```@docs +incidence_matrix +``` + +From the examples it can be seen that this type supports `julia`'s matrix functionality. There are also functions to retrieve specific rows or columns as a `Set` over the non-zero indices. ```@docs row(i::IncidenceMatrix, n::Int) diff --git a/experimental/Schemes/src/ToricBlowups/constructors.jl b/experimental/Schemes/src/ToricBlowups/constructors.jl index c9a192240382..0263c5d0028c 100644 --- a/experimental/Schemes/src/ToricBlowups/constructors.jl +++ b/experimental/Schemes/src/ToricBlowups/constructors.jl @@ -137,7 +137,7 @@ julia> rs = [1 1; -1 1] 1 1 -1 1 -julia> max_cones = IncidenceMatrix([[1, 2]]) +julia> max_cones = incidence_matrix([[1, 2]]) 1×2 IncidenceMatrix [1, 2] diff --git a/src/AlgebraicGeometry/ToricVarieties/AlgebraicCycles/special_attributes.jl b/src/AlgebraicGeometry/ToricVarieties/AlgebraicCycles/special_attributes.jl index 3f03add700af..c61df9438095 100644 --- a/src/AlgebraicGeometry/ToricVarieties/AlgebraicCycles/special_attributes.jl +++ b/src/AlgebraicGeometry/ToricVarieties/AlgebraicCycles/special_attributes.jl @@ -27,7 +27,7 @@ true julia> ngens(chow_ring(p2)) 3 -julia> v = normal_toric_variety(IncidenceMatrix([[1], [2], [3]]), [[1, 0], [0, 1], [-1, -1]]) +julia> v = normal_toric_variety(incidence_matrix([[1], [2], [3]]), [[1, 0], [0, 1], [-1, -1]]) Normal toric variety julia> is_complete(v) diff --git a/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/methods.jl b/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/methods.jl index 23eca4350f2f..21478f09306b 100644 --- a/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/methods.jl +++ b/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/methods.jl @@ -63,7 +63,7 @@ julia> m = 2; julia> ray_generators = [e1, -e1, e2, e3, - e2 - e3 - m * e1]; -julia> max_cones = IncidenceMatrix([[1,3,4], [1,3,5], [1,4,5], [2,3,4], [2,3,5], [2,4,5]]); +julia> max_cones = incidence_matrix([[1,3,4], [1,3,5], [1,4,5], [2,3,4], [2,3,5], [2,4,5]]); julia> X = normal_toric_variety(max_cones, ray_generators; non_redundant = true) Normal toric variety diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl index ecce8f513743..507d2c082daf 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl @@ -82,7 +82,7 @@ julia> ray_generators = [[1,0], [0, 1], [-1, 5], [0, -1]] [-1, 5] [0, -1] -julia> max_cones = IncidenceMatrix([[1, 2], [2, 3], [3, 4], [4, 1]]) +julia> max_cones = incidence_matrix([[1, 2], [2, 3], [3, 4], [4, 1]]) 4×4 IncidenceMatrix [1, 2] [2, 3] diff --git a/src/PolyhedralGeometry/PolyhedralComplex/constructors.jl b/src/PolyhedralGeometry/PolyhedralComplex/constructors.jl index 6b5217e01143..db9e9c56125d 100644 --- a/src/PolyhedralGeometry/PolyhedralComplex/constructors.jl +++ b/src/PolyhedralGeometry/PolyhedralComplex/constructors.jl @@ -40,7 +40,7 @@ points and the rows represent the polyhedra. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]) +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]) 2×4 IncidenceMatrix [1, 2, 3] [1, 3, 4] @@ -61,7 +61,7 @@ Polyhedral complex with rays and lineality: ```jldoctest julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> far_vertices = [2,3,4]; diff --git a/src/PolyhedralGeometry/PolyhedralComplex/properties.jl b/src/PolyhedralGeometry/PolyhedralComplex/properties.jl index f769ef684cdb..670ae81cc9e6 100644 --- a/src/PolyhedralGeometry/PolyhedralComplex/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralComplex/properties.jl @@ -5,7 +5,7 @@ Return the ambient dimension of `PC`. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]) +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]) 2×4 IncidenceMatrix [1, 2, 3] [1, 3, 4] @@ -41,7 +41,7 @@ Optional arguments for `as` include # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> V = [0 0; 1 0; 1 1; 0 1]; @@ -63,7 +63,7 @@ julia> matrix(QQ, vertices(PointVector, PC)) ``` The following complex has no vertices: ``` -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; @@ -139,7 +139,7 @@ function is mainly a helper function for [`maximal_polyhedra`](@ref maximal_poly # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; @@ -193,7 +193,7 @@ See also [`rays`](@ref rays(PC::PolyhedralComplex{T}) where {T<:scalar_types}) a ```jldoctest julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -259,7 +259,7 @@ See also [`vertices`](@ref vertices(as::Type{PointVector{T}}, PC::PolyhedralComp ```jldoctest julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -317,7 +317,7 @@ Optional arguments for `as` include # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0; 1 0; 1 1; 0 1]; @@ -333,7 +333,7 @@ julia> matrix(QQ, rays(RayVector, PC)) ``` The following complex has no vertices: ``` -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; @@ -398,7 +398,7 @@ refer to the output of [`vertices_and_rays`](@ref vertices_and_rays(PC::Polyhedr # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]) +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]) 2×4 IncidenceMatrix [1, 2, 3] [1, 3, 4] @@ -435,7 +435,7 @@ Return the number of maximal polyhedra of `PC` # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]) +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]) 2×4 IncidenceMatrix [1, 2, 3] [1, 3, 4] @@ -464,7 +464,7 @@ Determine whether the polyhedral complex is simplicial. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0; 1 0; 1 1; 0 1]; @@ -484,7 +484,7 @@ Determine whether the polyhedral complex is pure. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0; 1 0; 1 1; 0 1]; @@ -504,7 +504,7 @@ Compute the dimension of the polyhedral complex. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0; 1 0; 1 1; 0 1]; @@ -524,7 +524,7 @@ Return the polyhedra of a given dimension in the polyhedral complex `PC`. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> VR = [0 0; 1 0; 1 1; 0 1]; @@ -596,7 +596,7 @@ Return the lineality space of `PC`. ```jldoctest julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -636,7 +636,7 @@ Return the lineality dimension of `PC`. ```jldoctest julia> VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0]; -julia> IM = IncidenceMatrix([[1,2,3],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,3],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -661,7 +661,7 @@ faces of $PC$ of dimension $i$. ```jldoctest julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2,4],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,4],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -689,7 +689,7 @@ Return the number of rays of `PC`. ```jldoctest julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2,4],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,4],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -711,7 +711,7 @@ Return the number of vertices of `PC`. ```jldoctest julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2,4],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,4],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -733,7 +733,7 @@ Return the total number of polyhedra in the polyhedral complex `PC`. ```jldoctest julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2,4],[1,3,4]]); +julia> IM = incidence_matrix([[1,2,4],[1,3,4]]); julia> far_vertices = [2,3,4]; @@ -754,7 +754,7 @@ Compute the codimension of a polyhedral complex. ``` julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4]]); +julia> IM = incidence_matrix([[1,2],[1,3],[1,4]]); julia> far_vertices = [2,3,4]; @@ -777,7 +777,7 @@ subset of some $\mathbb{R}^n$. ```jldoctest julia> VR = [0 0; 1 0; -1 0; 0 1]; -julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4]]); +julia> IM = incidence_matrix([[1,2],[1,3],[1,4]]); julia> PC = polyhedral_complex(IM, VR) Polyhedral complex in ambient dimension 2 diff --git a/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl b/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl index 1eb7cf43e588..711c3a6d87c4 100644 --- a/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl +++ b/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl @@ -5,7 +5,7 @@ Return the common refinement of two polyhedral complexes. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3]]) +julia> IM = incidence_matrix([[1,2,3]]) 1×3 IncidenceMatrix [1, 2, 3] @@ -51,7 +51,7 @@ Return the k-skeleton of a polyhedral complex. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3]]) +julia> IM = incidence_matrix([[1,2,3]]) 1×3 IncidenceMatrix [1, 2, 3] diff --git a/src/PolyhedralGeometry/PolyhedralFan/constructors.jl b/src/PolyhedralGeometry/PolyhedralFan/constructors.jl index f54219634937..9005ace2a38d 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/constructors.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/constructors.jl @@ -43,7 +43,7 @@ To obtain the upper half-space of the plane: ```jldoctest julia> R = [1 0; 1 1; 0 1; -1 0; 0 -1]; -julia> IM=IncidenceMatrix([[1,2],[2,3],[3,4],[4,5],[1,5]]); +julia> IM = incidence_matrix([[1,2],[2,3],[3,4],[4,5],[1,5]]); julia> PF=polyhedral_fan(IM, R) Polyhedral fan in ambient dimension 2 @@ -55,7 +55,7 @@ julia> R = [1 0 0; 0 0 1]; julia> L = [0 1 0]; -julia> IM = IncidenceMatrix([[1],[2]]); +julia> IM = incidence_matrix([[1],[2]]); julia> PF=polyhedral_fan(IM, R, L) Polyhedral fan in ambient dimension 3 diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index 13d9a3fbeec3..2b157f65c563 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -50,7 +50,7 @@ julia> matrix(QQ, rays(NF)) ``` The following fan has no rays: ``` -julia> IM = IncidenceMatrix([[1,2],[2,3]]); +julia> IM = incidence_matrix([[1,2],[2,3]]); julia> R = [1 0 0; 0 1 0; -1 0 0]; @@ -287,7 +287,7 @@ Return the dimension of `PF`. This fan in the plane contains a 2-dimensional cone and is thus 2-dimensional itself. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); julia> dim(PF) 2 @@ -304,7 +304,7 @@ Return the number of maximal cones of `PF`. The cones given in this construction are non-redundant. Thus there are two maximal cones. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); julia> n_maximal_cones(PF) 2 @@ -321,7 +321,7 @@ Return the number of cones of `PF`. The cones given in this construction are non-redundant. There are six cones in this fan. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]) +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]) Polyhedral fan in ambient dimension 2 julia> n_cones(PF) @@ -441,7 +441,7 @@ This fan consists of two cones, one containing all the points with $y ≤ 0$ and one containing all the points with $y ≥ 0$. The fan's lineality is the common lineality of these two cones, i.e. in $x$-direction. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2, 3], [3, 4, 1]]), [1 0; 0 1; -1 0; 0 -1]) +julia> PF = polyhedral_fan(incidence_matrix([[1, 2, 3], [3, 4, 1]]), [1 0; 0 1; -1 0; 0 -1]) Polyhedral fan in ambient dimension 2 julia> lineality_space(PF) @@ -500,7 +500,7 @@ Determine whether `PF` is smooth. Even though the cones of this fan cover the positive orthant together, one of these und thus the whole fan is not smooth. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [2, 3]]), [0 1; 2 1; 1 0]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [2, 3]]), [0 1; 2 1; 1 0]); julia> is_smooth(PF) false @@ -516,7 +516,7 @@ Determine whether `PF` is regular, i.e. the normal fan of a polytope. # Examples This fan is not complete and thus not regular. ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); julia> is_regular(PF) false @@ -531,7 +531,7 @@ Determine whether `PF` is pure, i.e. all maximal cones have the same dimension. # Examples ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); julia> is_pure(PF) false @@ -547,7 +547,7 @@ dimension. # Examples ```jldoctest -julia> PF = polyhedral_fan(IncidenceMatrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); +julia> PF = polyhedral_fan(incidence_matrix([[1, 2], [3]]), [1 0; 0 1; -1 -1]); julia> is_fulldimensional(PF) true diff --git a/src/PolyhedralGeometry/SubdivisionOfPoints/constructors.jl b/src/PolyhedralGeometry/SubdivisionOfPoints/constructors.jl index 7411b26fee52..5373f5dd966c 100644 --- a/src/PolyhedralGeometry/SubdivisionOfPoints/constructors.jl +++ b/src/PolyhedralGeometry/SubdivisionOfPoints/constructors.jl @@ -52,7 +52,7 @@ triangulation. ```jldoctest julia> moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2]; -julia> moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); +julia> moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); julia> MOAE = subdivision_of_points(moaepts, moaeimnonreg0) Subdivision of points in ambient dimension 3 diff --git a/src/PolyhedralGeometry/SubdivisionOfPoints/functions.jl b/src/PolyhedralGeometry/SubdivisionOfPoints/functions.jl index 50775eeb099f..72b5b251074f 100644 --- a/src/PolyhedralGeometry/SubdivisionOfPoints/functions.jl +++ b/src/PolyhedralGeometry/SubdivisionOfPoints/functions.jl @@ -16,7 +16,7 @@ weights, but it will not be full-dimensional. ```jldoctest julia> moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2]; -julia> moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); +julia> moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); julia> MOAE = subdivision_of_points(moaepts, moaeimnonreg0) Subdivision of points in ambient dimension 3 diff --git a/src/PolyhedralGeometry/SubdivisionOfPoints/properties.jl b/src/PolyhedralGeometry/SubdivisionOfPoints/properties.jl index 761ce6ac8811..f7e03198f29c 100644 --- a/src/PolyhedralGeometry/SubdivisionOfPoints/properties.jl +++ b/src/PolyhedralGeometry/SubdivisionOfPoints/properties.jl @@ -14,7 +14,7 @@ Display the points of the "mother of all examples" non-regular triangulation. ```jldoctest julia> moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2]; -julia> moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); +julia> moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); julia> MOAE = subdivision_of_points(moaepts, moaeimnonreg0); @@ -62,7 +62,7 @@ julia> moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2] 1 2 1 1 1 2 -julia> moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]) +julia> moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]) 7×6 IncidenceMatrix [4, 5, 6] [1, 2, 4] @@ -245,7 +245,7 @@ triangulation of six points. ```jldoctest julia> moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2]; -julia> moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); +julia> moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]); julia> MOAE = subdivision_of_points(moaepts, moaeimnonreg0); diff --git a/src/PolyhedralGeometry/helpers.jl b/src/PolyhedralGeometry/helpers.jl index 8c4c1ff3ee38..dedeebea8ba9 100644 --- a/src/PolyhedralGeometry/helpers.jl +++ b/src/PolyhedralGeometry/helpers.jl @@ -6,9 +6,9 @@ import Polymake: IncidenceMatrix A matrix with boolean entries. Each row corresponds to a fixed element of a collection of mathematical objects and the same holds for the columns and a second (possibly equal) collection. A `1` at entry `(i, j)` is interpreted as an incidence between object `i` of the first collection and object `j` of the second one. # Examples -Note that the input and print of an `IncidenceMatrix` lists the non-zero indices for each row. +Note that the input of this example and the print of an `IncidenceMatrix` list the non-zero indices for each row. ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[4,5,6]]) +julia> IM = incidence_matrix([[1,2,3],[4,5,6]]) 2×6 IncidenceMatrix [1, 2, 3] [4, 5, 6] @@ -27,6 +27,109 @@ julia> IM[:, 4] """ IncidenceMatrix +@doc raw""" + incidence_matrix(r::Base.Integer, c::Base.Integer) + +Return an `IncidenceMatrix` of size r x c whose entries are all `false`. + +# Examples +```jldoctest +julia> IM = incidence_matrix(8, 5) +8×5 IncidenceMatrix +[] +[] +[] +[] +[] +[] +[] +[] + +``` +""" +incidence_matrix(r::Base.Integer, c::Base.Integer) = IncidenceMatrix(undef, r, c) + +@doc raw""" + incidence_matrix(mat::Union{AbstractMatrix{Bool}, IncidenceMatrix}) + +Convert `mat` to an `IncidenceMatrix`. + +# Examples +```jldoctest +julia> IM = incidence_matrix([true false true false true false; false true false true false true]) +2×6 IncidenceMatrix +[1, 3, 5] +[2, 4, 6] + +``` +""" +incidence_matrix(mat::Union{AbstractMatrix{Bool},IncidenceMatrix}) = IncidenceMatrix(mat) + +@doc raw""" + incidence_matrix(mat::AbstractMatrix) + +Convert the `0`/`1` matrix `mat` to an `IncidenceMatrix`. Entries become `true` if the initial entry is `1` and `false` if the initial entry is `0`. + +# Examples +```jldoctest +julia> IM = incidence_matrix([1 0 1 0 1 0; 0 1 0 1 0 1]) +2×6 IncidenceMatrix +[1, 3, 5] +[2, 4, 6] + +``` +""" +function incidence_matrix(mat::AbstractMatrix) + m, n = size(mat) + for i in 1:m + for j in 1:n + iszero(mat[i, j]) || isone(mat[i, j]) || + throw( + ArgumentError("incidence_matrix requires matrices with 0/1 or boolean entries.") + ) + end + end + return IncidenceMatrix(mat) +end + +@doc raw""" + incidence_matrix(r::Base.Integer, c::Base.Integer, incidenceRows::AbstractVector{<:AbstractVector{<:Base.Integer}}) + +Return an `IncidenceMatrix` of size r x c. The i-th element of `incidenceRows` lists the indices of the `true` entries of the i-th row. + +# Examples +```jldoctest +julia> IM = incidence_matrix(3, 4, [[2, 3], [1]]) +3×4 IncidenceMatrix +[2, 3] +[1] +[] + +``` +""" +incidence_matrix( + r::Base.Integer, + c::Base.Integer, + incidenceRows::AbstractVector{<:AbstractVector{<:Base.Integer}}, +) = IncidenceMatrix(r, c, incidenceRows) + +@doc raw""" + incidence_matrix(incidenceRows::AbstractVector{<:AbstractVector{<:Base.Integer}}) + +Return an `IncidenceMatrix` where the i-th element of `incidenceRows` lists the indices of the `true` entries of the i-th row. The dimensions of the result are the smallest possible row and column count that can be deduced from the input. + +# Examples +```jldoctest +julia> IM = incidence_matrix([[2, 3], [1]]) +2×3 IncidenceMatrix +[2, 3] +[1] + +``` +""" +incidence_matrix(incidenceRows::AbstractVector{<:AbstractVector{<:Base.Integer}}) = + IncidenceMatrix(incidenceRows) + number_of_rows(i::IncidenceMatrix) = Polymake.nrows(i) number_of_columns(i::IncidenceMatrix) = Polymake.ncols(i) @@ -40,7 +143,7 @@ Return the indices where the `n`-th row of `i` is `true`, as a `Set{Int}`. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[4,5,6]]) +julia> IM = incidence_matrix([[1,2,3],[4,5,6]]) 2×6 IncidenceMatrix [1, 2, 3] [4, 5, 6] @@ -62,7 +165,7 @@ Return the indices where the `n`-th column of `i` is `true`, as a `Set{Int}`. # Examples ```jldoctest -julia> IM = IncidenceMatrix([[1,2,3],[4,5,6]]) +julia> IM = incidence_matrix([[1,2,3],[4,5,6]]) 2×6 IncidenceMatrix [1, 2, 3] [4, 5, 6] diff --git a/src/PolyhedralGeometry/iterators.jl b/src/PolyhedralGeometry/iterators.jl index f87b07a8d9d6..d7b56e325448 100644 --- a/src/PolyhedralGeometry/iterators.jl +++ b/src/PolyhedralGeometry/iterators.jl @@ -324,6 +324,8 @@ function IncidenceMatrix(iter::SubObjectIterator) end end +incidence_matrix(iter::SubObjectIterator) = IncidenceMatrix(iter) + # primitive generators only for ray based iterators matrix(R::ZZRing, iter::SubObjectIterator{RayVector{QQFieldElem}}) = matrix(R, Polymake.common.primitive(matrix_for_polymake(iter))) diff --git a/src/PolyhedralGeometry/triangulations.jl b/src/PolyhedralGeometry/triangulations.jl index 774f29a27e00..084cf77e19af 100644 --- a/src/PolyhedralGeometry/triangulations.jl +++ b/src/PolyhedralGeometry/triangulations.jl @@ -471,7 +471,7 @@ Compute a triangulation of the square ```jldoctest julia> C = cube(2); -julia> cells = IncidenceMatrix([[1,2,3],[2,3,4]]); +julia> cells = incidence_matrix([[1,2,3],[2,3,4]]); julia> S = subdivision_of_points(C, cells) Subdivision of points in ambient dimension 2 diff --git a/src/TropicalGeometry/curve.jl b/src/TropicalGeometry/curve.jl index e6f70218c390..32ef9762d5eb 100644 --- a/src/TropicalGeometry/curve.jl +++ b/src/TropicalGeometry/curve.jl @@ -59,7 +59,7 @@ Return the embedded tropical curve consisting of the polyhedral complex `Sigma` ```jldoctest julia> verticesAndRays = [0 0; 1 0; 0 1; -1 -1]; -julia> incidenceMatrix = IncidenceMatrix([[1,2],[1,3],[1,4]]); +julia> incidenceMatrix = incidence_matrix([[1,2],[1,3],[1,4]]); julia> rayIndices = [2,3,4]; @@ -222,7 +222,7 @@ end # # Examples # ```jldoctest -# julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); +# julia> IM = incidence_matrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); # julia> tc = TropicalCurve(IM) # ERROR: MethodError: no method matching TropicalCurve(::Polymake.LibPolymake.IncidenceMatrixAllocated{Polymake.LibPolymake.NonSymmetric}) @@ -291,7 +291,7 @@ end # # Examples # ```jldoctest -# julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); +# julia> IM = incidence_matrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); # julia> tc = TropicalCurve(IM) # ERROR: MethodError: no method matching TropicalCurve(::Polymake.LibPolymake.IncidenceMatrixAllocated{Polymake.LibPolymake.NonSymmetric}) @@ -352,7 +352,7 @@ end # # Examples # ```jldoctest -# julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); +# julia> IM = incidence_matrix([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]); # julia> tc = TropicalCurve(IM) # ERROR: MethodError: no method matching TropicalCurve(::Polymake.LibPolymake.IncidenceMatrixAllocated{Polymake.LibPolymake.NonSymmetric}) @@ -411,7 +411,7 @@ end # ```jldoctest # julia> cg = complete_graph(5); -# julia> IM1=IncidenceMatrix([[src(e), dst(e)] for e in edges(cg)]) +# julia> IM1 = incidence_matrix([[src(e), dst(e)] for e in edges(cg)]) # 10×5 IncidenceMatrix # [1, 2] # [1, 3] @@ -438,7 +438,7 @@ end # julia> cg2 = complete_graph(3); -# julia> IM2=IncidenceMatrix([[src(e), dst(e)] for e in edges(cg2)]) +# julia> IM2 = incidence_matrix([[src(e), dst(e)] for e in edges(cg2)]) # 3×3 IncidenceMatrix # [1, 2] # [1, 3] @@ -456,7 +456,7 @@ end # [1] top-level scope # @ none:1 -# julia> IM3 = IncidenceMatrix([[1,2],[2,3],[3,4],[4,5],[1,5]]) +# julia> IM3 = incidence_matrix([[1,2],[2,3],[3,4],[4,5],[1,5]]) # 5×5 IncidenceMatrix # [1, 2] # [2, 3] @@ -500,7 +500,7 @@ end # # ```julia # # julia> using Revise, Plots, Oscar, Test; -# # julia> IM = IncidenceMatrix([[1,2],[1,3],[1,4]]); +# # julia> IM = incidence_matrix([[1,2],[1,3],[1,4]]); # # julia> VR = [0 0; 1 0; -1 0; 0 1]; diff --git a/src/TropicalGeometry/variety.jl b/src/TropicalGeometry/variety.jl index 7c2822564274..66f481378f70 100644 --- a/src/TropicalGeometry/variety.jl +++ b/src/TropicalGeometry/variety.jl @@ -46,7 +46,7 @@ Return the `TropicalVariety` whose polyhedral complex is `Sigma` with multiplici # Examples ```jldoctest -julia> Sigma = polyhedral_complex(IncidenceMatrix([[1],[2]]), [[0],[1]]) +julia> Sigma = polyhedral_complex(incidence_matrix([[1],[2]]), [[0],[1]]) Polyhedral complex in ambient dimension 1 julia> tropical_variety(Sigma) diff --git a/test/AlgebraicGeometry/ToricVarieties/affine_normal_varieties.jl b/test/AlgebraicGeometry/ToricVarieties/affine_normal_varieties.jl index b412a3037c33..a21649946a33 100644 --- a/test/AlgebraicGeometry/ToricVarieties/affine_normal_varieties.jl +++ b/test/AlgebraicGeometry/ToricVarieties/affine_normal_varieties.jl @@ -5,7 +5,7 @@ antv3 = affine_normal_toric_variety(antv2) antv4 = affine_normal_toric_variety(Oscar.positive_hull([1 0])) antv5 = affine_space(NormalToricVariety, 2) - antv6 = normal_toric_variety(IncidenceMatrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) + antv6 = normal_toric_variety(incidence_matrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) @testset "Basic properties" begin @test is_smooth(antv) == false diff --git a/test/AlgebraicGeometry/ToricVarieties/direct_products.jl b/test/AlgebraicGeometry/ToricVarieties/direct_products.jl index a617af339b27..05ced475ffe1 100644 --- a/test/AlgebraicGeometry/ToricVarieties/direct_products.jl +++ b/test/AlgebraicGeometry/ToricVarieties/direct_products.jl @@ -1,6 +1,6 @@ @testset "Direct products" begin - F5 = normal_toric_variety(IncidenceMatrix([[1, 2], [2, 3], [3, 4], [4, 1]]), [[1, 0], [0, 1], [-1, 5], [0, -1]]) + F5 = normal_toric_variety(incidence_matrix([[1, 2], [2, 3], [3, 4], [4, 1]]), [[1, 0], [0, 1], [-1, 5], [0, -1]]) P2 = normal_toric_variety(normal_fan(Oscar.simplex(2))) variety = F5 * P2 diff --git a/test/AlgebraicGeometry/ToricVarieties/intersection_numbers.jl b/test/AlgebraicGeometry/ToricVarieties/intersection_numbers.jl index e83d5a469cf9..384dfd4c369a 100644 --- a/test/AlgebraicGeometry/ToricVarieties/intersection_numbers.jl +++ b/test/AlgebraicGeometry/ToricVarieties/intersection_numbers.jl @@ -2,9 +2,9 @@ antv = affine_normal_toric_variety(Oscar.positive_hull([1 1; -1 1])) - antv2 = normal_toric_variety(IncidenceMatrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) + antv2 = normal_toric_variety(incidence_matrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) - v = normal_toric_variety(IncidenceMatrix([[1], [2], [3]]), [[1, 0], [0, 1], [-1, -1]]) + v = normal_toric_variety(incidence_matrix([[1], [2], [3]]), [[1, 0], [0, 1], [-1, -1]]) dP1 = del_pezzo_surface(NormalToricVariety, 1) c0 = cohomology_class(dP1, gens(cohomology_ring(dP1))[1]) diff --git a/test/AlgebraicGeometry/ToricVarieties/line_bundle_cohomologies.jl b/test/AlgebraicGeometry/ToricVarieties/line_bundle_cohomologies.jl index ec7bc5eed471..ba7074feeb1c 100644 --- a/test/AlgebraicGeometry/ToricVarieties/line_bundle_cohomologies.jl +++ b/test/AlgebraicGeometry/ToricVarieties/line_bundle_cohomologies.jl @@ -5,7 +5,7 @@ F5 = hirzebruch_surface(NormalToricVariety, 5) ray_generators = [[1, 0, 0,-2,-3], [0, 1, 0,-2,-3], [0, 0, 1,-2,-3], [-1,-1,-1,-2,-3], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1], [0, 0, 0,-2,-3]] - max_cones = IncidenceMatrix([[1, 2, 3, 5, 6], [1, 2, 3, 5, 7], [1, 2, 3, 6, 7], [2, 3, 4, 5, 6], [2, 3, 4, 5, 7], [2, 3, 4, 6, 7], [1, 3, 4, 5, 6], [1, 3, 4, 5, 7], [1, 3, 4, 6, 7], [1, 2, 4, 5, 6], [1, 2, 4, 5, 7], [1, 2, 4, 6, 7]]) + max_cones = incidence_matrix([[1, 2, 3, 5, 6], [1, 2, 3, 5, 7], [1, 2, 3, 6, 7], [2, 3, 4, 5, 6], [2, 3, 4, 5, 7], [2, 3, 4, 6, 7], [1, 3, 4, 5, 6], [1, 3, 4, 5, 7], [1, 3, 4, 6, 7], [1, 2, 4, 5, 6], [1, 2, 4, 5, 7], [1, 2, 4, 6, 7]]) weierstrass_over_p3 = normal_toric_variety(max_cones, ray_generators; non_redundant = true) l = toric_line_bundle(dP3, [1, 2, 3, 4]) diff --git a/test/AlgebraicGeometry/ToricVarieties/subvarieties.jl b/test/AlgebraicGeometry/ToricVarieties/subvarieties.jl index d54d38dc197d..f50c9a3305a1 100644 --- a/test/AlgebraicGeometry/ToricVarieties/subvarieties.jl +++ b/test/AlgebraicGeometry/ToricVarieties/subvarieties.jl @@ -1,6 +1,6 @@ @testset "Closed subvarieties" begin - antv = normal_toric_variety(IncidenceMatrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) + antv = normal_toric_variety(incidence_matrix([[1,2,3,4]]), [[1, 0, 0], [1, 0, 1], [1, 1, 1], [1, 1, 0]]) ntv = normal_toric_variety(cube(2)) (x1, x2, y1, y2) = gens(cox_ring(ntv)); diff --git a/test/AlgebraicGeometry/ToricVarieties/toric_divisor_classes.jl b/test/AlgebraicGeometry/ToricVarieties/toric_divisor_classes.jl index 775e514cd954..724830921061 100644 --- a/test/AlgebraicGeometry/ToricVarieties/toric_divisor_classes.jl +++ b/test/AlgebraicGeometry/ToricVarieties/toric_divisor_classes.jl @@ -5,7 +5,7 @@ P2 = projective_space(NormalToricVariety, 2) rs = [[0, 1, 0], [-1, 1, 1], [-1, 0, 0], [-1, -2, -2], [0, -1, -1], [0, 0, 1], [-1, -1, -1], [1, 0, 0]] - cs = IncidenceMatrix([[1, 2, 3], [1, 4, 5], [3, 6, 7], [4, 5, 6], [1, 4, 7], [5, 6, 8], [1, 6, 8], [2, 3, 6], [1, 5, 8], [1, 3, 7], [1, 2, 6], [4, 6, 7]]) + cs = incidence_matrix([[1, 2, 3], [1, 4, 5], [3, 6, 7], [4, 5, 6], [1, 4, 7], [5, 6, 8], [1, 6, 8], [2, 3, 6], [1, 5, 8], [1, 3, 7], [1, 2, 6], [4, 6, 7]]) test_space = normal_toric_variety(cs, rs) DC = toric_divisor_class(F5, [ZZRingElem(0), ZZRingElem(0)]) diff --git a/test/AlgebraicGeometry/ToricVarieties/toric_schemes.jl b/test/AlgebraicGeometry/ToricVarieties/toric_schemes.jl index b723ea89f198..8eb6b42cbd9f 100644 --- a/test/AlgebraicGeometry/ToricVarieties/toric_schemes.jl +++ b/test/AlgebraicGeometry/ToricVarieties/toric_schemes.jl @@ -97,7 +97,7 @@ end @testset "Lazy gluings" begin - f = polyhedral_fan(IncidenceMatrix([[1, 2, 3],[1, 4, 5, 6]]), [0 0 1; 1 0 1; 0 1 0; -1 0 1; -1 -1 1; 0 -1 1]) + f = polyhedral_fan(incidence_matrix([[1, 2, 3],[1, 4, 5, 6]]), [0 0 1; 1 0 1; 0 1 0; -1 0 1; -1 -1 1; 0 -1 1]) number_of_maximal_cones(f) ntv = normal_toric_variety(f) X = Oscar.underlying_scheme(ntv) diff --git a/test/Combinatorics/Graph.jl b/test/Combinatorics/Graph.jl index 0e6ef72156b8..d742171d7b67 100644 --- a/test/Combinatorics/Graph.jl +++ b/test/Combinatorics/Graph.jl @@ -67,7 +67,7 @@ @test n_vertices(egplc) == 2 @test n_edges(egplc) == 1 - @test incidence_matrix(egtriangle) == IncidenceMatrix([[1,2],[1,3],[2,3]]) + @test incidence_matrix(egtriangle) == incidence_matrix([[1,2],[1,3],[2,3]]) @test is_isomorphic(dual_graph(convex_hull([0 0 0; 1 0 0], nothing, [0 1 0])), Graph{Undirected}(2)) @test is_isomorphic(dual_graph(convex_hull([0 0 0], [0 0 1; 0 1 0; 1 0 0])), complete_graph(3)) diff --git a/test/Combinatorics/SimplicialComplexes.jl b/test/Combinatorics/SimplicialComplexes.jl index c89c3baa1856..3ba32a31e8b6 100644 --- a/test/Combinatorics/SimplicialComplexes.jl +++ b/test/Combinatorics/SimplicialComplexes.jl @@ -4,7 +4,7 @@ sphere = simplicial_complex([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]) - sphere2 = simplicial_complex(IncidenceMatrix([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])) + sphere2 = simplicial_complex(incidence_matrix([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])) not_known_to_be_a_ball = SimplicialComplex(Polymake.topaz.SimplicialComplex(FACETS=[[0,1]], BALL=nothing)) diff --git a/test/PolyhedralGeometry/cone.jl b/test/PolyhedralGeometry/cone.jl index ba97410c6449..8adcfc125b94 100644 --- a/test/PolyhedralGeometry/cone.jl +++ b/test/PolyhedralGeometry/cone.jl @@ -57,7 +57,7 @@ @test linear_inequality_matrix(facets(S, Cone1)) == _oscar_matrix_from_property(f, facets(S, Cone1)) @test Oscar.linear_matrix_for_polymake(facets(S, Cone1)) == _polymake_matrix_from_property(facets(S, Cone1)) @test _check_im_perm_rows(ray_indices(facets(S, Cone1)), [[1], [2]]) - @test _check_im_perm_rows(IncidenceMatrix(facets(S, Cone1)), [[1], [2]]) + @test _check_im_perm_rows(incidence_matrix(facets(S, Cone1)), [[1], [2]]) end @test _check_im_perm_rows(facets(IncidenceMatrix, Cone1), [[1], [2]]) @test facets(Halfspace, Cone1) isa SubObjectIterator{LinearHalfspace{T}} @@ -96,13 +96,13 @@ @test length(faces(Cone4, 1)) == 2 @test issetequal(faces(Cone2, 2), positive_hull.(Ref(f), [[1 0 0], [0 0 1]], [[0 1 0]])) @test _check_im_perm_rows(ray_indices(faces(Cone2, 2)), [[1], [2]]) - @test _check_im_perm_rows(IncidenceMatrix(faces(Cone2, 2)), [[1], [2]]) + @test _check_im_perm_rows(incidence_matrix(faces(Cone2, 2)), [[1], [2]]) @test _check_im_perm_rows(faces(IncidenceMatrix, Cone2, 2), [[1], [2]]) @test issetequal(faces(Cone4, 1), positive_hull.(Ref(f), [[0 0 1], [1 0 0]])) @test _check_im_perm_rows(ray_indices(faces(Cone4, 1)), [[1], [2]]) - @test _check_im_perm_rows(IncidenceMatrix(faces(Cone4, 1)), [[1], [2]]) + @test _check_im_perm_rows(incidence_matrix(faces(Cone4, 1)), [[1], [2]]) @test _check_im_perm_rows(faces(IncidenceMatrix, Cone4, 1), [[1], [2]]) - @test _check_im_perm_rows(IncidenceMatrix(faces(Cone5, 1)), [[1], [2], [3], [4]]) + @test _check_im_perm_rows(incidence_matrix(faces(Cone5, 1)), [[1], [2], [3], [4]]) @test isnothing(faces(Cone2, 1)) @test f_vector(Cone5) == [4, 4] diff --git a/test/PolyhedralGeometry/extended.jl b/test/PolyhedralGeometry/extended.jl index 10de0db2606e..c7fc6ee058a6 100644 --- a/test/PolyhedralGeometry/extended.jl +++ b/test/PolyhedralGeometry/extended.jl @@ -195,8 +195,8 @@ @test_throws ArgumentError convex_hull(vertices(Pos_poly), collect(vertices(Pos_poly))) @test_throws ArgumentError positive_hull(collect(vertices(Pos_poly))) - @test_throws ArgumentError IncidenceMatrix(lineality_space(Pos_poly)) - IM = IncidenceMatrix([[1]]) + @test_throws ArgumentError incidence_matrix(lineality_space(Pos_poly)) + IM = incidence_matrix([[1]]) lincone = positive_hull([1 0 0], [0 1 0]) @test positive_hull(rays_modulo_lineality(lincone)...) == lincone diff --git a/test/PolyhedralGeometry/group.jl b/test/PolyhedralGeometry/group.jl index a0945be71ea9..e749e1c87b1b 100644 --- a/test/PolyhedralGeometry/group.jl +++ b/test/PolyhedralGeometry/group.jl @@ -45,7 +45,7 @@ M = matroid_from_nonbases([[1, 2, 3, 4], [1, 2, 5, 6], [1, 3, 5, 7], [3, 4, 6, 8]], 8) GM = automorphism_group(M) @test is_trivial(GM) - IM = IncidenceMatrix(bases(M)) + IM = incidence_matrix(bases(M)) GIM = automorphism_group(IM) @test length(GIM) == 2 @test haskey(GIM, :on_cols) diff --git a/test/PolyhedralGeometry/lineality.jl b/test/PolyhedralGeometry/lineality.jl index 4b76e87430ec..abdf1280b44c 100644 --- a/test/PolyhedralGeometry/lineality.jl +++ b/test/PolyhedralGeometry/lineality.jl @@ -82,7 +82,7 @@ @testset "PolyhedralComplex" begin VR = [0 0 0; 1 0 0; 0 1 0; -1 0 0] - IM = IncidenceMatrix([[1, 2, 3], [1, 3, 4]]) + IM = incidence_matrix([[1, 2, 3], [1, 3, 4]]) far_vertices = [2, 3, 4] L = [0 0 1] PC = polyhedral_complex(IM, VR, far_vertices, L) diff --git a/test/PolyhedralGeometry/polyhedral_complex.jl b/test/PolyhedralGeometry/polyhedral_complex.jl index 45aaa618e7ad..605ca05a1da6 100644 --- a/test/PolyhedralGeometry/polyhedral_complex.jl +++ b/test/PolyhedralGeometry/polyhedral_complex.jl @@ -1,5 +1,5 @@ @testset "PolyhedralComplex{$T}" for (f, T) in _prepare_scalar_types() - I = IncidenceMatrix([[1, 2, 3], [2, 4]]) + I = incidence_matrix([[1, 2, 3], [2, 4]]) P = f.([0 0; 1 0; 0 1; 1 1]) P2 = f.([0 0 0; 1 0 0; 0 1 0; 1 1 0]) F = [4] @@ -86,7 +86,7 @@ @test vertex_indices(maximal_polyhedra(PCFLN)) == I[:, 1:3] @test ray_indices(maximal_polyhedra(PCFLN)) == I[:, 4:4] @test vertex_and_ray_indices(maximal_polyhedra(PCFLN)) == I - @test IncidenceMatrix(maximal_polyhedra(PCFLN)) == I + @test incidence_matrix(maximal_polyhedra(PCFLN)) == I @test maximal_polyhedra(IncidenceMatrix, PCFLN) == I @test polyhedral_complex(maximal_polyhedra(PCF)) isa PolyhedralComplex @@ -109,7 +109,7 @@ @testset "Fan conversion" begin F1 = normal_fan(cube(f, 2)) F2 = normal_fan(convex_hull(f, [0 0; 1 0])) - IM = IncidenceMatrix([[1, 2], [2, 3], [4]]) + IM = incidence_matrix([[1, 2], [2, 3], [4]]) R = [0 1 0; 0 0 1; 0 -1 0; 0 -1 -1] F3 = polyhedral_fan(f, IM, R, [1 0 0]) for F in [F1, F2, F3] diff --git a/test/PolyhedralGeometry/polyhedral_fan.jl b/test/PolyhedralGeometry/polyhedral_fan.jl index e012d5615d7b..133ba92b6a59 100644 --- a/test/PolyhedralGeometry/polyhedral_fan.jl +++ b/test/PolyhedralGeometry/polyhedral_fan.jl @@ -10,8 +10,8 @@ @test polyhedral_fan([Cone4, Cone5]) isa PolyhedralFan{T} F0 = polyhedral_fan([Cone4, Cone5]) I3 = [1 0 0; 0 1 0; 0 0 1] - incidence1 = IncidenceMatrix([[1, 2], [2, 3]]) - incidence2 = IncidenceMatrix([[1, 2]]) + incidence1 = incidence_matrix([[1, 2], [2, 3]]) + incidence2 = incidence_matrix([[1, 2]]) @test polyhedral_fan(f, incidence1, I3) isa PolyhedralFan{T} F1 = polyhedral_fan(f, incidence1, I3) F1NR = polyhedral_fan(f, incidence1, I3; non_redundant=true) @@ -46,7 +46,7 @@ @test maximal_cones(F1) isa SubObjectIterator{Cone{T}} @test dim.(maximal_cones(F1)) == [2, 2] @test _check_im_perm_rows(ray_indices(maximal_cones(F1)), incidence1) - @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F1)), incidence1) + @test _check_im_perm_rows(incidence_matrix(maximal_cones(F1)), incidence1) @test _check_im_perm_rows(maximal_cones(IncidenceMatrix, F1), incidence1) @test number_of_maximal_cones(F1) == 2 @test lineality_space(F2) isa SubObjectIterator{RayVector{T}} @@ -60,7 +60,7 @@ @test rays.(cones(F2, 2)) == [[], []] @test isnothing(cones(F2, 1)) @test _check_im_perm_rows(ray_indices(cones(F1, 2)), incidence1) - @test _check_im_perm_rows(IncidenceMatrix(cones(F1, 2)), incidence1) + @test _check_im_perm_rows(incidence_matrix(cones(F1, 2)), incidence1) @test _check_im_perm_rows(cones(IncidenceMatrix, F1, 2), incidence1) II = ray_indices(maximal_cones(NFsquare)) @@ -74,7 +74,7 @@ @test f_vector(NFsquare) == [4, 4] @test rays(F1NR) == collect(eachrow(I3)) @test _check_im_perm_rows(ray_indices(maximal_cones(F1NR)), incidence1) - @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F1NR)), incidence1) + @test _check_im_perm_rows(incidence_matrix(maximal_cones(F1NR)), incidence1) @test n_rays(F2NR) == 0 @test lineality_dim(F2NR) == 1 RMLF2NR = rays_modulo_lineality(F2NR) @@ -82,7 +82,7 @@ @test RMLF2NR[:rays_modulo_lineality] == collect(eachrow(R)) @test lineality_space(F2NR) == collect(eachrow(L)) @test _check_im_perm_rows(ray_indices(maximal_cones(F2NR)), incidence2) - @test _check_im_perm_rows(IncidenceMatrix(maximal_cones(F2NR)), incidence2) + @test _check_im_perm_rows(incidence_matrix(maximal_cones(F2NR)), incidence2) C = positive_hull(f, identity_matrix(ZZ, 0)) pf = polyhedral_fan(C) @@ -138,7 +138,7 @@ end end @testset "Star Subdivision" begin - f = polyhedral_fan(IncidenceMatrix([[1, 2, 3, 4]]), [1 0 0; 1 1 0; 1 1 1; 1 0 1]) + f = polyhedral_fan(incidence_matrix([[1, 2, 3, 4]]), [1 0 0; 1 1 0; 1 1 1; 1 0 1]) @test is_pure(f) @test is_fulldimensional(f) v0 = [1; 0; 0] @@ -151,7 +151,7 @@ end @test number_of_maximal_cones(sf1) == 3 @test number_of_maximal_cones(sf2) == 4 - ff = polyhedral_fan(IncidenceMatrix([[1], [2, 3]]), [1 0 0; -1 0 0; 0 1 0]) + ff = polyhedral_fan(incidence_matrix([[1], [2, 3]]), [1 0 0; -1 0 0; 0 1 0]) @test !is_pure(ff) @test !is_fulldimensional(ff) w0 = [1; 0; 0] diff --git a/test/PolyhedralGeometry/polyhedron.jl b/test/PolyhedralGeometry/polyhedron.jl index 405e7a31cbfb..3556449f31dd 100644 --- a/test/PolyhedralGeometry/polyhedron.jl +++ b/test/PolyhedralGeometry/polyhedron.jl @@ -118,16 +118,16 @@ convex_hull.(Ref(f), [[-1 -1; -1 1], [1 -1; 1 1], [-1 -1; 1 -1], [-1 1; 1 1]])) @test _check_im_perm_rows(vertex_indices(faces(square, 1)), [[1, 3], [2, 4], [1, 2], [3, 4]]) - @test ray_indices(faces(square, 1)) == IncidenceMatrix(4, 0) + @test ray_indices(faces(square, 1)) == incidence_matrix(4, 0) @test _check_im_perm_rows(vertex_and_ray_indices(faces(square, 1)), [[2, 4], [1, 3], [1, 2], [3, 4]]) - @test _check_im_perm_rows(IncidenceMatrix(faces(square, 1)), + @test _check_im_perm_rows(incidence_matrix(faces(square, 1)), [[1, 3], [2, 4], [1, 2], [3, 4]]) @test _check_im_perm_rows(faces(IncidenceMatrix, square, 1), [[1, 3], [2, 4], [1, 2], [3, 4]]) @test _check_im_perm_rows(facet_indices(vertices(square)), [[1, 3], [2, 3], [1, 4], [2, 4]]) - @test _check_im_perm_rows(IncidenceMatrix(vertices(square)), + @test _check_im_perm_rows(incidence_matrix(vertices(square)), [[1, 3], [2, 3], [1, 4], [2, 4]]) @test _check_im_perm_rows(vertices(IncidenceMatrix, square), [[1, 3], [2, 3], [1, 4], [2, 4]]) @@ -137,7 +137,7 @@ @test _check_im_perm_rows(vertex_indices(faces(Pos, 1)), [[1], [1], [1]]) @test _check_im_perm_rows(ray_indices(faces(Pos, 1)), [[1], [2], [3]]) @test _check_im_perm_rows(vertex_and_ray_indices(faces(Pos, 1)), [[1, 4], [2, 4], [3, 4]]) - @test _check_im_perm_rows(IncidenceMatrix(faces(Pos, 1)), [[1, 4], [2, 4], [3, 4]]) + @test _check_im_perm_rows(incidence_matrix(faces(Pos, 1)), [[1, 4], [2, 4], [3, 4]]) @test _check_im_perm_rows(faces(IncidenceMatrix, Pos, 1), [[1, 4], [2, 4], [3, 4]]) @test isnothing(faces(Q2, 0)) v = vertices(minkowski_sum(Q0, square)) @@ -166,17 +166,17 @@ end @test _check_im_perm_rows(ray_indices(facets(S, Pos)), [[2, 3], [1, 3], [1, 2]]) @test _check_im_perm_rows(vertex_and_ray_indices(facets(S, Pos)), [[2, 3, 4], [1, 3, 4], [1, 2, 4]]) - @test _check_im_perm_rows(IncidenceMatrix(facets(S, Pos)), [[2, 3, 4], [1, 3, 4], [1, 2, 4]]) + @test _check_im_perm_rows(incidence_matrix(facets(S, Pos)), [[2, 3, 4], [1, 3, 4], [1, 2, 4]]) @test _check_im_perm_rows(vertex_indices(facets(S, Pos)), [[1], [1], [1]]) end @test _check_im_perm_rows(facets(IncidenceMatrix, Pos), [[2, 3, 4], [1, 3, 4], [1, 2, 4]] ) @test _check_im_perm_rows(facet_indices(vertices(Pos)), [[1, 2, 3]]) - @test _check_im_perm_rows(IncidenceMatrix(vertices(Pos)), [[1, 2, 3]]) + @test _check_im_perm_rows(incidence_matrix(vertices(Pos)), [[1, 2, 3]]) @test _check_im_perm_rows(vertices(IncidenceMatrix, Pos), [[1, 2, 3]]) @test _check_im_perm_rows(facet_indices(rays(Pos)), [[1, 3], [2, 3], [1, 2]]) - @test _check_im_perm_rows(IncidenceMatrix(rays(Pos)), [[2, 3], [1, 3], [1, 2]]) + @test _check_im_perm_rows(incidence_matrix(rays(Pos)), [[2, 3], [1, 3], [1, 2]]) @test _check_im_perm_rows(rays(IncidenceMatrix, Pos), [[1, 3], [2, 3], [1, 2]]) @test facets(Pair, Pos) isa SubObjectIterator{Pair{Matrix{T},T}} @test facets(Pos) isa SubObjectIterator{AffineHalfspace{T}} @@ -606,7 +606,7 @@ @test affine_inequality_matrix(facets(S, D)) == matrix(R, hcat(-b, vcat(A...))) @test halfspace_matrix_pair(facets(S, D)) == (A=matrix(R, vcat(A...)), b=b) - @test ray_indices(facets(S, D)) == IncidenceMatrix(12, 0) + @test ray_indices(facets(S, D)) == incidence_matrix(12, 0) @test _check_im_perm_rows(vertex_indices(facets(S, D)), [ [1, 3, 5, 9, 10], [1, 2, 3, 4, 6], @@ -635,7 +635,7 @@ [9, 10, 14, 17, 19], [15, 17, 18, 19, 20], ]) - @test _check_im_perm_rows(IncidenceMatrix(facets(S, D)), [ + @test _check_im_perm_rows(incidence_matrix(facets(S, D)), [ [1, 3, 5, 9, 10], [1, 2, 3, 4, 6], [1, 2, 5, 7, 8], @@ -666,9 +666,9 @@ [9, 10, 14, 17, 19], [15, 17, 18, 19, 20], ]) - @test facet_indices(rays(D)) == IncidenceMatrix(0, 12) - @test IncidenceMatrix(rays(D)) == IncidenceMatrix(0, 12) - @test rays(IncidenceMatrix, D) == IncidenceMatrix(0, 12) + @test facet_indices(rays(D)) == incidence_matrix(0, 12) + @test incidence_matrix(rays(D)) == incidence_matrix(0, 12) + @test rays(IncidenceMatrix, D) == incidence_matrix(0, 12) @test _check_im_perm_rows(facet_indices(vertices(D)), [ [1, 2, 3], [2, 3, 6], @@ -691,7 +691,7 @@ [9, 11, 12], [4, 9, 12], ]) - @test _check_im_perm_rows(IncidenceMatrix(vertices(D)), [ + @test _check_im_perm_rows(incidence_matrix(vertices(D)), [ [1, 2, 3], [2, 3, 6], [1, 2, 7], diff --git a/test/PolyhedralGeometry/scalar_types.jl b/test/PolyhedralGeometry/scalar_types.jl index 802b1794a306..450e0ccc6280 100644 --- a/test/PolyhedralGeometry/scalar_types.jl +++ b/test/PolyhedralGeometry/scalar_types.jl @@ -47,7 +47,7 @@ @test length(lattice_points(sd)) == 11 let pc = polyhedral_complex( - E, IncidenceMatrix(facets(sd)), vertices(sd); non_redundant=true + E, incidence_matrix(facets(sd)), vertices(sd); non_redundant=true ) @test issetequal(maximal_polyhedra(pc), faces(sd, 2)) end diff --git a/test/PolyhedralGeometry/setup_tests.jl b/test/PolyhedralGeometry/setup_tests.jl index 6d2ba9757c99..c6cc8613922f 100644 --- a/test/PolyhedralGeometry/setup_tests.jl +++ b/test/PolyhedralGeometry/setup_tests.jl @@ -9,7 +9,7 @@ if !isdefined(Main, :_prepare_scalar_types) end function _check_im_perm_rows(inc::IncidenceMatrix, o) - oinc = IncidenceMatrix(o) + oinc = incidence_matrix(o) nr, nc = size(inc) (nr, nc) == size(oinc) && issetequal(Polymake.row.(Ref(inc), 1:nr), diff --git a/test/PolyhedralGeometry/subdivision_of_points.jl b/test/PolyhedralGeometry/subdivision_of_points.jl index 2608f0e46f6f..eebf7eae44b8 100644 --- a/test/PolyhedralGeometry/subdivision_of_points.jl +++ b/test/PolyhedralGeometry/subdivision_of_points.jl @@ -2,7 +2,7 @@ C = cube(2) square_weights = [0, 0, 1, 2] square_max_cells = [[1, 2, 3], [2, 3, 4]] - square_incidence = IncidenceMatrix(square_max_cells) + square_incidence = incidence_matrix(square_max_cells) square_by_weights = subdivision_of_points(C, square_weights) square_by_cells = subdivision_of_points(C, square_max_cells) @@ -21,9 +21,9 @@ moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2] fulldim_moaepts = moaepts[:, 2:3] - moaeimreg0 = IncidenceMatrix(1, 6) + moaeimreg0 = incidence_matrix(1, 6) moaeimreg0[1, :] = [1, 1, 1, 0, 0, 0] - moaeimnonreg0 = IncidenceMatrix([ + moaeimnonreg0 = incidence_matrix([ [4, 5, 6], [1, 4, 2], [2, 4, 5], [2, 3, 5], [3, 5, 6], [1, 3, 6], [1, 4, 6] ]) diff --git a/test/PolyhedralGeometry/types.jl b/test/PolyhedralGeometry/types.jl index f54e31a349b7..d079c17df422 100644 --- a/test/PolyhedralGeometry/types.jl +++ b/test/PolyhedralGeometry/types.jl @@ -1,12 +1,21 @@ @testset "types" begin @testset "IncidenceMatrix" begin - im = IncidenceMatrix([[1, 2, 3], [4, 5, 6]]) + im = incidence_matrix([[1, 2, 3], [4, 5, 6]]) @test nrows(im) == 2 @test ncols(im) == 6 @test row(im, 1) isa Set{Int} @test row(im, 1) == Set{Int}([1, 2, 3]) @test column(im, 2) isa Set{Int} @test column(im, 2) == Set{Int}([1]) + + @testset "IncidenceMatrix constructions" begin + @test incidence_matrix([true true true false false false; false false false true true true]) == im + @test incidence_matrix([1 1 1 0 0 0; 0 0 0 1 1 1]) == im + @test incidence_matrix(4, 2) == incidence_matrix([0 0; 0 0; 0 0; 0 0]) + @test incidence_matrix(im) == im + @test incidence_matrix(3, 8, [[1, 2, 3], [4, 5, 6]]) == incidence_matrix([1 1 1 0 0 0 0 0; 0 0 0 1 1 1 0 0; 0 0 0 0 0 0 0 0]) + @test_throws ArgumentError incidence_matrix([1 1 1 0 0 0; 0 0 0 1 2 1]) + end end a = [1, 2, 3] diff --git a/test/Serialization/PolyhedralGeometry.jl b/test/Serialization/PolyhedralGeometry.jl index 0d8b8e1f4c03..9d04515719d3 100644 --- a/test/Serialization/PolyhedralGeometry.jl +++ b/test/Serialization/PolyhedralGeometry.jl @@ -87,7 +87,7 @@ using Oscar: _integer_variables end @testset "PolyhedralComplex" begin - IM = IncidenceMatrix([[1,2,3],[1,3,4]]) + IM = incidence_matrix([[1,2,3],[1,3,4]]) vr = [0 0; 1 0; 1 1; 0 1] PC = polyhedral_complex(IM, vr) test_save_load_roundtrip(path, PC) do loaded @@ -156,7 +156,7 @@ using Oscar: _integer_variables @testset "SubdivisionOfPoints" begin moaepts = [4 0 0; 0 4 0; 0 0 4; 2 1 1; 1 2 1; 1 1 2] - moaeimnonreg0 = IncidenceMatrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]) + moaeimnonreg0 = incidence_matrix([[4,5,6],[1,4,2],[2,4,5],[2,3,5],[3,5,6],[1,3,6],[1,4,6]]) MOAE = subdivision_of_points(moaepts, moaeimnonreg0) test_save_load_roundtrip(path, MOAE) do loaded @test number_of_maximal_cells(MOAE) == number_of_maximal_cells(loaded) diff --git a/test/Serialization/TropicalGeometry.jl b/test/Serialization/TropicalGeometry.jl index 17f01e907466..7d80d1c430cf 100644 --- a/test/Serialization/TropicalGeometry.jl +++ b/test/Serialization/TropicalGeometry.jl @@ -10,7 +10,7 @@ end @testset "Embedded" begin - IM = IncidenceMatrix([[1,2],[1,3],[1,4]]) + IM = incidence_matrix([[1,2],[1,3],[1,4]]) VR = [0 0; 1 0; -1 0; 0 1] PC = polyhedral_complex(QQFieldElem, IM, VR) TC = tropical_curve(PC) From eb407e514ce86963291225213299451d9c48a22e Mon Sep 17 00:00:00 2001 From: ederc Date: Tue, 29 Oct 2024 09:28:31 +0100 Subject: [PATCH 18/84] Splits groebner.jl into parts (#4250) --- src/Rings/Rings.jl | 2 +- src/Rings/groebner.jl | 1792 ------------------- src/Rings/groebner/f4.jl | 88 + src/Rings/groebner/fglm.jl | 162 ++ src/Rings/groebner/general.jl | 242 +++ src/Rings/groebner/generators.jl | 72 + src/Rings/groebner/groebner.jl | 10 + src/Rings/groebner/highest-corner.jl | 23 + src/Rings/groebner/hilbert-driven.jl | 239 +++ src/Rings/groebner/leading-ideal.jl | 67 + src/Rings/groebner/modular.jl | 135 ++ src/Rings/groebner/reduce.jl | 662 +++++++ src/Rings/groebner/transformation-matrix.jl | 89 + 13 files changed, 1790 insertions(+), 1793 deletions(-) delete mode 100644 src/Rings/groebner.jl create mode 100644 src/Rings/groebner/f4.jl create mode 100644 src/Rings/groebner/fglm.jl create mode 100644 src/Rings/groebner/general.jl create mode 100644 src/Rings/groebner/generators.jl create mode 100644 src/Rings/groebner/groebner.jl create mode 100644 src/Rings/groebner/highest-corner.jl create mode 100644 src/Rings/groebner/hilbert-driven.jl create mode 100644 src/Rings/groebner/leading-ideal.jl create mode 100644 src/Rings/groebner/modular.jl create mode 100644 src/Rings/groebner/reduce.jl create mode 100644 src/Rings/groebner/transformation-matrix.jl diff --git a/src/Rings/Rings.jl b/src/Rings/Rings.jl index 2a0d9ad474f8..4c25c36d612b 100644 --- a/src/Rings/Rings.jl +++ b/src/Rings/Rings.jl @@ -5,7 +5,7 @@ include("mpoly_types.jl") include("mpoly-graded.jl") include("hilbert_zach.jl") include("mpoly-ideals.jl") -include("groebner.jl") +include("groebner/groebner.jl") include("solving.jl") include("MPolyQuo.jl") include("FractionalIdeal.jl") diff --git a/src/Rings/groebner.jl b/src/Rings/groebner.jl deleted file mode 100644 index a2ba4d40f879..000000000000 --- a/src/Rings/groebner.jl +++ /dev/null @@ -1,1792 +0,0 @@ -# groebner stuff ####################################################### -@doc raw""" - groebner_assure(I::MPolyIdeal, complete_reduction::Bool = false, need_global::Bool = false) - groebner_assure(I::MPolyIdeal, ordering::MonomialOrdering, complete_reduction::Bool = false) - -**Note**: Internal function, subject to change, do not use. - -Given an ideal `I` in a multivariate polynomial ring this function assures that a -Gröbner basis w.r.t. the given monomial ordering is attached to `I` in `I.gb`. -It *currently* also ensures that the basis is defined on the Singular side in -`I.gb.S`, but this should not be relied upon: use `singular_assure(I.gb)` before -accessing `I.gb.S`. - -# Examples -```jldoctest -julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> I = ideal([x*y-3*x,y^3-2*x^2*y]) -Ideal generated by - x*y - 3*x - -2*x^2*y + y^3 - -julia> Oscar.groebner_assure(I, degrevlex(R)); - -julia> I.gb[degrevlex(R)] -Gröbner basis with elements - 1 -> x*y - 3*x - 2 -> y^3 - 6*x^2 - 3 -> 2*x^3 - 9*x -with respect to the ordering - degrevlex([x, y]) -``` -""" -function groebner_assure(I::MPolyIdeal, complete_reduction::Bool = false, need_global::Bool = false) - if !isempty(I.gb) - for G in values(I.gb) - need_global || return G - is_global(G.ord) || continue - complete_reduction || return G - if !G.isReduced - I.gb[G.ord] = _compute_standard_basis(G, G.ord, true) - end - return I.gb[G.ord] - end - end - ord = default_ordering(base_ring(I)) - (need_global <= is_global(ord)) || error("Monomial ordering must be global.") - I.gb[ord] = groebner_assure(I, ord, complete_reduction) - return I.gb[ord] -end - -function groebner_assure(I::MPolyIdeal, ordering::MonomialOrdering, complete_reduction::Bool = false) - return get!(I.gb, ordering) do - _compute_standard_basis(I.gens, ordering, complete_reduction) - end -end - -function oscar_groebner_generators(I::MPolyIdeal, ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) - standard_basis(I, ordering=ordering, complete_reduction = complete_reduction) - oscar_assure(I.gb[ordering]) - return I.gb[ordering].gens.O -end - -function singular_groebner_generators(I::MPolyIdeal, ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) - standard_basis(I, ordering=ordering, complete_reduction = complete_reduction) - return singular_generators(I.gb[ordering], ordering) -end - -function singular_groebner_generators(I::MPolyIdeal, complete_reduction::Bool, need_global::Bool) - G = groebner_assure(I, complete_reduction, need_global) - return singular_generators(G, G.ord) -end - -@doc raw""" - _compute_standard_basis(B::IdealGens; ordering::MonomialOrdering, - complete_reduction::Bool = false) - -**Note**: Internal function, subject to change, do not use. - -Given an `IdealGens` `B` and optional parameters `ordering` for a monomial ordering and `complete_reduction` -this function computes a Gröbner basis (if `complete_reduction = true` the reduced Gröbner basis) of the -ideal spanned by the elements in `B` w.r.t. the given monomial ordering `ordering`. The Gröbner basis is then -returned in `B.S`. - -# Examples -```jldoctest -julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) -Ideal generating system with elements - 1 -> x*y - 3*x - 2 -> -2*x^2*y + y^3 - -julia> B = Oscar._compute_standard_basis(A, degrevlex(R)) -Gröbner basis with elements - 1 -> x*y - 3*x - 2 -> y^3 - 6*x^2 - 3 -> 2*x^3 - 9*x -with respect to the ordering - degrevlex([x, y]) -``` -""" -function _compute_standard_basis(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) - gensSord = singular_generators(B, ordering) - i = Singular.std(gensSord, complete_reduction = complete_reduction) - BA = IdealGens(B.Ox, i, complete_reduction) - BA.isGB = true - BA.ord = ordering - if isdefined(BA, :S) - BA.S.isGB = true - end - return BA -end - -# standard basis for non-global orderings ############################# -@doc raw""" - standard_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), - complete_reduction::Bool = false, algorithm::Symbol = :buchberger) - -Return a standard basis of `I` with respect to `ordering`. - -The keyword `algorithm` can be set to -- `:buchberger` (implementation of Buchberger's algorithm in *Singular*), -- `:modular` (implementation of multi-modular approach, if applicable), -- `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package), -- `:fglm` (implementation of the FGLM algorithm in *Singular*), -- `:hc` (implementation of Buchberger's algorithm in *Singular* trying to first compute the highest corner modulo some prime), and -- `:hilbert` (implementation of a Hilbert driven Gröbner basis computation in *Singular*). - -!!! note - See the description of the functions `groebner_basis_hilbert_driven`, `fglm`, - and `f4` in the OSCAR documentation for some more details and for restrictions - on the input data when using these versions of the standard basis algorithm. - -!!! note - The returned standard basis is reduced if `ordering` is `global` and `complete_reduction = true`. - -# Examples -```jldoctest -julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]); - -julia> I = ideal([x*(x+1), x^2-y^2+(x-2)*y]); - -julia> standard_basis(I, ordering = negdegrevlex(R)) -Standard basis with elements - 1 -> x - 2 -> y -with respect to the ordering - negdegrevlex([x, y]) -``` -""" -function standard_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), - complete_reduction::Bool = false, algorithm::Symbol = :buchberger) - complete_reduction && @assert is_global(ordering) - @req is_exact_type(elem_type(base_ring(I))) "This functionality is only supported over exact fields." - if haskey(I.gb, ordering) && (complete_reduction == false || I.gb[ordering].isReduced == true) - return I.gb[ordering] - end - if algorithm == :buchberger - if !haskey(I.gb, ordering) - I.gb[ordering] = _compute_standard_basis(I.gens, ordering, complete_reduction) - elseif complete_reduction == true - I.gb[ordering] = _compute_standard_basis(I.gb[ordering], ordering, complete_reduction) - end - elseif algorithm == :modular - if is_f4_applicable(I, ordering) - # since msolve v0.7.0 is most of the time more efficient - # to compute a reduced GB by default - groebner_basis_f4(I, complete_reduction=true) - elseif base_ring(I) isa QQMPolyRing - groebner_basis_modular(I, ordering=ordering) - else - error("Modular option not applicable in this setting.") - end - elseif algorithm == :fglm - _compute_groebner_basis_using_fglm(I, ordering) - elseif algorithm == :hc - standard_basis_highest_corner(I, ordering=ordering) - elseif algorithm == :hilbert - weights = _find_weights(gens(I)) - if !any(iszero, weights) - J, target_ordering, hn = I, ordering, nothing - else - R = base_ring(I) - K = iszero(characteristic(R)) && !haskey(I.gb, degrevlex(R)) ? _mod_rand_prime(I) : I - S = base_ring(K) - gb = groebner_assure(K, degrevlex(S)) - # 2024-02-09 Next lines "blindly" updated to use new homogenization UI - H = homogenizer(S, "w") - K_hom = H(K) - gb_hom = IdealGens(H.(gens(gb))) - gb_hom.isGB = true - K_hom.gb[degrevlex(S)] = gb_hom - singular_assure(K_hom.gb[degrevlex(S)]) - hn = hilbert_series(quo(base_ring(K_hom), K_hom)[1])[1] - H2 = homogenizer(R, "w") - J = H2(I) - weights = ones(Int, ngens(base_ring(J))) - target_ordering = _extend_mon_order(ordering, base_ring(J)) - end - GB = groebner_basis_hilbert_driven(J, destination_ordering=target_ordering, - complete_reduction=complete_reduction, - weights=weights, - hilbert_numerator=hn) - if base_ring(I) == base_ring(J) - I.gb[ordering] = GB - else - DH2 = dehomogenizer(H2) - GB_dehom_gens = DH2.(gens(GB)) - I.gb[ordering] = IdealGens(GB_dehom_gens, ordering, isGB = true) - end - elseif algorithm == :f4 - # since msolve v0.7.0 is most of the time more efficient - # to compute a reduced GB by default - groebner_basis_f4(I, complete_reduction=true) - end - return I.gb[ordering] -end - -@doc raw""" - groebner_basis(I::MPolyIdeal; - ordering::MonomialOrdering = default_ordering(base_ring(I)), - complete_reduction::Bool = false, algorithm::Symbol = :buchberger) - -If `ordering` is global, return a Gröbner basis of `I` with respect to `ordering`. - -The keyword `algorithm` can be set to -- `:buchberger` (implementation of Buchberger's algorithm in *Singular*), -- `:modular` (implementation of multi-modular approach, if applicable), -- `:hilbert` (implementation of a Hilbert driven Gröbner basis computation in *Singular*), -- `:fglm` (implementation of the FGLM algorithm in *Singular*), and -- `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package). - -!!! note - See the description of the functions `groebner_basis_hilbert_driven`, `fglm`, - and `f4` in the OSCAR documentation for some more details and for restrictions - on the input data when using these versions of the standard basis algorithm. - -!!! note - The returned Gröbner basis is reduced if `complete_reduction = true`. - -# Examples -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); - -julia> I = ideal(R, [y-x^2, z-x^3]); - -julia> G = groebner_basis(I) -Gröbner basis with elements - 1 -> y^2 - x*z - 2 -> x*y - z - 3 -> x^2 - y -with respect to the ordering - degrevlex([x, y, z]) - -julia> elements(G) -3-element Vector{QQMPolyRingElem}: - -x*z + y^2 - x*y - z - x^2 - y - -julia> elements(G) == gens(G) -true - -julia> groebner_basis(I, ordering = lex(R)) -Gröbner basis with elements - 1 -> y^3 - z^2 - 2 -> x*z - y^2 - 3 -> x*y - z - 4 -> x^2 - y -with respect to the ordering - lex([x, y, z]) -``` -```jldoctest -julia> R, (x, y) = graded_polynomial_ring(QQ, [:x, :y], [1, 3]); - -julia> I = ideal(R, [x*y-3*x^4,y^3-2*x^6*y]); - -julia> groebner_basis(I) -Gröbner basis with elements - 1 -> 3*x^4 - x*y - 2 -> 2*x^3*y^2 - 3*y^3 - 3 -> x*y^3 - 4 -> y^4 -with respect to the ordering - wdegrevlex([x, y], [1, 3]) -``` - -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); - -julia> V = [3*x^3*y+x^3+x*y^3+y^2*z^2, 2*x^3*z-x*y-x*z^3-y^4-z^2, - 2*x^2*y*z-2*x*y^2+x*z^2-y^4]; - -julia> I = ideal(R, V); - -julia> G = groebner_basis(I, ordering = lex(R), algorithm = :fglm); - -julia> length(G) -8 - -julia> total_degree(G[8]) -34 - -julia> leading_coefficient(G[8]) --91230304237130414552564280286681870842473427917231798336639893796481988733936505735341479640589040146625319419037353645834346047404145021391726185993823650399589880820226804328750 -``` -""" -function groebner_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool=false, - algorithm::Symbol = :buchberger) - is_global(ordering) || error("Ordering must be global") - return standard_basis(I, ordering=ordering, complete_reduction=complete_reduction, algorithm=algorithm) -end - -@doc raw""" - standard_basis_highest_corner(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) - -Return a standard basis of `I` with respect to `ordering`. `ordering` needs to be local, the coefficient ring needs to be `QQ`. -The algorithm first computes a standard basis over a finite field in order to get an upper bound for the highest corner fast. -Then this bound is used to speed up the standard basis computation over `QQ´. -""" -function standard_basis_highest_corner(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) - @req is_local(ordering) "Monomial ordering must be local for this variant." - @req coefficient_ring(I) == QQ "Base ring must be QQ." - - #= apply highest corner standard basis variant in Singular =# - ssb = Singular.LibStandard.groebner(singular_groebner_generators(I, ordering), "HC") - - sb = IdealGens(I.gens.Ox, ssb, false) - sb.isGB = true - sb.ord = ordering - if isdefined(sb, :S) - sb.S.isGB = true - end - I.gb[ordering] = sb - return sb -end - -function is_f4_applicable(I::MPolyIdeal, ordering::MonomialOrdering) - return (ordering == degrevlex(base_ring(I)) && !is_graded(base_ring(I)) - && ((coefficient_ring(I) isa FqField - && absolute_degree(coefficient_ring(I)) == 1 - && characteristic(coefficient_ring(I)) < 2^31) - || base_ring(I) isa QQMPolyRing)) -end - -@doc raw""" - groebner_basis_f4(I::MPolyIdeal, ) - -Compute a Gröbner basis of `I` with respect to `degrevlex` using Faugère's F4 algorithm. -See [Fau99](@cite) for more information. - -!!! note - At current state only prime fields of characteristic `0 < p < 2^{31}` and the rationals are supported. - -# Possible keyword arguments -- `initial_hts::Int=17`: initial hash table size `log_2`. -- `nr_thrds::Int=1`: number of threads for parallel linear algebra. -- `max_nr_pairs::Int=0`: maximal number of pairs per matrix, only bounded by minimal degree if `0`. -- `la_option::Int=2`: linear algebra option: exact sparse-dense (`1`), exact sparse (`2`, default), probabilistic sparse-dense (`42`), probabilistic sparse(`44`). -- `eliminate::Int=0`: size of first block of variables to be eliminated. -- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I` -- `normalize::Bool=true`: normalizes elements in computed Gröbner basis for `I` -- `truncate_lifting::Int=0`: degree up to which the elements of the Gröbner basis are lifted to `QQ`, `0` for complete lifting -- `info_level::Int=0`: info level printout: off (`0`, default), summary (`1`), detailed (`2`). - -# Examples -```jldoctest -julia> R,(x,y,z) = polynomial_ring(GF(101), [:x,:y,:z]) -(Multivariate polynomial ring in 3 variables over GF(101), FqMPolyRingElem[x, y, z]) - -julia> I = ideal(R, [x+2*y+2*z-1, x^2+2*y^2+2*z^2-x, 2*x*y+2*y*z-y]) -Ideal generated by - x + 2*y + 2*z + 100 - x^2 + 100*x + 2*y^2 + 2*z^2 - 2*x*y + 2*y*z + 100*y - -julia> groebner_basis_f4(I) -Gröbner basis with elements - 1 -> x + 2*y + 2*z + 100 - 2 -> y*z + 82*z^2 + 10*y + 40*z - 3 -> y^2 + 60*z^2 + 20*y + 81*z - 4 -> z^3 + 28*z^2 + 64*y + 13*z -with respect to the ordering - degrevlex([x, y, z]) -``` -""" -function groebner_basis_f4( - I::MPolyIdeal; - initial_hts::Int=17, - nr_thrds::Int=1, - max_nr_pairs::Int=0, - la_option::Int=2, - eliminate::Int=0, - complete_reduction::Bool=true, - normalize::Bool=true, - truncate_lifting::Int=0, - info_level::Int=0 - ) - - AI = AlgebraicSolving.Ideal(I.gens.O) - vars = gens(base_ring(I))[eliminate+1:end] - ord = degrevlex(vars) - if length(AI.gens) == 0 - I.gb[ord] = IdealGens(I.gens.Ox, singular_generators(I), complete_reduction) - I.gb[ord].ord = ord - I.gb[ord].isGB = true - I.gb[ord].S.isGB = true - else - AlgebraicSolving.groebner_basis(AI, - initial_hts = initial_hts, - nr_thrds = nr_thrds, - max_nr_pairs = max_nr_pairs, - la_option = la_option, - eliminate = eliminate, - complete_reduction = complete_reduction, - normalize = normalize, - truncate_lifting = truncate_lifting, - info_level = info_level) - - I.gb[ord] = - IdealGens(AI.gb[eliminate], ord, keep_ordering = false, isGB = true) - I.gb[ord].isReduced = complete_reduction - end - return I.gb[ord] -end - -@doc raw""" - _compute_standard_basis_with_transform(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) - -**Note**: Internal function, subject to change, do not use. - -Given an `IdealGens` `B` and optional parameters `ordering` for a monomial ordering and `complete_reduction` -this function computes a standard basis (if `ordering` is a global monomial ordering and `complete_reduction = true` -the reduced Gröbner basis) of the ideal spanned by the elements in `B` w.r.t. the given monomial ordering `ordering` -and the transformation matrix from the ideal to the standard basis. Return value is a IdealGens together with a map. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) -Ideal generating system with elements - 1 -> x*y - 3*x - 2 -> -2*x^2*y + y^3 - -julia> B,m = Oscar._compute_standard_basis_with_transform(A, degrevlex(R)) -(Ideal generating system with 3 elements with associated ordering degrevlex([x, y]), [1 2*x -2*x^2+y^2+3*y+9; 0 1 -x]) -``` -""" -function _compute_standard_basis_with_transform(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) - istd, m = Singular.lift_std(singular_generators(B, ordering), complete_reduction = complete_reduction) - return IdealGens(B.Ox, istd), map_entries(B.Ox, m) -end - -@doc raw""" - standard_basis_with_transformation_matrix(I::MPolyIdeal; - ordering::MonomialOrdering = default_ordering(base_ring(I)), - complete_reduction::Bool=false) - -Return a pair `G`, `T`, say, where `G` is a standard basis of `I` with respect to `ordering`, and `T` -is a transformation matrix from `gens(I)` to `G`. That is, `gens(I)*T == G`. - -!!! note - The returned Gröbner basis is reduced if `ordering` is a global monomial odering and `complete_reduction = true`. - -# Examples -```jldoctest -julia> R,(x,y) = polynomial_ring(QQ,[:x,:y]); - -julia> I = ideal([x*y^2-1,x^3+y^2+x*y]); - -julia> G, T = standard_basis_with_transformation_matrix(I, ordering=neglex(R)) -(Standard basis with 1 element w.r.t. neglex([x, y]), [-1; 0]) - -julia> gens(I)*T == gens(G) -true -``` -""" -function standard_basis_with_transformation_matrix(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) - complete_reduction && @assert is_global(ordering) - G, m = _compute_standard_basis_with_transform(I.gens, ordering, complete_reduction) - G.isGB = true - I.gb[ordering] = G - return G, m -end - -@doc raw""" - groebner_basis_with_transformation_matrix(I::MPolyIdeal; - ordering::MonomialOrdering = default_ordering(base_ring(I)), - complete_reduction::Bool=false) - -Return a pair `G`, `T`, say, where `G` is a Gröbner basis of `I` with respect to `ordering`, and `T` -is a transformation matrix from `gens(I)` to `G`. That is, `gens(I)*T == G`. - -!!! note - The returned Gröbner basis is reduced if `complete_reduction = true`. - -# Examples -```jldoctest -julia> R,(x,y) = polynomial_ring(QQ,[:x,:y]); - -julia> I = ideal([x*y^2-1,x^3+y^2+x*y]); - -julia> G, T = groebner_basis_with_transformation_matrix(I) -(Gröbner basis with 3 elements w.r.t. degrevlex([x, y]), [1 0 -x^2-y; 0 1 y^2]) - -julia> gens(I)*T == gens(G) -true -``` -""" -function groebner_basis_with_transformation_matrix(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) - is_global(ordering) || error("Ordering must be global") - return standard_basis_with_transformation_matrix(I, ordering=ordering, complete_reduction=complete_reduction) -end - -# syzygies ####################################################### -# See src/Modules/UngradedModules/FreeMod.jl for the implementation. - -# leading ideal ####################################################### -@doc raw""" - leading_ideal(G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(G[1]))) - where T <: MPolyRingElem - -Return the leading ideal of `G` with respect to `ordering`. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> L = leading_ideal([x*y^2-3*x, x^3-14*y^5], ordering=degrevlex(R)) -Ideal generated by - x*y^2 - y^5 - -julia> L = leading_ideal([x*y^2-3*x, x^3-14*y^5], ordering=lex(R)) -Ideal generated by - x*y^2 - x^3 -``` -""" -function leading_ideal(G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(G[1]))) where { T <: MPolyRingElem } - return ideal(parent(G[1]), [leading_monomial(f; ordering = ordering) for f in G]) -end - -function leading_ideal(I::IdealGens{T}) where { T <: MPolyRingElem } - return ideal(base_ring(I), [leading_monomial(f; ordering = I.ord) for f in I]) -end - -function leading_ideal(I::IdealGens{T}, ordering::MonomialOrdering) where T <: MPolyRingElem - return ideal(base_ring(I), [leading_monomial(f; ordering = ordering) for f in I]) -end - - -@doc raw""" - leading_ideal(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) - -Return the leading ideal of `I` with respect to `ordering`. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> I = ideal(R,[x*y^2-3*x, x^3-14*y^5]) -Ideal generated by - x*y^2 - 3*x - x^3 - 14*y^5 - -julia> L = leading_ideal(I, ordering=degrevlex(R)) -Ideal generated by - x*y^2 - x^4 - y^5 - -julia> L = leading_ideal(I, ordering=lex(R)) -Ideal generated by - y^7 - x*y^2 - x^3 -``` -""" -function leading_ideal(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) - G = standard_basis(I, ordering=ordering) - return ideal(base_ring(I), [leading_monomial(g; ordering = ordering) for g in G]) -end - -@doc raw""" - reduce(I::IdealGens, J::IdealGens; - ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - -Return a `Vector` whose elements are the underlying elements of `I` -reduced by the underlying generators of `J` w.r.t. the monomial -ordering `ordering`. `J` need not be a Gröbner basis. The returned -`Vector` will have the same number of elements as `I`, even if they -are zero. - -# Examples -```jldoctest -julia> R, (x, y, z) = polynomial_ring(GF(11), [:x, :y, :z]); - -julia> I = ideal(R, [x^2, x*y - y^2]); - -julia> J = ideal(R, [y^3]) -Ideal generated by - y^3 - -julia> reduce(J.gens, I.gens) -1-element Vector{FqMPolyRingElem}: - y^3 - -julia> reduce(J.gens, groebner_basis(I)) -1-element Vector{FqMPolyRingElem}: - 0 - -julia> reduce(y^3, [x^2, x*y-y^3]) -x*y - -julia> reduce(y^3, [x^2, x*y-y^3], ordering=lex(R)) -y^3 - -julia> reduce([y^3], [x^2, x*y-y^3], ordering=lex(R)) -1-element Vector{FqMPolyRingElem}: - y^3 -``` -""" -function reduce(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - @assert base_ring(J) == base_ring(I) - Is = singular_generators(I, ordering) - Js = singular_generators(J, ordering) - res = reduce(Is, Js, complete_reduction=complete_reduction) - return [J.gens.Ox(x) for x = gens(res)] -end - -@doc raw""" - reduce(g::T, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(g)), complete_reduction::Bool = false) where T <: MPolyRingElem - -If `ordering` is global, return the remainder in a standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. -Otherwise, return the remainder in a *weak* standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. - - reduce(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem - -Return a `Vector` which contains, for each element `g` of `G`, a remainder as above. - -!!! note - The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. - -!!! note - The reduction strategy behind the `reduce` function and the reduction strategy behind the functions - `reduce_with_quotients` and `reduce_with_quotients_and_unit` differ. As a consequence, the computed - remainders may differ. - -# Examples -```jldoctest -julia> R, (z, y, x) = polynomial_ring(QQ, [:z, :y, :x]); - -julia> f1 = y-x^2; f2 = z-x^3; - -julia> g = x^3*y-3*y^2*z^2+x*y*z; - -julia> reduce(g, [f1, f2], ordering = lex(R)) --3*x^10 + x^6 + x^5 -``` - -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); - -julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; - -julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; - -julia> reduce(g, [f1, f2, f3], ordering = lex(R)) -x^5 + x^3*y + x^2*y^2*z^2 + z^6 - -julia> reduce(g, [f1,f2, f3], ordering = lex(R), complete_reduction = true) -x^5 - x^3 + y^6 + z^6 -``` - -""" -function reduce(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - isempty(F) && return f - J = IdealGens(parent(F[1]), F, ordering) - return reduce(f, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - isempty(G) && return F - J = IdealGens(parent(G[1]), G, ordering) - return reduce(F, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - isempty(F) && return f - @assert parent(f) == base_ring(F) - R = parent(f) - I = IdealGens(R, [f], ordering) - redv = reduce(I, F, ordering=ordering, complete_reduction=complete_reduction) - return redv[1] -end - -function reduce(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - (isempty(F) || isempty(G)) && return F - @assert parent(F[1]) == base_ring(G) - R = parent(F[1]) - I = IdealGens(R, F, ordering) - return reduce(I, G, ordering=ordering, complete_reduction=complete_reduction) -end - -@doc raw""" - reduce_with_quotients_and_unit(g::T, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(parent(g)), complete_reduction::Bool = false) where T <: MPolyRingElem - -Return the unit, the quotients and the remainder in a weak standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. - - reduce_with_quotients_and_unit(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem - -Return a `Vector` which contains, for each element `g` of `G`, a unit, quotients, and a remainder as above. - -!!! note - The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. - -!!! note - The reduction strategy behind the `reduce` function and the reduction strategy behind the functions - `reduce_with_quotients` and `reduce_with_quotients_and_unit` differ. As a consequence, the computed - remainders may differ. - -# Examples -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); - -julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; - -julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; - -julia> u, Q, h =reduce_with_quotients_and_unit(g, [f1,f2, f3], ordering = lex(R)); - -julia> u -[1] - -julia> G = [g, x*y^3-3*x^2*y^2*z^2]; - -julia> U, Q, H = reduce_with_quotients_and_unit(G, [f1, f2, f3], ordering = negdegrevlex(R)); - -julia> U -[y + 1 0] -[ 0 y + 1] - -julia> Q -[ x^3 - x*y^2*z^2 + x*y + y^2*z^2 0 y*z^2 + z^2] -[x*y*z^2 + y^3*z - 3*y^2*z^2 - y*z -x^2*y*z - x^2*z + x*y + x 0] - -julia> H -2-element Vector{QQMPolyRingElem}: - 0 - 0 - -julia> U*G == Q*[f1, f2, f3]+H -true -``` -""" -function reduce_with_quotients_and_unit(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - if isempty(F) - return identity_matrix(parent(f), 1), zero_matrix(parent(f), 1, 0), f - end - J = IdealGens(parent(F[1]), F, ordering) - return reduce_with_quotients_and_unit(f, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce_with_quotients_and_unit(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - @assert !isempty(F) - if isempty(G) - return identity_matrix(parent(F[1]), length(F)), zero_matrix(parent(F[1]), length(F), 0), F - end - J = IdealGens(parent(G[1]), G, ordering) - return reduce_with_quotients_and_unit(F, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce_with_quotients_and_unit(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - if isempty(F) - return identity_matrix(parent(f), 1), zero_matrix(parent(f), 1, 0), f - end - @assert parent(f) == base_ring(F) - R = parent(f) - I = IdealGens(R, [f], ordering) - u, q, r = _reduce_with_quotients_and_unit(I, F, ordering, complete_reduction) - return u, q, r[1] -end - -function reduce_with_quotients_and_unit(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - @assert !isempty(F) - if isempty(G) - return identity_matrix(parent(F[1]), length(F)), zero_matrix(parent(F[1]), length(F), 0), F - end - @assert parent(F[1]) == base_ring(G) - R = parent(F[1]) - I = IdealGens(R, F, ordering) - return _reduce_with_quotients_and_unit(I, G, ordering, complete_reduction) -end - -@doc raw""" - reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens; - ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - -Return a `Tuple` consisting of a `Generic.MatSpaceElem` `M`, a -`Vector` `res` whose elements are the underlying elements of `I` -reduced by the underlying generators of `J` w.r.t. the monomial -ordering `ordering` and a diagonal matrix `units` such that `M * -gens(J) + res == units * gens(I)`. If `ordering` is global then -`units` will always be the identity matrix, see also -`reduce_with_quotients`. `J` need not be a Gröbner basis. `res` will -have the same number of elements as `I`, even if they are zero. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(GF(11), [:x, :y]); - -julia> I = ideal(R, [x]); - -julia> R, (x, y) = polynomial_ring(GF(11), [:x, :y]); - -julia> I = ideal(R, [x]); - -julia> J = ideal(R, [x+1]); - -julia> unit, M, res = reduce_with_quotients_and_unit(I.gens, J.gens, ordering = neglex(R)) -([x+1], [x], FqMPolyRingElem[0]) - -julia> M * gens(J) + res == unit * gens(I) -true - -julia> f = x^3*y^2-y^4-10 -x^3*y^2 + 10*y^4 + 1 - -julia> F = [x^2*y-y^3, x^3-y^4] -2-element Vector{FqMPolyRingElem}: - x^2*y + 10*y^3 - x^3 + 10*y^4 - -julia> reduce_with_quotients_and_unit(f, F) -([1], [x*y 10*x+1], x^4 + 10*x^3 + 1) - -julia> unit, M, res = reduce_with_quotients_and_unit(f, F, ordering=lex(R)) -([1], [x*y 0], x*y^4 + 10*y^4 + 1) - -julia> M * F + [res] == unit * [f] -true -``` -""" -function reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - return _reduce_with_quotients_and_unit(I, J, ordering, complete_reduction) -end - - -@doc raw""" - reduce_with_quotients(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - -Return a `Tuple` consisting of a `Generic.MatSpaceElem` `M` and a -`Vector` `res` whose elements are the underlying elements of `I` -reduced by the underlying generators of `J` w.r.t. the monomial -ordering `ordering` such that `M * gens(J) + res == gens(I)` if `ordering` is global. -If `ordering` is local then this equality holds after `gens(I)` has been multiplied -with an unknown diagonal matrix of units, see `reduce_with_quotients_and_unit` to -obtain this matrix. `J` need not be a Gröbner basis. `res` will have the same number -of elements as `I`, even if they are zero. - -# Examples -```jldoctest -julia> R, (x, y, z) = polynomial_ring(GF(11), [:x, :y, :z]); - -julia> J = ideal(R, [x^2, x*y - y^2]); - -julia> I = ideal(R, [x*y, y^3]); - -julia> gb = groebner_basis(J) -Gröbner basis with elements - 1 -> x*y + 10*y^2 - 2 -> x^2 - 3 -> y^3 -with respect to the ordering - degrevlex([x, y, z]) - -julia> M, res = reduce_with_quotients(I.gens, gb) -([1 0 0; 0 0 1], FqMPolyRingElem[y^2, 0]) - -julia> M * gens(gb) + res == gens(I) -true - -julia> f = x^3*y^2-y^4-10 -x^3*y^2 + 10*y^4 + 1 - -julia> F = [x^2*y-y^3, x^3-y^4] -2-element Vector{FqMPolyRingElem}: - x^2*y + 10*y^3 - x^3 + 10*y^4 - -julia> reduce_with_quotients_and_unit(f, F) -([1], [x*y 10*x+1], x^4 + 10*x^3 + 1) - -julia> unit, M, res = reduce_with_quotients_and_unit(f, F, ordering=lex(R)) -([1], [x*y 0], x*y^4 + 10*y^4 + 1) - -julia> M * F + [res] == unit * [f] -true -``` -""" -function reduce_with_quotients(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) - _, q, r = _reduce_with_quotients_and_unit(I, J, ordering, complete_reduction) - return q, r -end - -@doc raw""" - reduce_with_quotients(g::T, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(parent(g)), complete_reduction::Bool = false) where T <: MPolyRingElem - -If `ordering` is global, return the quotients and the remainder in a standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. -Otherwise, return the quotients and the remainder in a *weak* standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. - - reduce_with_quotients(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; - ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem - -Return a `Vector` which contains, for each element `g` of `G`, quotients and a remainder as above. - -!!! note - The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. - -# Examples - -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); - -julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; - -julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; - -julia> Q, h = reduce_with_quotients(g, [f1,f2, f3], ordering = lex(R)); - -julia> h -x^5 - x^3 + y^6 + z^6 - -julia> g == Q[1]*f1+Q[2]*f2+Q[3]*f3+h -true -``` -""" -function reduce_with_quotients(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - isempty(F) && return zero_matrix(parent(f), 1, 0), f - J = IdealGens(parent(F[1]), F, ordering) - return reduce_with_quotients(f, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce_with_quotients(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - @assert !isempty(F) - isempty(G) && return zero_matrix(parent(F[1]), length(F), 0), F - J = IdealGens(parent(G[1]), G, ordering) - return reduce_with_quotients(F, J; ordering=ordering, complete_reduction=complete_reduction) -end - -function reduce_with_quotients(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} - isempty(F) && return zero_matrix(parent(f), 1, 0), f - @assert parent(f) == parent(F[1]) - R = parent(f) - I = IdealGens(R, [f], ordering) - _, q, r = _reduce_with_quotients_and_unit(I, F, ordering, complete_reduction) - return q, r[1] -end - -function reduce_with_quotients(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} - @assert !isempty(F) - isempty(G) && return zero_matrix(parent(F[1]), length(F), 0), F - @assert parent(F[1]) == parent(G[1]) - R = parent(F[1]) - I = IdealGens(R, F, ordering) - _, q, r = _reduce_with_quotients_and_unit(I, G, ordering, complete_reduction) - return q, r -end - -function _reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens, ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = complete_reduction) - @assert base_ring(J) == base_ring(I) - sI = singular_generators(I, ordering) - sJ = singular_generators(J, ordering) - res = Singular.divrem2(sI, sJ, complete_reduction=complete_reduction) - return matrix(base_ring(I), res[3]), matrix(base_ring(I), res[1]), [J.gens.Ox(x) for x = gens(res[2])] -end - -@doc raw""" - normal_form(g::T, I::MPolyIdeal; - ordering::MonomialOrdering = default_ordering(base_ring(I))) where T <: MPolyRingElem - -Compute the normal form of `g` mod `I` with respect to `ordering`. - - normal_form(G::Vector{T}, I::MPolyIdeal; - ordering::MonomialOrdering = default_ordering(base_ring(I))) where T <: MPolyRingElem - -Return a `Vector` which contains for each element `g` of `G` a normal form as above. - -# Examples -```jldoctest -julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) -(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) - -julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) -Ideal generated by - b + c - 1 - 2*a*b + a*c + b - 1 - -julia> gens(groebner_basis(J)) -2-element Vector{QQMPolyRingElem}: - b + c - 1 - a*c - 2*a + c - -julia> normal_form(-1+c+b+a^3, J) -a^3 - -julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) -(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) - -julia> A = [-1+c+b+a^3,-1+b+c*a+2*a^3,5+c*b+c^2*a] -3-element Vector{QQMPolyRingElem}: - a^3 + b + c - 1 - 2*a^3 + a*c + b - 1 - a*c^2 + b*c + 5 - -julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) -Ideal generated by - b + c - 1 - 2*a*b + a*c + b - 1 - -julia> gens(groebner_basis(J)) -2-element Vector{QQMPolyRingElem}: - b + c - 1 - a*c - 2*a + c - -julia> normal_form(A, J) -3-element Vector{QQMPolyRingElem}: - a^3 - 2*a^3 + 2*a - 2*c - 4*a - 2*c^2 - c + 5 -``` -""" -function normal_form(f::T, J::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(J))) where { T <: MPolyRingElem } - res = normal_form([f], J, ordering = ordering) - - return res[1] -end - -function normal_form(A::Vector{T}, J::MPolyIdeal; ordering::MonomialOrdering=default_ordering(base_ring(J))) where { T <: MPolyRingElem } - @req is_exact_type(elem_type(base_ring(J))) "This functionality is only supported over exact fields." - if is_normal_form_f4_applicable(J, ordering) - res = _normal_form_f4(A, J) - else - res = _normal_form_singular(A, J, ordering) - end - - return res -end - -function is_normal_form_f4_applicable(I::MPolyIdeal, ordering::MonomialOrdering) - return (ordering == degrevlex(base_ring(I)) && !is_graded(base_ring(I)) - && ((coefficient_ring(I) isa FqField - && absolute_degree(coefficient_ring(I)) == 1 - && characteristic(coefficient_ring(I)) < 2^31))) -end - -@doc raw""" - _normal_form_f4(A::Vector{T}, J::MPolyIdeal) where { T <: MPolyRingElem } - -**Note**: Internal function, subject to change, do not use. - -Compute the normal form of the elements of `A` w.r.t. a -Gröbner basis of `J` and the monomial ordering `degrevlex` using the F4 Algorithm from AlgebraicSolving. - -CAVEAT: This computation needs a Gröbner basis of `J` and the monomial ordering -`ordering. If this Gröbner basis is not available, one is computed automatically. -This may take some time. This function only works in polynomial rings over prime fields -with the degree reverse lexicographical ordering. - -# Examples -```jldoctest -julia> R,(a,b,c) = polynomial_ring(GF(65521),[:a,:b,:c]) -(Multivariate polynomial ring in 3 variables over GF(65521), FqMPolyRingElem[a, b, c]) - -julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) -Ideal generated by - b + c + 65520 - 2*a*b + a*c + b + 65520 - -julia> A = [-1+c+b+a^3, -1+b+c*a+2*a^3, 5+c*b+c^2*a] -3-element Vector{FqMPolyRingElem}: - a^3 + b + c + 65520 - 2*a^3 + a*c + b + 65520 - a*c^2 + b*c + 5 - -julia> Oscar._normal_form_f4(A, J) -3-element Vector{FqMPolyRingElem}: - a^3 - 2*a^3 + 2*a + 65519*c - 4*a + 65519*c^2 + 65520*c + 5 -``` -""" -function _normal_form_f4(A::Vector{T}, J::MPolyIdeal) where { T <: MPolyRingElem } - if !haskey(J.gb, degrevlex(base_ring(J))) - groebner_basis_f4(J, complete_reduction = true) - end - - AJ = AlgebraicSolving.Ideal(J.gens.O) - AJ.gb[0] = oscar_groebner_generators(J, degrevlex(base_ring(J)), true) - - return AlgebraicSolving.normal_form(A, AJ) -end - -@doc raw""" - _normal_form_singular(A::Vector{T}, J::MPolyIdeal, ordering::MonomialOrdering) where { T <: MPolyRingElem } - -**Note**: Internal function, subject to change, do not use. - -Compute the normal form of the elements of `A` w.r.t. a -Gröbner basis of `J` and the monomial ordering `ordering` using Singular. - -CAVEAT: This computation needs a Gröbner basis of `J` and the monomial ordering -`ordering. If this Gröbner basis is not available, one is computed automatically. -This may take some time. - -# Examples -```jldoctest -julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) -(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) - -julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) -Ideal generated by - b + c - 1 - 2*a*b + a*c + b - 1 - -julia> gens(groebner_basis(J)) -2-element Vector{QQMPolyRingElem}: - b + c - 1 - a*c - 2*a + c - -julia> A = [-1+c+b+a^3, -1+b+c*a+2*a^3, 5+c*b+c^2*a] -3-element Vector{QQMPolyRingElem}: - a^3 + b + c - 1 - 2*a^3 + a*c + b - 1 - a*c^2 + b*c + 5 - -julia> Oscar._normal_form_singular(A, J, default_ordering(base_ring(J))) -3-element Vector{QQMPolyRingElem}: - a^3 - 2*a^3 + 2*a - 2*c - 4*a - 2*c^2 - c + 5 -``` -""" -function _normal_form_singular(A::Vector{T}, J::MPolyIdeal, ordering::MonomialOrdering) where { T <: MPolyRingElem } - GS = singular_groebner_generators(J, ordering) - SR = base_ring(GS) - tmp = map(SR, A) - IS = Singular.Ideal(SR, tmp) - K = reduce(IS, GS) - OR = base_ring(J) - return map(OR, gens(K)) -end - -@doc raw""" - is_standard_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) - -Tests if a given IdealGens `F` is a standard basis w.r.t. the given monomial ordering `ordering`. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> I = ideal(R,[x^2+y,x*y-y]) -Ideal generated by - x^2 + y - x*y - y - -julia> is_standard_basis(I.gens, ordering=neglex(R)) -false - -julia> standard_basis(I, ordering=neglex(R)) -Standard basis with elements - 1 -> y - 2 -> x^2 -with respect to the ordering - neglex([x, y]) - -julia> is_standard_basis(I.gb[neglex(R)], ordering=neglex(R)) -true -``` -""" -function is_standard_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) - @req is_exact_type(elem_type(base_ring(F))) "This functionality is only supported over exact fields." - if F.isGB && F.ord == ordering - return true - else - # Try to reduce all possible s-polynomials, i.e. Buchberger's criterion - R = base_ring(F) - for i in 1:length(F) - lt_i = leading_term(F[i], ordering=ordering) - for j in i+1:length(F) - lt_j = leading_term(F[j], ordering=ordering) - lcm_ij = lcm(lt_i, lt_j) - sp_ij = div(lcm_ij, lt_i) * F[i] - div(lcm_ij, lt_j) * F[j] - if reduce(IdealGens([sp_ij], ordering), F, ordering=ordering) != [R(0)] - return false - end - end - end - F.isGB = true - F.ord = ordering - return true - end -end - -@doc raw""" - is_groebner_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) - -Tests if a given IdealGens `F` is a Gröbner basis w.r.t. the given monomial ordering `ordering`. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> I = ideal(R,[x^2+y,x*y-y]) -Ideal generated by - x^2 + y - x*y - y - -julia> is_groebner_basis(I.gens, ordering=lex(R)) -false - -julia> groebner_basis(I, ordering=lex(R)) -Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y -with respect to the ordering - lex([x, y]) - -julia> is_groebner_basis(I.gb[lex(R)], ordering=lex(R)) -true -``` -""" -function is_groebner_basis(F::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(F))) - is_global(ordering) || error("Ordering must be global") - return is_standard_basis(F, ordering=ordering) -end - -@doc raw""" - _fglm(G::IdealGens; ordering::MonomialOrdering) - -Converts a Gröbner basis `G` w.r.t. a given global monomial ordering for `` -to a Gröbner basis `H` w.r.t. another monomial ordering `ordering` for ``. - -**NOTE**: `_fglm` assumes that `G` is a reduced Gröbner basis (i.e. w.r.t. a global monomial ordering) and that `ordering` is a global monomial ordering. - -# Examples -```jldoctest -julia> R, (x1, x2, x3, x4) = polynomial_ring(GF(101), [:x1, :x2, :x3, :x4]) -(Multivariate polynomial ring in 4 variables over GF(101), FqMPolyRingElem[x1, x2, x3, x4]) - -julia> J = ideal(R, [x1+2*x2+2*x3+2*x4-1, - x1^2+2*x2^2+2*x3^2+2*x4^2-x1, - 2*x1*x2+2*x2*x3+2*x3*x4-x2, - x2^2+2*x1*x3+2*x2*x4-x3 - ]) -Ideal generated by - x1 + 2*x2 + 2*x3 + 2*x4 + 100 - x1^2 + 100*x1 + 2*x2^2 + 2*x3^2 + 2*x4^2 - 2*x1*x2 + 2*x2*x3 + 100*x2 + 2*x3*x4 - 2*x1*x3 + x2^2 + 2*x2*x4 + 100*x3 - -julia> groebner_basis(J, ordering=degrevlex(R), complete_reduction=true) -Gröbner basis with elements - 1 -> x1 + 2*x2 + 2*x3 + 2*x4 + 100 - 2 -> x3^2 + 2*x2*x4 + 19*x3*x4 + 76*x4^2 + 72*x2 + 86*x3 + 42*x4 - 3 -> x2*x3 + 99*x2*x4 + 40*x3*x4 + 11*x4^2 + 65*x2 + 58*x3 + 30*x4 - 4 -> x2^2 + 2*x2*x4 + 30*x3*x4 + 45*x4^2 + 43*x2 + 72*x3 + 86*x4 - 5 -> x3*x4^2 + 46*x4^3 + 28*x2*x4 + 16*x3*x4 + 7*x4^2 + 58*x2 + 63*x3 + 15*x4 - 6 -> x2*x4^2 + 67*x4^3 + 56*x2*x4 + 58*x3*x4 + 45*x4^2 + 14*x2 + 86*x3 - 7 -> x4^4 + 65*x4^3 + 26*x2*x4 + 47*x3*x4 + 71*x4^2 + 37*x2 + 79*x3 + 100*x4 -with respect to the ordering - degrevlex([x1, x2, x3, x4]) - -julia> Oscar._fglm(J.gb[degrevlex(R)], lex(R)) -Gröbner basis with elements - 1 -> x4^8 + 36*x4^7 + 95*x4^6 + 39*x4^5 + 74*x4^4 + 7*x4^3 + 45*x4^2 + 98*x4 - 2 -> x3 + 53*x4^7 + 93*x4^6 + 74*x4^5 + 26*x4^4 + 56*x4^3 + 15*x4^2 + 88*x4 - 3 -> x2 + 25*x4^7 + 57*x4^6 + 13*x4^5 + 16*x4^4 + 78*x4^3 + 31*x4^2 + 16*x4 - 4 -> x1 + 46*x4^7 + 3*x4^6 + 28*x4^5 + 17*x4^4 + 35*x4^3 + 9*x4^2 + 97*x4 + 100 -with respect to the ordering - lex([x1, x2, x3, x4]) -``` -""" -function _fglm(G::IdealGens, ordering::MonomialOrdering) - (G.isGB == true && G.isReduced == true) || error("Input must be a reduced Gröbner basis.") - Singular.dimension(singular_generators(G)) == 0 || error("Dimension of corresponding ideal must be zero.") - SR_destination, = Singular.polynomial_ring(base_ring(G.Sx), symbols(G.Sx); ordering = singular(ordering)) - - ptr = Singular.libSingular.fglmzero(G.S.ptr, G.Sx.ptr, SR_destination.ptr) - return IdealGens(base_ring(G), Singular.sideal{Singular.spoly}(SR_destination, ptr, true)) -end - -@doc raw""" - fglm(I::MPolyIdeal; start_ordering::MonomialOrdering = default_ordering(base_ring(I)), - destination_ordering::MonomialOrdering) - -Given a **zero-dimensional** ideal `I`, return the reduced Gröbner basis of `I` with respect to `destination_ordering`. - -!!! note - Both `start_ordering` and `destination_ordering` must be global and the base ring of `I` must be a polynomial ring over a field. - -!!! note - The function implements the Gröbner basis conversion algorithm by **F**augère, **G**ianni, **L**azard, and **M**ora. See [FGLM93](@cite) for more information. - -# Examples -```jldoctest -julia> R, (a, b, c, d, e) = polynomial_ring(QQ, [:a, :b, :c, :d, :e]); - -julia> f1 = a+b+c+d+e; - -julia> f2 = a*b+b*c+c*d+a*e+d*e; - -julia> f3 = a*b*c+b*c*d+a*b*e+a*d*e+c*d*e; - -julia> f4 = b*c*d+a*b*c*e+a*b*d*e+a*c*d*e+b*c*d*e; - -julia> f5 = a*b*c*d*e-1; - -julia> I = ideal(R, [f1, f2, f3, f4, f5]); - -julia> G = fglm(I, destination_ordering = lex(R)); - -julia> length(G) -8 - -julia> total_degree(G[8]) -60 - -julia> leading_coefficient(G[8]) -83369589588385815165248207597941242098312973356252482872580035860533111990678631297423089011608753348453253671406641805924218003925165995322989635503951507226650115539638517111445927746874479234 -``` -""" -function fglm(I::MPolyIdeal; start_ordering::MonomialOrdering = default_ordering(base_ring(I)), destination_ordering::MonomialOrdering) - isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The FGLM algorithm requires a coefficient ring that is a field.") - (is_global(start_ordering) && is_global(destination_ordering)) || error("Start and destination orderings must be global.") - haskey(I.gb, destination_ordering) && return I.gb[destination_ordering] - if !haskey(I.gb, start_ordering) - standard_basis(I, ordering=start_ordering, complete_reduction=true) - elseif I.gb[start_ordering].isReduced == false - I.gb[start_ordering] = _compute_standard_basis(I.gb[start_ordering], start_ordering, true) - end - - I.gb[destination_ordering] = _fglm(I.gb[start_ordering], destination_ordering) - - return I.gb[destination_ordering] -end - -@doc raw""" - _compute_groebner_basis_using_fglm(I::MPolyIdeal, destination_ordering::MonomialOrdering) - -Computes a reduced Gröbner basis for `I` w.r.t. `destination_ordering` using the FGLM algorithm. - -**Note**: Internal function, subject to change, do not use. - -# Examples -```jldoctest -julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) -(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) - -julia> I = ideal(R,[x^2+y,x*y-y]) -Ideal generated by - x^2 + y - x*y - y - -julia> Oscar._compute_groebner_basis_using_fglm(I, lex(R)) -Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y -with respect to the ordering - lex([x, y]) - -julia> I.gb[lex(R)] -Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y -with respect to the ordering - lex([x, y]) - -julia> I.gb[degrevlex(R)] -Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y -with respect to the ordering - degrevlex([x, y]) -``` -""" -function _compute_groebner_basis_using_fglm(I::MPolyIdeal, - destination_ordering::MonomialOrdering) - isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The FGLM algorithm requires a coefficient ring that is a field.") - haskey(I.gb, destination_ordering) && return I.gb[destination_ordering] - is_global(destination_ordering) || error("Destination ordering must be global.") - G = groebner_assure(I, true, true) - start_ordering = G.ord - dim(I) == 0 || error("Dimension of ideal must be zero.") - I.gb[destination_ordering] = _fglm(G, destination_ordering) -end - -@doc raw""" - groebner_basis_hilbert_driven(I::MPolyIdeal{P}; destination_ordering::MonomialOrdering, - complete_reduction::Bool = false, - weights::Vector{Int} = ones(Int, ngens(base_ring(I))), - hilbert_numerator::Union{Nothing, ZZPolyRingElem} = nothing) - where {P <: MPolyRingElem} - -Return a Gröbner basis of `I` with respect to `destination_ordering`. - -!!! note - The function implements a version of the Hilbert driven Gröbner basis algorithm. - See the corresponding section of the OSCAR documentation for some details. - -!!! note - All weights must be positive. If no weight vector is entered by the user, all weights - are set to 1. An error is thrown if the generators of `I` are not homogeneous with - respect to the corresponding (weighted) degree. - -!!! note - If $R$ denotes the parent ring of $I$, and $p, q\in\mathbb Z[t]$ are polynomials - such that $p/q$ represents the Hilbert series of $R/I$ as a rational function with - denominator $q = (1-t^{w_1})\cdots (1-t^{w_n}),$ where $n$ is the number of variables - of $R$, and $w_1, \dots, w_n$ are the assigned weights, then `hilbert_numerator` is - meant to be $p$. If this numerator is not entered by the user, it will be computed - internally. - -# Examples -```jldoctest -julia> R, (a, b, c, d, e, f, g) = polynomial_ring(QQ, [:a, :b, :c, :d, :e, :f, :g]); - -julia> V = [-3*a^2+2*f*b+3*f*d, (3*g*b+3*g*e)*a-3*f*c*b, - -3*g^2*a^2-c*b^2*a-g^2*f*e-g^4, e*a-f*b-d*c]; - -julia> I = ideal(R, V); - -julia> o = degrevlex([a, b, c])*degrevlex([d, e, f, g]); - -julia> G = groebner_basis_hilbert_driven(I, destination_ordering = o); - -julia> length(G) -296 - -julia> total_degree(G[49]) -30 -``` - -```jldoctest -julia> R, (x, y, z) = polynomial_ring(GF(32003), [:x, :y, :z]); - -julia> f1 = x^2*y+169*y^21+151*x*y*z^10; - -julia> f2 = 6*x^2*y^4+x*z^14+3*z^24; - -julia> f3 = 11*x^3+5*x*y^10*z^10+2*y^20*z^10+y^10*z^20; - -julia> I = ideal(R, [f1, f2,f3]); - -julia> W = [10, 1, 1]; - -julia> GB = groebner_basis_hilbert_driven(I, destination_ordering = lex(R), weights = W); - -julia> length(GB) -40 -``` - -```jldoctest -julia> R, (x, y, z) = polynomial_ring(GF(32003), [:x, :y, :z]); - -julia> f1 = x^2*y+169*y^21+151*x*y*z^10; - -julia> f2 = 6*x^2*y^4+x*z^14+3*z^24; - -julia> f3 = 11*x^3+5*x*y^10*z^10+2*y^20*z^10+y^10*z^20; - -julia> I = ideal(R, [f1, f2,f3]); - -julia> W = [10, 1, 1]; - -julia> S, t = polynomial_ring(ZZ, :t) -(Univariate polynomial ring in t over ZZ, t) - -julia> hn = -t^75 + t^54 + t^51 + t^45 - t^30 - t^24 - t^21 + 1 --t^75 + t^54 + t^51 + t^45 - t^30 - t^24 - t^21 + 1 - -julia> GB = groebner_basis_hilbert_driven(I, destination_ordering = lex(R), weights = W, hilbert_numerator = hn); - -julia> length(GB) -40 -``` -""" -function groebner_basis_hilbert_driven(I::MPolyIdeal{P}; - destination_ordering::MonomialOrdering, - complete_reduction::Bool = false, - weights::Vector{Int} = ones(Int, ngens(base_ring(I))), - hilbert_numerator::Union{Nothing, ZZPolyRingElem} = nothing) where {P <: MPolyRingElem} - - all(f -> _is_homogeneous(f, weights), gens(I)) || error("I must be given by generators homogeneous with respect to the given weights.") - isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The underlying coefficient ring of I must be a field.") - ordering = destination_ordering - is_global(ordering) || error("Destination ordering must be global.") - haskey(I.gb, ordering) && return I.gb[ordering] - if isnothing(hilbert_numerator) - if isempty(I.gb) - J = iszero(characteristic(base_ring(I))) ? _mod_rand_prime(I) : I - G = groebner_assure(J, wdegrevlex(base_ring(J), weights)) - else - G = groebner_assure(I) - end - - if characteristic(base_ring(I)) > 0 && ordering == wdegrevlex(base_ring(I), weights) - return G - end - singular_assure(G) - h = Singular.hilbert_series(G.S, weights) - - else - # Quoting from the documentation of Singular.hilbert_series: - # The coefficient vector is returned as a `Vector{Int32}`, and the last element is not actually part of the coefficients of Q(t). - # what? - h = (Int32).([coeff(hilbert_numerator, i) for i in 0:degree(hilbert_numerator)+1]) - end - - singular_I_gens = singular_generators(I.gens, ordering) - singular_ring = base_ring(singular_I_gens) - J = Singular.Ideal(singular_ring, gens(singular_I_gens)...) - i = Singular.std_hilbert(J, h, (Int32).(weights), - complete_reduction = complete_reduction) - GB = IdealGens(I.gens.Ox, i, complete_reduction) - GB.isGB = true - GB.ord = ordering - if isdefined(GB, :S) - GB.S.isGB = true - end - I.gb[destination_ordering] = GB - return GB -end - -# Helper functions for groebner_basis_with_hilbert - -function _extract_weights(T::MPolyDecRing) - if !is_z_graded(T) - error("Ring must be graded by the Integers.") - end - return [Int(first(gr_elem.coeff)) for gr_elem in T.d] -end - -function _extend_mon_order(ordering::MonomialOrdering, - homogenized_ring::MPolyDecRing) - - nvars = ngens(ordering.R) - m = canonical_matrix(ordering) - m_hom = similar(m, nvars + 1, nvars + 1) - m_hom[1, :] = ones(Int, nvars + 1) - m_hom[2:end, 2:end] = m - return matrix_ordering(homogenized_ring, m_hom) -end - -function _mod_rand_prime(I::MPolyIdeal) - p = 32771 - while true - p = Hecke.next_prime(p) - - base_field = GF(p) - ModP, _ = polynomial_ring(base_field, ngens(base_ring(I)); cached = false) - I_mod_p_gens = - try - [map_coefficients(base_field, f; parent=ModP) for f in gens(I)] - catch e - # this precise error is thrown if the chosen prime p divides one - # of the denominators of the coefficients of the generators of I. - # In this case we simply choose the next prime and try again. - if e == ErrorException("Unable to coerce") - continue - else - rethrow(e) - end - end - return ideal(ModP, I_mod_p_gens) - end -end - -# check homogeneity w.r.t. some weights - -function _is_homogeneous(f::MPolyRingElem, weights::Vector{Int}) - w = sum(weights .* first(exponents(f))) - all(sum(weights .* e) == w for e in exponents(f)) -end - - -# check homogeneity w.r.t. total degree -function _is_homogeneous(f::MPolyRingElem) - leadexpv,tailexpvs = Iterators.peel(AbstractAlgebra.exponent_vectors(f)) - d = sum(leadexpv) - for tailexpv in tailexpvs - if d!=sum(tailexpv) - return false - end - end - return true -end - -# compute weights such that F is a homogeneous system w.r.t. these weights -function _find_weights(F::Vector{P}) where {P <: MPolyRingElem} - - if all(_is_homogeneous, F) - return ones(Int, ngens(parent(F[1]))) - end - - nrows = sum((length).(F)) - length(F) - ncols = ngens(parent(first(F))) - - exp_diffs = permutedims(reduce(hcat, [e[i] - e[1] for e in - (collect).((exponents).(F)) - for i in 2:length(e)])) - K = kernel(matrix(QQ, nrows, ncols, exp_diffs); side = :right) - isempty(K) && return zeros(Int, ncols) - # Here we try to find a vector with strictly positive entries in K - # this method to find such a vector is taken from - # https://mathoverflow.net/questions/363181/intersection-of-a-vector-subspace-with-a-cone - Pol = polyhedron(-K, zeros(Int, ncols)) - !is_feasible(Pol) && return zeros(Int, ncols) - pos_vec = zeros(Int, ncols) - for i in 1:ncols - ei = [j == i ? one(QQ) : zero(QQ) for j in 1:ncols] - obj_func = ei * K - L = linear_program(Pol, obj_func) - m, v = solve_lp(L) - if isnothing(v) - Pol_new = intersect(Pol, polyhedron(ei*K, [1])) - L = linear_program(Pol_new, obj_func) - v = optimal_vertex(L) - end - pos_vec += K*(v.p) - end - ret = (Int).(lcm((denominator).(pos_vec)) .* pos_vec) - ret = (x -> div(x, gcd(ret))).(ret) - # assure that the weights fit in Int32 for singular - return all(ret .< 2^32) ? ret : zeros(Int,ncols) -end - -# modular gröbner basis techniques using Singular -@doc raw""" - groebner_basis_modular(I::MPolyIdeal{QQMPolyRingElem}; ordering::MonomialOrdering = default_ordering(base_ring(I)), certify::Bool = false) - -Compute the reduced Gröbner basis of `I` w.r.t. `ordering` using a -multi-modular strategy. - -!!! note - This function is probabilistic and returns a correct result - only with high probability. -```jldoctest -julia> R, (x, y, z) = polynomial_ring(QQ, [:x,:y,:z]); - -julia> I = ideal(R, [x^2+1209, x*y + 3279*y^2]) -Ideal generated by - x^2 + 1209 - x*y + 3279*y^2 - -julia> groebner_basis_modular(I) -Gröbner basis with elements - 1 -> y^3 + 403//3583947*y - 2 -> x^2 + 1209 - 3 -> x*y + 3279*y^2 -with respect to the ordering - degrevlex([x, y, z]) -``` -""" -function groebner_basis_modular(I::MPolyIdeal{QQMPolyRingElem}; ordering::MonomialOrdering = default_ordering(base_ring(I)), - certify::Bool = false) - - # small function to get a canonically sorted reduced gb - sorted_gb = idl -> begin - R = base_ring(idl) - gb = gens(groebner_basis(idl, ordering = ordering, - complete_reduction = true)) - sort!(gb; by = leading_monomial, - lt = (m1, m2) -> cmp(MonomialOrdering(R, ordering.o), m1, m2) > 0) - end - - if haskey(I.gb, ordering) - return I.gb[ordering] - end - - primes = Hecke.PrimesSet(rand(2^15:2^16), -1) - - p = iterate(primes)[1] - Qt = base_ring(I) - Zt = polynomial_ring(ZZ, [string(s) for s = symbols(Qt)]; cached = false)[1] - - Rt, t = polynomial_ring(GF(p), [string(s) for s = symbols(Qt)]; cached = false) - std_basis_mod_p_lifted = map(sorted_gb(ideal(Rt, gens(I)))) do x - map_coefficients(z -> lift(ZZ, z), x, parent = Zt) - end - std_basis_crt_previous = std_basis_mod_p_lifted - - n_stable_primes = 0 - d = ZZRingElem(p) - unlucky_primes_in_a_row = 0 - done = false - while !done - while n_stable_primes < 2 - p = iterate(primes, p)[1] - Rt, t = polynomial_ring(GF(p), [string(s) for s = symbols(Qt)]; cached = false) - std_basis_mod_p_lifted = map(sorted_gb(ideal(Rt, gens(I)))) do x - map_coefficients(z -> lift(ZZ, z), x, parent = Zt) - end - - # test for unlucky prime - if any(((i, p), ) -> leading_monomial(p) != leading_monomial(std_basis_crt_previous[i]), - enumerate(std_basis_mod_p_lifted)) - unlucky_primes_in_a_row += 1 - # if we get unlucky twice in a row we assume that - # we started with an unlucky prime - if unlucky_primes_in_a_row == 2 - std_basis_crt_previous = std_basis_mod_p_lifted - end - continue - end - unlucky_primes_in_a_row = 0 - - is_stable = true - for (i, f) in enumerate(std_basis_mod_p_lifted) - if !iszero(f - std_basis_crt_previous[i]) - std_basis_crt_previous[i], _ = induce_crt(std_basis_crt_previous[i], d, f, ZZRingElem(p), true) - stable = false - end - end - if is_stable - n_stable_primes += 1 - end - d *= ZZRingElem(p) - end - final_gb = QQMPolyRingElem[induce_rational_reconstruction(f, d, parent = base_ring(I)) for f in std_basis_crt_previous] - - I.gb[ordering] = IdealGens(final_gb, ordering) - if certify - done = _certify_modular_groebner_basis(I, ordering) - else - done = true - end - end - I.gb[ordering].isGB = true - return I.gb[ordering] -end - -function induce_rational_reconstruction(f::ZZMPolyRingElem, d::ZZRingElem; parent = 1) - g = MPolyBuildCtx(parent) - for (c, v) in zip(AbstractAlgebra.coefficients(f), AbstractAlgebra.exponent_vectors(f)) - fl, r, s = Hecke.rational_reconstruction(c, d) - fl ? push_term!(g, r//s, v) : push_term!(g, c, v) - end - return finish(g) -end - -function _certify_modular_groebner_basis(I::MPolyIdeal, ordering::MonomialOrdering) - @req haskey(I.gb, ordering) "There exists no standard basis w.r.t. the given ordering." - ctr = 0 - singular_generators(I.gb[ordering]) - SR = I.gb[ordering].gens.Sx - SG = I.gb[ordering].gens.S - - #= test if I is included in =# - for f in I.gens - if Singular.reduce(SR(f), SG) != 0 - break - end - ctr += 1 - end - if ctr != ngens(I) - return false - end - - #= test if G is a standard basis of w.r.t. ordering =# - return is_standard_basis(I.gb[ordering], ordering=ordering) -end diff --git a/src/Rings/groebner/f4.jl b/src/Rings/groebner/f4.jl new file mode 100644 index 000000000000..4e889a5425ac --- /dev/null +++ b/src/Rings/groebner/f4.jl @@ -0,0 +1,88 @@ +function is_f4_applicable(I::MPolyIdeal, ordering::MonomialOrdering) + return (ordering == degrevlex(base_ring(I)) && !is_graded(base_ring(I)) + && ((coefficient_ring(I) isa FqField + && absolute_degree(coefficient_ring(I)) == 1 + && characteristic(coefficient_ring(I)) < 2^31) + || base_ring(I) isa QQMPolyRing)) +end + +@doc raw""" + groebner_basis_f4(I::MPolyIdeal, ) + +Compute a Gröbner basis of `I` with respect to `degrevlex` using Faugère's F4 algorithm. +See [Fau99](@cite) for more information. + +!!! note + At current state only prime fields of characteristic `0 < p < 2^{31}` and the rationals are supported. + +# Possible keyword arguments +- `initial_hts::Int=17`: initial hash table size `log_2`. +- `nr_thrds::Int=1`: number of threads for parallel linear algebra. +- `max_nr_pairs::Int=0`: maximal number of pairs per matrix, only bounded by minimal degree if `0`. +- `la_option::Int=2`: linear algebra option: exact sparse-dense (`1`), exact sparse (`2`, default), probabilistic sparse-dense (`42`), probabilistic sparse(`44`). +- `eliminate::Int=0`: size of first block of variables to be eliminated. +- `complete_reduction::Bool=true`: compute a reduced Gröbner basis for `I` +- `normalize::Bool=true`: normalizes elements in computed Gröbner basis for `I` +- `truncate_lifting::Int=0`: degree up to which the elements of the Gröbner basis are lifted to `QQ`, `0` for complete lifting +- `info_level::Int=0`: info level printout: off (`0`, default), summary (`1`), detailed (`2`). + +# Examples +```jldoctest +julia> R,(x,y,z) = polynomial_ring(GF(101), [:x,:y,:z]) +(Multivariate polynomial ring in 3 variables over GF(101), FqMPolyRingElem[x, y, z]) + +julia> I = ideal(R, [x+2*y+2*z-1, x^2+2*y^2+2*z^2-x, 2*x*y+2*y*z-y]) +Ideal generated by + x + 2*y + 2*z + 100 + x^2 + 100*x + 2*y^2 + 2*z^2 + 2*x*y + 2*y*z + 100*y + +julia> groebner_basis_f4(I) +Gröbner basis with elements + 1 -> x + 2*y + 2*z + 100 + 2 -> y*z + 82*z^2 + 10*y + 40*z + 3 -> y^2 + 60*z^2 + 20*y + 81*z + 4 -> z^3 + 28*z^2 + 64*y + 13*z +with respect to the ordering + degrevlex([x, y, z]) +``` +""" +function groebner_basis_f4( + I::MPolyIdeal; + initial_hts::Int=17, + nr_thrds::Int=1, + max_nr_pairs::Int=0, + la_option::Int=2, + eliminate::Int=0, + complete_reduction::Bool=true, + normalize::Bool=true, + truncate_lifting::Int=0, + info_level::Int=0 + ) + + AI = AlgebraicSolving.Ideal(I.gens.O) + vars = gens(base_ring(I))[eliminate+1:end] + ord = degrevlex(vars) + if length(AI.gens) == 0 + I.gb[ord] = IdealGens(I.gens.Ox, singular_generators(I), complete_reduction) + I.gb[ord].ord = ord + I.gb[ord].isGB = true + I.gb[ord].S.isGB = true + else + AlgebraicSolving.groebner_basis(AI, + initial_hts = initial_hts, + nr_thrds = nr_thrds, + max_nr_pairs = max_nr_pairs, + la_option = la_option, + eliminate = eliminate, + complete_reduction = complete_reduction, + normalize = normalize, + truncate_lifting = truncate_lifting, + info_level = info_level) + + I.gb[ord] = + IdealGens(AI.gb[eliminate], ord, keep_ordering = false, isGB = true) + I.gb[ord].isReduced = complete_reduction + end + return I.gb[ord] +end diff --git a/src/Rings/groebner/fglm.jl b/src/Rings/groebner/fglm.jl new file mode 100644 index 000000000000..7e7b05754c14 --- /dev/null +++ b/src/Rings/groebner/fglm.jl @@ -0,0 +1,162 @@ +@doc raw""" + _fglm(G::IdealGens; ordering::MonomialOrdering) + +Converts a Gröbner basis `G` w.r.t. a given global monomial ordering for `` +to a Gröbner basis `H` w.r.t. another monomial ordering `ordering` for ``. + +**NOTE**: `_fglm` assumes that `G` is a reduced Gröbner basis (i.e. w.r.t. a global monomial ordering) and that `ordering` is a global monomial ordering. + +# Examples +```jldoctest +julia> R, (x1, x2, x3, x4) = polynomial_ring(GF(101), [:x1, :x2, :x3, :x4]) +(Multivariate polynomial ring in 4 variables over GF(101), FqMPolyRingElem[x1, x2, x3, x4]) + +julia> J = ideal(R, [x1+2*x2+2*x3+2*x4-1, + x1^2+2*x2^2+2*x3^2+2*x4^2-x1, + 2*x1*x2+2*x2*x3+2*x3*x4-x2, + x2^2+2*x1*x3+2*x2*x4-x3 + ]) +Ideal generated by + x1 + 2*x2 + 2*x3 + 2*x4 + 100 + x1^2 + 100*x1 + 2*x2^2 + 2*x3^2 + 2*x4^2 + 2*x1*x2 + 2*x2*x3 + 100*x2 + 2*x3*x4 + 2*x1*x3 + x2^2 + 2*x2*x4 + 100*x3 + +julia> groebner_basis(J, ordering=degrevlex(R), complete_reduction=true) +Gröbner basis with elements + 1 -> x1 + 2*x2 + 2*x3 + 2*x4 + 100 + 2 -> x3^2 + 2*x2*x4 + 19*x3*x4 + 76*x4^2 + 72*x2 + 86*x3 + 42*x4 + 3 -> x2*x3 + 99*x2*x4 + 40*x3*x4 + 11*x4^2 + 65*x2 + 58*x3 + 30*x4 + 4 -> x2^2 + 2*x2*x4 + 30*x3*x4 + 45*x4^2 + 43*x2 + 72*x3 + 86*x4 + 5 -> x3*x4^2 + 46*x4^3 + 28*x2*x4 + 16*x3*x4 + 7*x4^2 + 58*x2 + 63*x3 + 15*x4 + 6 -> x2*x4^2 + 67*x4^3 + 56*x2*x4 + 58*x3*x4 + 45*x4^2 + 14*x2 + 86*x3 + 7 -> x4^4 + 65*x4^3 + 26*x2*x4 + 47*x3*x4 + 71*x4^2 + 37*x2 + 79*x3 + 100*x4 +with respect to the ordering + degrevlex([x1, x2, x3, x4]) + +julia> Oscar._fglm(J.gb[degrevlex(R)], lex(R)) +Gröbner basis with elements + 1 -> x4^8 + 36*x4^7 + 95*x4^6 + 39*x4^5 + 74*x4^4 + 7*x4^3 + 45*x4^2 + 98*x4 + 2 -> x3 + 53*x4^7 + 93*x4^6 + 74*x4^5 + 26*x4^4 + 56*x4^3 + 15*x4^2 + 88*x4 + 3 -> x2 + 25*x4^7 + 57*x4^6 + 13*x4^5 + 16*x4^4 + 78*x4^3 + 31*x4^2 + 16*x4 + 4 -> x1 + 46*x4^7 + 3*x4^6 + 28*x4^5 + 17*x4^4 + 35*x4^3 + 9*x4^2 + 97*x4 + 100 +with respect to the ordering + lex([x1, x2, x3, x4]) +``` +""" +function _fglm(G::IdealGens, ordering::MonomialOrdering) + (G.isGB == true && G.isReduced == true) || error("Input must be a reduced Gröbner basis.") + Singular.dimension(singular_generators(G)) == 0 || error("Dimension of corresponding ideal must be zero.") + SR_destination, = Singular.polynomial_ring(base_ring(G.Sx), symbols(G.Sx); ordering = singular(ordering)) + + ptr = Singular.libSingular.fglmzero(G.S.ptr, G.Sx.ptr, SR_destination.ptr) + return IdealGens(base_ring(G), Singular.sideal{Singular.spoly}(SR_destination, ptr, true)) +end + +@doc raw""" + fglm(I::MPolyIdeal; start_ordering::MonomialOrdering = default_ordering(base_ring(I)), + destination_ordering::MonomialOrdering) + +Given a **zero-dimensional** ideal `I`, return the reduced Gröbner basis of `I` with respect to `destination_ordering`. + +!!! note + Both `start_ordering` and `destination_ordering` must be global and the base ring of `I` must be a polynomial ring over a field. + +!!! note + The function implements the Gröbner basis conversion algorithm by **F**augère, **G**ianni, **L**azard, and **M**ora. See [FGLM93](@cite) for more information. + +# Examples +```jldoctest +julia> R, (a, b, c, d, e) = polynomial_ring(QQ, [:a, :b, :c, :d, :e]); + +julia> f1 = a+b+c+d+e; + +julia> f2 = a*b+b*c+c*d+a*e+d*e; + +julia> f3 = a*b*c+b*c*d+a*b*e+a*d*e+c*d*e; + +julia> f4 = b*c*d+a*b*c*e+a*b*d*e+a*c*d*e+b*c*d*e; + +julia> f5 = a*b*c*d*e-1; + +julia> I = ideal(R, [f1, f2, f3, f4, f5]); + +julia> G = fglm(I, destination_ordering = lex(R)); + +julia> length(G) +8 + +julia> total_degree(G[8]) +60 + +julia> leading_coefficient(G[8]) +83369589588385815165248207597941242098312973356252482872580035860533111990678631297423089011608753348453253671406641805924218003925165995322989635503951507226650115539638517111445927746874479234 +``` +""" +function fglm(I::MPolyIdeal; start_ordering::MonomialOrdering = default_ordering(base_ring(I)), destination_ordering::MonomialOrdering) + isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The FGLM algorithm requires a coefficient ring that is a field.") + (is_global(start_ordering) && is_global(destination_ordering)) || error("Start and destination orderings must be global.") + haskey(I.gb, destination_ordering) && return I.gb[destination_ordering] + if !haskey(I.gb, start_ordering) + standard_basis(I, ordering=start_ordering, complete_reduction=true) + elseif I.gb[start_ordering].isReduced == false + I.gb[start_ordering] = _compute_standard_basis(I.gb[start_ordering], start_ordering, true) + end + + I.gb[destination_ordering] = _fglm(I.gb[start_ordering], destination_ordering) + + return I.gb[destination_ordering] +end + +@doc raw""" + _compute_groebner_basis_using_fglm(I::MPolyIdeal, destination_ordering::MonomialOrdering) + +Computes a reduced Gröbner basis for `I` w.r.t. `destination_ordering` using the FGLM algorithm. + +**Note**: Internal function, subject to change, do not use. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> I = ideal(R,[x^2+y,x*y-y]) +Ideal generated by + x^2 + y + x*y - y + +julia> Oscar._compute_groebner_basis_using_fglm(I, lex(R)) +Gröbner basis with elements + 1 -> y^2 + y + 2 -> x*y - y + 3 -> x^2 + y +with respect to the ordering + lex([x, y]) + +julia> I.gb[lex(R)] +Gröbner basis with elements + 1 -> y^2 + y + 2 -> x*y - y + 3 -> x^2 + y +with respect to the ordering + lex([x, y]) + +julia> I.gb[degrevlex(R)] +Gröbner basis with elements + 1 -> y^2 + y + 2 -> x*y - y + 3 -> x^2 + y +with respect to the ordering + degrevlex([x, y]) +``` +""" +function _compute_groebner_basis_using_fglm(I::MPolyIdeal, + destination_ordering::MonomialOrdering) + isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The FGLM algorithm requires a coefficient ring that is a field.") + haskey(I.gb, destination_ordering) && return I.gb[destination_ordering] + is_global(destination_ordering) || error("Destination ordering must be global.") + G = groebner_assure(I, true, true) + start_ordering = G.ord + dim(I) == 0 || error("Dimension of ideal must be zero.") + I.gb[destination_ordering] = _fglm(G, destination_ordering) +end diff --git a/src/Rings/groebner/general.jl b/src/Rings/groebner/general.jl new file mode 100644 index 000000000000..a082f9512d13 --- /dev/null +++ b/src/Rings/groebner/general.jl @@ -0,0 +1,242 @@ +# groebner stuff ####################################################### +@doc raw""" + _compute_standard_basis(B::IdealGens; ordering::MonomialOrdering, + complete_reduction::Bool = false) + +**Note**: Internal function, subject to change, do not use. + +Given an `IdealGens` `B` and optional parameters `ordering` for a monomial ordering and `complete_reduction` +this function computes a Gröbner basis (if `complete_reduction = true` the reduced Gröbner basis) of the +ideal spanned by the elements in `B` w.r.t. the given monomial ordering `ordering`. The Gröbner basis is then +returned in `B.S`. + +# Examples +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) +Ideal generating system with elements + 1 -> x*y - 3*x + 2 -> -2*x^2*y + y^3 + +julia> B = Oscar._compute_standard_basis(A, degrevlex(R)) +Gröbner basis with elements + 1 -> x*y - 3*x + 2 -> y^3 - 6*x^2 + 3 -> 2*x^3 - 9*x +with respect to the ordering + degrevlex([x, y]) +``` +""" +function _compute_standard_basis(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) + gensSord = singular_generators(B, ordering) + i = Singular.std(gensSord, complete_reduction = complete_reduction) + BA = IdealGens(B.Ox, i, complete_reduction) + BA.isGB = true + BA.ord = ordering + if isdefined(BA, :S) + BA.S.isGB = true + end + return BA +end + +# standard basis for non-global orderings ############################# +@doc raw""" + standard_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), + complete_reduction::Bool = false, algorithm::Symbol = :buchberger) + +Return a standard basis of `I` with respect to `ordering`. + +The keyword `algorithm` can be set to +- `:buchberger` (implementation of Buchberger's algorithm in *Singular*), +- `:modular` (implementation of multi-modular approach, if applicable), +- `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package), +- `:fglm` (implementation of the FGLM algorithm in *Singular*), +- `:hc` (implementation of Buchberger's algorithm in *Singular* trying to first compute the highest corner modulo some prime), and +- `:hilbert` (implementation of a Hilbert driven Gröbner basis computation in *Singular*). + +!!! note + See the description of the functions `groebner_basis_hilbert_driven`, `fglm`, + and `f4` in the OSCAR documentation for some more details and for restrictions + on the input data when using these versions of the standard basis algorithm. + +!!! note + The returned standard basis is reduced if `ordering` is `global` and `complete_reduction = true`. + +# Examples +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]); + +julia> I = ideal([x*(x+1), x^2-y^2+(x-2)*y]); + +julia> standard_basis(I, ordering = negdegrevlex(R)) +Standard basis with elements + 1 -> x + 2 -> y +with respect to the ordering + negdegrevlex([x, y]) +``` +""" +function standard_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), + complete_reduction::Bool = false, algorithm::Symbol = :buchberger) + complete_reduction && @assert is_global(ordering) + @req is_exact_type(elem_type(base_ring(I))) "This functionality is only supported over exact fields." + if haskey(I.gb, ordering) && (complete_reduction == false || I.gb[ordering].isReduced == true) + return I.gb[ordering] + end + if algorithm == :buchberger + if !haskey(I.gb, ordering) + I.gb[ordering] = _compute_standard_basis(I.gens, ordering, complete_reduction) + elseif complete_reduction == true + I.gb[ordering] = _compute_standard_basis(I.gb[ordering], ordering, complete_reduction) + end + elseif algorithm == :modular + if is_f4_applicable(I, ordering) + # since msolve v0.7.0 is most of the time more efficient + # to compute a reduced GB by default + groebner_basis_f4(I, complete_reduction=true) + elseif base_ring(I) isa QQMPolyRing + groebner_basis_modular(I, ordering=ordering) + else + error("Modular option not applicable in this setting.") + end + elseif algorithm == :fglm + _compute_groebner_basis_using_fglm(I, ordering) + elseif algorithm == :hc + standard_basis_highest_corner(I, ordering=ordering) + elseif algorithm == :hilbert + weights = _find_weights(gens(I)) + if !any(iszero, weights) + J, target_ordering, hn = I, ordering, nothing + else + R = base_ring(I) + K = iszero(characteristic(R)) && !haskey(I.gb, degrevlex(R)) ? _mod_rand_prime(I) : I + S = base_ring(K) + gb = groebner_assure(K, degrevlex(S)) + # 2024-02-09 Next lines "blindly" updated to use new homogenization UI + H = homogenizer(S, "w") + K_hom = H(K) + gb_hom = IdealGens(H.(gens(gb))) + gb_hom.isGB = true + K_hom.gb[degrevlex(S)] = gb_hom + singular_assure(K_hom.gb[degrevlex(S)]) + hn = hilbert_series(quo(base_ring(K_hom), K_hom)[1])[1] + H2 = homogenizer(R, "w") + J = H2(I) + weights = ones(Int, ngens(base_ring(J))) + target_ordering = _extend_mon_order(ordering, base_ring(J)) + end + GB = groebner_basis_hilbert_driven(J, destination_ordering=target_ordering, + complete_reduction=complete_reduction, + weights=weights, + hilbert_numerator=hn) + if base_ring(I) == base_ring(J) + I.gb[ordering] = GB + else + DH2 = dehomogenizer(H2) + GB_dehom_gens = DH2.(gens(GB)) + I.gb[ordering] = IdealGens(GB_dehom_gens, ordering, isGB = true) + end + elseif algorithm == :f4 + # since msolve v0.7.0 is most of the time more efficient + # to compute a reduced GB by default + groebner_basis_f4(I, complete_reduction=true) + end + return I.gb[ordering] +end + +@doc raw""" + groebner_basis(I::MPolyIdeal; + ordering::MonomialOrdering = default_ordering(base_ring(I)), + complete_reduction::Bool = false, algorithm::Symbol = :buchberger) + +If `ordering` is global, return a Gröbner basis of `I` with respect to `ordering`. + +The keyword `algorithm` can be set to +- `:buchberger` (implementation of Buchberger's algorithm in *Singular*), +- `:modular` (implementation of multi-modular approach, if applicable), +- `:hilbert` (implementation of a Hilbert driven Gröbner basis computation in *Singular*), +- `:fglm` (implementation of the FGLM algorithm in *Singular*), and +- `:f4` (implementation of Faugère's F4 algorithm in the *msolve* package). + +!!! note + See the description of the functions `groebner_basis_hilbert_driven`, `fglm`, + and `f4` in the OSCAR documentation for some more details and for restrictions + on the input data when using these versions of the standard basis algorithm. + +!!! note + The returned Gröbner basis is reduced if `complete_reduction = true`. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> I = ideal(R, [y-x^2, z-x^3]); + +julia> G = groebner_basis(I) +Gröbner basis with elements + 1 -> y^2 - x*z + 2 -> x*y - z + 3 -> x^2 - y +with respect to the ordering + degrevlex([x, y, z]) + +julia> elements(G) +3-element Vector{QQMPolyRingElem}: + -x*z + y^2 + x*y - z + x^2 - y + +julia> elements(G) == gens(G) +true + +julia> groebner_basis(I, ordering = lex(R)) +Gröbner basis with elements + 1 -> y^3 - z^2 + 2 -> x*z - y^2 + 3 -> x*y - z + 4 -> x^2 - y +with respect to the ordering + lex([x, y, z]) +``` +```jldoctest +julia> R, (x, y) = graded_polynomial_ring(QQ, [:x, :y], [1, 3]); + +julia> I = ideal(R, [x*y-3*x^4,y^3-2*x^6*y]); + +julia> groebner_basis(I) +Gröbner basis with elements + 1 -> 3*x^4 - x*y + 2 -> 2*x^3*y^2 - 3*y^3 + 3 -> x*y^3 + 4 -> y^4 +with respect to the ordering + wdegrevlex([x, y], [1, 3]) +``` + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> V = [3*x^3*y+x^3+x*y^3+y^2*z^2, 2*x^3*z-x*y-x*z^3-y^4-z^2, + 2*x^2*y*z-2*x*y^2+x*z^2-y^4]; + +julia> I = ideal(R, V); + +julia> G = groebner_basis(I, ordering = lex(R), algorithm = :fglm); + +julia> length(G) +8 + +julia> total_degree(G[8]) +34 + +julia> leading_coefficient(G[8]) +-91230304237130414552564280286681870842473427917231798336639893796481988733936505735341479640589040146625319419037353645834346047404145021391726185993823650399589880820226804328750 +``` +""" +function groebner_basis(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool=false, + algorithm::Symbol = :buchberger) + is_global(ordering) || error("Ordering must be global") + return standard_basis(I, ordering=ordering, complete_reduction=complete_reduction, algorithm=algorithm) +end diff --git a/src/Rings/groebner/generators.jl b/src/Rings/groebner/generators.jl new file mode 100644 index 000000000000..fe140d494c90 --- /dev/null +++ b/src/Rings/groebner/generators.jl @@ -0,0 +1,72 @@ +@doc raw""" + groebner_assure(I::MPolyIdeal, complete_reduction::Bool = false, need_global::Bool = false) + groebner_assure(I::MPolyIdeal, ordering::MonomialOrdering, complete_reduction::Bool = false) + +**Note**: Internal function, subject to change, do not use. + +Given an ideal `I` in a multivariate polynomial ring this function assures that a +Gröbner basis w.r.t. the given monomial ordering is attached to `I` in `I.gb`. +It *currently* also ensures that the basis is defined on the Singular side in +`I.gb.S`, but this should not be relied upon: use `singular_assure(I.gb)` before +accessing `I.gb.S`. + +# Examples +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> I = ideal([x*y-3*x,y^3-2*x^2*y]) +Ideal generated by + x*y - 3*x + -2*x^2*y + y^3 + +julia> Oscar.groebner_assure(I, degrevlex(R)); + +julia> I.gb[degrevlex(R)] +Gröbner basis with elements + 1 -> x*y - 3*x + 2 -> y^3 - 6*x^2 + 3 -> 2*x^3 - 9*x +with respect to the ordering + degrevlex([x, y]) +``` +""" +function groebner_assure(I::MPolyIdeal, complete_reduction::Bool = false, need_global::Bool = false) + if !isempty(I.gb) + for G in values(I.gb) + need_global || return G + is_global(G.ord) || continue + complete_reduction || return G + if !G.isReduced + I.gb[G.ord] = _compute_standard_basis(G, G.ord, true) + end + return I.gb[G.ord] + end + end + ord = default_ordering(base_ring(I)) + (need_global <= is_global(ord)) || error("Monomial ordering must be global.") + I.gb[ord] = groebner_assure(I, ord, complete_reduction) + return I.gb[ord] +end + +function groebner_assure(I::MPolyIdeal, ordering::MonomialOrdering, complete_reduction::Bool = false) + return get!(I.gb, ordering) do + _compute_standard_basis(I.gens, ordering, complete_reduction) + end +end + +function oscar_groebner_generators(I::MPolyIdeal, ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) + standard_basis(I, ordering=ordering, complete_reduction = complete_reduction) + oscar_assure(I.gb[ordering]) + return I.gb[ordering].gens.O +end + +function singular_groebner_generators(I::MPolyIdeal, ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) + standard_basis(I, ordering=ordering, complete_reduction = complete_reduction) + return singular_generators(I.gb[ordering], ordering) +end + +function singular_groebner_generators(I::MPolyIdeal, complete_reduction::Bool, need_global::Bool) + G = groebner_assure(I, complete_reduction, need_global) + return singular_generators(G, G.ord) +end diff --git a/src/Rings/groebner/groebner.jl b/src/Rings/groebner/groebner.jl new file mode 100644 index 000000000000..232e4eace58d --- /dev/null +++ b/src/Rings/groebner/groebner.jl @@ -0,0 +1,10 @@ +include("f4.jl") +include("fglm.jl") +include("general.jl") +include("generators.jl") +include("highest-corner.jl") +include("hilbert-driven.jl") +include("leading-ideal.jl") +include("modular.jl") +include("reduce.jl") +include("transformation-matrix.jl") diff --git a/src/Rings/groebner/highest-corner.jl b/src/Rings/groebner/highest-corner.jl new file mode 100644 index 000000000000..e721e71e58ba --- /dev/null +++ b/src/Rings/groebner/highest-corner.jl @@ -0,0 +1,23 @@ +@doc raw""" + standard_basis_highest_corner(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) + +Return a standard basis of `I` with respect to `ordering`. `ordering` needs to be local, the coefficient ring needs to be `QQ`. +The algorithm first computes a standard basis over a finite field in order to get an upper bound for the highest corner fast. +Then this bound is used to speed up the standard basis computation over `QQ´. +""" +function standard_basis_highest_corner(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) + @req is_local(ordering) "Monomial ordering must be local for this variant." + @req coefficient_ring(I) == QQ "Base ring must be QQ." + + #= apply highest corner standard basis variant in Singular =# + ssb = Singular.LibStandard.groebner(singular_groebner_generators(I, ordering), "HC") + + sb = IdealGens(I.gens.Ox, ssb, false) + sb.isGB = true + sb.ord = ordering + if isdefined(sb, :S) + sb.S.isGB = true + end + I.gb[ordering] = sb + return sb +end diff --git a/src/Rings/groebner/hilbert-driven.jl b/src/Rings/groebner/hilbert-driven.jl new file mode 100644 index 000000000000..1bc52994cb0a --- /dev/null +++ b/src/Rings/groebner/hilbert-driven.jl @@ -0,0 +1,239 @@ +@doc raw""" + groebner_basis_hilbert_driven(I::MPolyIdeal{P}; destination_ordering::MonomialOrdering, + complete_reduction::Bool = false, + weights::Vector{Int} = ones(Int, ngens(base_ring(I))), + hilbert_numerator::Union{Nothing, ZZPolyRingElem} = nothing) + where {P <: MPolyRingElem} + +Return a Gröbner basis of `I` with respect to `destination_ordering`. + +!!! note + The function implements a version of the Hilbert driven Gröbner basis algorithm. + See the corresponding section of the OSCAR documentation for some details. + +!!! note + All weights must be positive. If no weight vector is entered by the user, all weights + are set to 1. An error is thrown if the generators of `I` are not homogeneous with + respect to the corresponding (weighted) degree. + +!!! note + If $R$ denotes the parent ring of $I$, and $p, q\in\mathbb Z[t]$ are polynomials + such that $p/q$ represents the Hilbert series of $R/I$ as a rational function with + denominator $q = (1-t^{w_1})\cdots (1-t^{w_n}),$ where $n$ is the number of variables + of $R$, and $w_1, \dots, w_n$ are the assigned weights, then `hilbert_numerator` is + meant to be $p$. If this numerator is not entered by the user, it will be computed + internally. + +# Examples +```jldoctest +julia> R, (a, b, c, d, e, f, g) = polynomial_ring(QQ, [:a, :b, :c, :d, :e, :f, :g]); + +julia> V = [-3*a^2+2*f*b+3*f*d, (3*g*b+3*g*e)*a-3*f*c*b, + -3*g^2*a^2-c*b^2*a-g^2*f*e-g^4, e*a-f*b-d*c]; + +julia> I = ideal(R, V); + +julia> o = degrevlex([a, b, c])*degrevlex([d, e, f, g]); + +julia> G = groebner_basis_hilbert_driven(I, destination_ordering = o); + +julia> length(G) +296 + +julia> total_degree(G[49]) +30 +``` + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(GF(32003), [:x, :y, :z]); + +julia> f1 = x^2*y+169*y^21+151*x*y*z^10; + +julia> f2 = 6*x^2*y^4+x*z^14+3*z^24; + +julia> f3 = 11*x^3+5*x*y^10*z^10+2*y^20*z^10+y^10*z^20; + +julia> I = ideal(R, [f1, f2,f3]); + +julia> W = [10, 1, 1]; + +julia> GB = groebner_basis_hilbert_driven(I, destination_ordering = lex(R), weights = W); + +julia> length(GB) +40 +``` + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(GF(32003), [:x, :y, :z]); + +julia> f1 = x^2*y+169*y^21+151*x*y*z^10; + +julia> f2 = 6*x^2*y^4+x*z^14+3*z^24; + +julia> f3 = 11*x^3+5*x*y^10*z^10+2*y^20*z^10+y^10*z^20; + +julia> I = ideal(R, [f1, f2,f3]); + +julia> W = [10, 1, 1]; + +julia> S, t = polynomial_ring(ZZ, :t) +(Univariate polynomial ring in t over ZZ, t) + +julia> hn = -t^75 + t^54 + t^51 + t^45 - t^30 - t^24 - t^21 + 1 +-t^75 + t^54 + t^51 + t^45 - t^30 - t^24 - t^21 + 1 + +julia> GB = groebner_basis_hilbert_driven(I, destination_ordering = lex(R), weights = W, hilbert_numerator = hn); + +julia> length(GB) +40 +``` +""" +function groebner_basis_hilbert_driven(I::MPolyIdeal{P}; + destination_ordering::MonomialOrdering, + complete_reduction::Bool = false, + weights::Vector{Int} = ones(Int, ngens(base_ring(I))), + hilbert_numerator::Union{Nothing, ZZPolyRingElem} = nothing) where {P <: MPolyRingElem} + + all(f -> _is_homogeneous(f, weights), gens(I)) || error("I must be given by generators homogeneous with respect to the given weights.") + isa(coefficient_ring(I), AbstractAlgebra.Field) || error("The underlying coefficient ring of I must be a field.") + ordering = destination_ordering + is_global(ordering) || error("Destination ordering must be global.") + haskey(I.gb, ordering) && return I.gb[ordering] + if isnothing(hilbert_numerator) + if isempty(I.gb) + J = iszero(characteristic(base_ring(I))) ? _mod_rand_prime(I) : I + G = groebner_assure(J, wdegrevlex(base_ring(J), weights)) + else + G = groebner_assure(I) + end + + if characteristic(base_ring(I)) > 0 && ordering == wdegrevlex(base_ring(I), weights) + return G + end + singular_assure(G) + h = Singular.hilbert_series(G.S, weights) + + else + # Quoting from the documentation of Singular.hilbert_series: + # The coefficient vector is returned as a `Vector{Int32}`, and the last element is not actually part of the coefficients of Q(t). + # what? + h = (Int32).([coeff(hilbert_numerator, i) for i in 0:degree(hilbert_numerator)+1]) + end + + singular_I_gens = singular_generators(I.gens, ordering) + singular_ring = base_ring(singular_I_gens) + J = Singular.Ideal(singular_ring, gens(singular_I_gens)...) + i = Singular.std_hilbert(J, h, (Int32).(weights), + complete_reduction = complete_reduction) + GB = IdealGens(I.gens.Ox, i, complete_reduction) + GB.isGB = true + GB.ord = ordering + if isdefined(GB, :S) + GB.S.isGB = true + end + I.gb[destination_ordering] = GB + return GB +end + +# Helper functions for groebner_basis_with_hilbert + +function _extract_weights(T::MPolyDecRing) + if !is_z_graded(T) + error("Ring must be graded by the Integers.") + end + return [Int(first(gr_elem.coeff)) for gr_elem in T.d] +end + +function _extend_mon_order(ordering::MonomialOrdering, + homogenized_ring::MPolyDecRing) + + nvars = ngens(ordering.R) + m = canonical_matrix(ordering) + m_hom = similar(m, nvars + 1, nvars + 1) + m_hom[1, :] = ones(Int, nvars + 1) + m_hom[2:end, 2:end] = m + return matrix_ordering(homogenized_ring, m_hom) +end + +function _mod_rand_prime(I::MPolyIdeal) + p = 32771 + while true + p = Hecke.next_prime(p) + + base_field = GF(p) + ModP, _ = polynomial_ring(base_field, ngens(base_ring(I)); cached = false) + I_mod_p_gens = + try + [map_coefficients(base_field, f; parent=ModP) for f in gens(I)] + catch e + # this precise error is thrown if the chosen prime p divides one + # of the denominators of the coefficients of the generators of I. + # In this case we simply choose the next prime and try again. + if e == ErrorException("Unable to coerce") + continue + else + rethrow(e) + end + end + return ideal(ModP, I_mod_p_gens) + end +end + +# check homogeneity w.r.t. some weights + +function _is_homogeneous(f::MPolyRingElem, weights::Vector{Int}) + w = sum(weights .* first(exponents(f))) + all(sum(weights .* e) == w for e in exponents(f)) +end + + +# check homogeneity w.r.t. total degree +function _is_homogeneous(f::MPolyRingElem) + leadexpv,tailexpvs = Iterators.peel(AbstractAlgebra.exponent_vectors(f)) + d = sum(leadexpv) + for tailexpv in tailexpvs + if d!=sum(tailexpv) + return false + end + end + return true +end + +# compute weights such that F is a homogeneous system w.r.t. these weights +function _find_weights(F::Vector{P}) where {P <: MPolyRingElem} + + if all(_is_homogeneous, F) + return ones(Int, ngens(parent(F[1]))) + end + + nrows = sum((length).(F)) - length(F) + ncols = ngens(parent(first(F))) + + exp_diffs = permutedims(reduce(hcat, [e[i] - e[1] for e in + (collect).((exponents).(F)) + for i in 2:length(e)])) + K = kernel(matrix(QQ, nrows, ncols, exp_diffs); side = :right) + isempty(K) && return zeros(Int, ncols) + # Here we try to find a vector with strictly positive entries in K + # this method to find such a vector is taken from + # https://mathoverflow.net/questions/363181/intersection-of-a-vector-subspace-with-a-cone + Pol = polyhedron(-K, zeros(Int, ncols)) + !is_feasible(Pol) && return zeros(Int, ncols) + pos_vec = zeros(Int, ncols) + for i in 1:ncols + ei = [j == i ? one(QQ) : zero(QQ) for j in 1:ncols] + obj_func = ei * K + L = linear_program(Pol, obj_func) + m, v = solve_lp(L) + if isnothing(v) + Pol_new = intersect(Pol, polyhedron(ei*K, [1])) + L = linear_program(Pol_new, obj_func) + v = optimal_vertex(L) + end + pos_vec += K*(v.p) + end + ret = (Int).(lcm((denominator).(pos_vec)) .* pos_vec) + ret = (x -> div(x, gcd(ret))).(ret) + # assure that the weights fit in Int32 for singular + return all(ret .< 2^32) ? ret : zeros(Int,ncols) +end diff --git a/src/Rings/groebner/leading-ideal.jl b/src/Rings/groebner/leading-ideal.jl new file mode 100644 index 000000000000..c0e8b1f8dbd7 --- /dev/null +++ b/src/Rings/groebner/leading-ideal.jl @@ -0,0 +1,67 @@ +@doc raw""" + leading_ideal(G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(G[1]))) + where T <: MPolyRingElem + +Return the leading ideal of `G` with respect to `ordering`. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> L = leading_ideal([x*y^2-3*x, x^3-14*y^5], ordering=degrevlex(R)) +Ideal generated by + x*y^2 + y^5 + +julia> L = leading_ideal([x*y^2-3*x, x^3-14*y^5], ordering=lex(R)) +Ideal generated by + x*y^2 + x^3 +``` +""" +function leading_ideal(G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(G[1]))) where { T <: MPolyRingElem } + return ideal(parent(G[1]), [leading_monomial(f; ordering = ordering) for f in G]) +end + +function leading_ideal(I::IdealGens{T}) where { T <: MPolyRingElem } + return ideal(base_ring(I), [leading_monomial(f; ordering = I.ord) for f in I]) +end + +function leading_ideal(I::IdealGens{T}, ordering::MonomialOrdering) where T <: MPolyRingElem + return ideal(base_ring(I), [leading_monomial(f; ordering = ordering) for f in I]) +end + + +@doc raw""" + leading_ideal(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) + +Return the leading ideal of `I` with respect to `ordering`. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> I = ideal(R,[x*y^2-3*x, x^3-14*y^5]) +Ideal generated by + x*y^2 - 3*x + x^3 - 14*y^5 + +julia> L = leading_ideal(I, ordering=degrevlex(R)) +Ideal generated by + x*y^2 + x^4 + y^5 + +julia> L = leading_ideal(I, ordering=lex(R)) +Ideal generated by + y^7 + x*y^2 + x^3 +``` +""" +function leading_ideal(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I))) + G = standard_basis(I, ordering=ordering) + return ideal(base_ring(I), [leading_monomial(g; ordering = ordering) for g in G]) +end diff --git a/src/Rings/groebner/modular.jl b/src/Rings/groebner/modular.jl new file mode 100644 index 000000000000..c0a67059fc6d --- /dev/null +++ b/src/Rings/groebner/modular.jl @@ -0,0 +1,135 @@ +# modular gröbner basis techniques using Singular +@doc raw""" + groebner_basis_modular(I::MPolyIdeal{QQMPolyRingElem}; ordering::MonomialOrdering = default_ordering(base_ring(I)), certify::Bool = false) + +Compute the reduced Gröbner basis of `I` w.r.t. `ordering` using a +multi-modular strategy. + +!!! note + This function is probabilistic and returns a correct result + only with high probability. +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x,:y,:z]); + +julia> I = ideal(R, [x^2+1209, x*y + 3279*y^2]) +Ideal generated by + x^2 + 1209 + x*y + 3279*y^2 + +julia> groebner_basis_modular(I) +Gröbner basis with elements + 1 -> y^3 + 403//3583947*y + 2 -> x^2 + 1209 + 3 -> x*y + 3279*y^2 +with respect to the ordering + degrevlex([x, y, z]) +``` +""" +function groebner_basis_modular(I::MPolyIdeal{QQMPolyRingElem}; ordering::MonomialOrdering = default_ordering(base_ring(I)), + certify::Bool = false) + + # small function to get a canonically sorted reduced gb + sorted_gb = idl -> begin + R = base_ring(idl) + gb = gens(groebner_basis(idl, ordering = ordering, + complete_reduction = true)) + sort!(gb; by = leading_monomial, + lt = (m1, m2) -> cmp(MonomialOrdering(R, ordering.o), m1, m2) > 0) + end + + if haskey(I.gb, ordering) + return I.gb[ordering] + end + + primes = Hecke.PrimesSet(rand(2^15:2^16), -1) + + p = iterate(primes)[1] + Qt = base_ring(I) + Zt = polynomial_ring(ZZ, [string(s) for s = symbols(Qt)]; cached = false)[1] + + Rt, t = polynomial_ring(GF(p), [string(s) for s = symbols(Qt)]; cached = false) + std_basis_mod_p_lifted = map(sorted_gb(ideal(Rt, gens(I)))) do x + map_coefficients(z -> lift(ZZ, z), x, parent = Zt) + end + std_basis_crt_previous = std_basis_mod_p_lifted + + n_stable_primes = 0 + d = ZZRingElem(p) + unlucky_primes_in_a_row = 0 + done = false + while !done + while n_stable_primes < 2 + p = iterate(primes, p)[1] + Rt, t = polynomial_ring(GF(p), [string(s) for s = symbols(Qt)]; cached = false) + std_basis_mod_p_lifted = map(sorted_gb(ideal(Rt, gens(I)))) do x + map_coefficients(z -> lift(ZZ, z), x, parent = Zt) + end + + # test for unlucky prime + if any(((i, p), ) -> leading_monomial(p) != leading_monomial(std_basis_crt_previous[i]), + enumerate(std_basis_mod_p_lifted)) + unlucky_primes_in_a_row += 1 + # if we get unlucky twice in a row we assume that + # we started with an unlucky prime + if unlucky_primes_in_a_row == 2 + std_basis_crt_previous = std_basis_mod_p_lifted + end + continue + end + unlucky_primes_in_a_row = 0 + + is_stable = true + for (i, f) in enumerate(std_basis_mod_p_lifted) + if !iszero(f - std_basis_crt_previous[i]) + std_basis_crt_previous[i], _ = induce_crt(std_basis_crt_previous[i], d, f, ZZRingElem(p), true) + stable = false + end + end + if is_stable + n_stable_primes += 1 + end + d *= ZZRingElem(p) + end + final_gb = QQMPolyRingElem[induce_rational_reconstruction(f, d, parent = base_ring(I)) for f in std_basis_crt_previous] + + I.gb[ordering] = IdealGens(final_gb, ordering) + if certify + done = _certify_modular_groebner_basis(I, ordering) + else + done = true + end + end + I.gb[ordering].isGB = true + return I.gb[ordering] +end + +function induce_rational_reconstruction(f::ZZMPolyRingElem, d::ZZRingElem; parent = 1) + g = MPolyBuildCtx(parent) + for (c, v) in zip(AbstractAlgebra.coefficients(f), AbstractAlgebra.exponent_vectors(f)) + fl, r, s = Hecke.rational_reconstruction(c, d) + fl ? push_term!(g, r//s, v) : push_term!(g, c, v) + end + return finish(g) +end + +function _certify_modular_groebner_basis(I::MPolyIdeal, ordering::MonomialOrdering) + @req haskey(I.gb, ordering) "There exists no standard basis w.r.t. the given ordering." + ctr = 0 + singular_generators(I.gb[ordering]) + SR = I.gb[ordering].gens.Sx + SG = I.gb[ordering].gens.S + + #= test if I is included in =# + for f in I.gens + if Singular.reduce(SR(f), SG) != 0 + break + end + ctr += 1 + end + if ctr != ngens(I) + return false + end + + #= test if G is a standard basis of w.r.t. ordering =# + return is_standard_basis(I.gb[ordering], ordering=ordering) +end diff --git a/src/Rings/groebner/reduce.jl b/src/Rings/groebner/reduce.jl new file mode 100644 index 000000000000..6d200333b0d6 --- /dev/null +++ b/src/Rings/groebner/reduce.jl @@ -0,0 +1,662 @@ +@doc raw""" + reduce(I::IdealGens, J::IdealGens; + ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + +Return a `Vector` whose elements are the underlying elements of `I` +reduced by the underlying generators of `J` w.r.t. the monomial +ordering `ordering`. `J` need not be a Gröbner basis. The returned +`Vector` will have the same number of elements as `I`, even if they +are zero. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(GF(11), [:x, :y, :z]); + +julia> I = ideal(R, [x^2, x*y - y^2]); + +julia> J = ideal(R, [y^3]) +Ideal generated by + y^3 + +julia> reduce(J.gens, I.gens) +1-element Vector{FqMPolyRingElem}: + y^3 + +julia> reduce(J.gens, groebner_basis(I)) +1-element Vector{FqMPolyRingElem}: + 0 + +julia> reduce(y^3, [x^2, x*y-y^3]) +x*y + +julia> reduce(y^3, [x^2, x*y-y^3], ordering=lex(R)) +y^3 + +julia> reduce([y^3], [x^2, x*y-y^3], ordering=lex(R)) +1-element Vector{FqMPolyRingElem}: + y^3 +``` +""" +function reduce(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + @assert base_ring(J) == base_ring(I) + Is = singular_generators(I, ordering) + Js = singular_generators(J, ordering) + res = reduce(Is, Js, complete_reduction=complete_reduction) + return [J.gens.Ox(x) for x = gens(res)] +end + +@doc raw""" + reduce(g::T, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(g)), complete_reduction::Bool = false) where T <: MPolyRingElem + +If `ordering` is global, return the remainder in a standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. +Otherwise, return the remainder in a *weak* standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. + + reduce(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem + +Return a `Vector` which contains, for each element `g` of `G`, a remainder as above. + +!!! note + The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. + +!!! note + The reduction strategy behind the `reduce` function and the reduction strategy behind the functions + `reduce_with_quotients` and `reduce_with_quotients_and_unit` differ. As a consequence, the computed + remainders may differ. + +# Examples +```jldoctest +julia> R, (z, y, x) = polynomial_ring(QQ, [:z, :y, :x]); + +julia> f1 = y-x^2; f2 = z-x^3; + +julia> g = x^3*y-3*y^2*z^2+x*y*z; + +julia> reduce(g, [f1, f2], ordering = lex(R)) +-3*x^10 + x^6 + x^5 +``` + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; + +julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; + +julia> reduce(g, [f1, f2, f3], ordering = lex(R)) +x^5 + x^3*y + x^2*y^2*z^2 + z^6 + +julia> reduce(g, [f1,f2, f3], ordering = lex(R), complete_reduction = true) +x^5 - x^3 + y^6 + z^6 +``` + +""" +function reduce(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + isempty(F) && return f + J = IdealGens(parent(F[1]), F, ordering) + return reduce(f, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + isempty(G) && return F + J = IdealGens(parent(G[1]), G, ordering) + return reduce(F, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + isempty(F) && return f + @assert parent(f) == base_ring(F) + R = parent(f) + I = IdealGens(R, [f], ordering) + redv = reduce(I, F, ordering=ordering, complete_reduction=complete_reduction) + return redv[1] +end + +function reduce(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + (isempty(F) || isempty(G)) && return F + @assert parent(F[1]) == base_ring(G) + R = parent(F[1]) + I = IdealGens(R, F, ordering) + return reduce(I, G, ordering=ordering, complete_reduction=complete_reduction) +end + +@doc raw""" + reduce_with_quotients_and_unit(g::T, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(parent(g)), complete_reduction::Bool = false) where T <: MPolyRingElem + +Return the unit, the quotients and the remainder in a weak standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. + + reduce_with_quotients_and_unit(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem + +Return a `Vector` which contains, for each element `g` of `G`, a unit, quotients, and a remainder as above. + +!!! note + The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. + +!!! note + The reduction strategy behind the `reduce` function and the reduction strategy behind the functions + `reduce_with_quotients` and `reduce_with_quotients_and_unit` differ. As a consequence, the computed + remainders may differ. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; + +julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; + +julia> u, Q, h =reduce_with_quotients_and_unit(g, [f1,f2, f3], ordering = lex(R)); + +julia> u +[1] + +julia> G = [g, x*y^3-3*x^2*y^2*z^2]; + +julia> U, Q, H = reduce_with_quotients_and_unit(G, [f1, f2, f3], ordering = negdegrevlex(R)); + +julia> U +[y + 1 0] +[ 0 y + 1] + +julia> Q +[ x^3 - x*y^2*z^2 + x*y + y^2*z^2 0 y*z^2 + z^2] +[x*y*z^2 + y^3*z - 3*y^2*z^2 - y*z -x^2*y*z - x^2*z + x*y + x 0] + +julia> H +2-element Vector{QQMPolyRingElem}: + 0 + 0 + +julia> U*G == Q*[f1, f2, f3]+H +true +``` +""" +function reduce_with_quotients_and_unit(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + if isempty(F) + return identity_matrix(parent(f), 1), zero_matrix(parent(f), 1, 0), f + end + J = IdealGens(parent(F[1]), F, ordering) + return reduce_with_quotients_and_unit(f, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce_with_quotients_and_unit(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + @assert !isempty(F) + if isempty(G) + return identity_matrix(parent(F[1]), length(F)), zero_matrix(parent(F[1]), length(F), 0), F + end + J = IdealGens(parent(G[1]), G, ordering) + return reduce_with_quotients_and_unit(F, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce_with_quotients_and_unit(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + if isempty(F) + return identity_matrix(parent(f), 1), zero_matrix(parent(f), 1, 0), f + end + @assert parent(f) == base_ring(F) + R = parent(f) + I = IdealGens(R, [f], ordering) + u, q, r = _reduce_with_quotients_and_unit(I, F, ordering, complete_reduction) + return u, q, r[1] +end + +function reduce_with_quotients_and_unit(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + @assert !isempty(F) + if isempty(G) + return identity_matrix(parent(F[1]), length(F)), zero_matrix(parent(F[1]), length(F), 0), F + end + @assert parent(F[1]) == base_ring(G) + R = parent(F[1]) + I = IdealGens(R, F, ordering) + return _reduce_with_quotients_and_unit(I, G, ordering, complete_reduction) +end + +@doc raw""" + reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens; + ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + +Return a `Tuple` consisting of a `Generic.MatSpaceElem` `M`, a +`Vector` `res` whose elements are the underlying elements of `I` +reduced by the underlying generators of `J` w.r.t. the monomial +ordering `ordering` and a diagonal matrix `units` such that `M * +gens(J) + res == units * gens(I)`. If `ordering` is global then +`units` will always be the identity matrix, see also +`reduce_with_quotients`. `J` need not be a Gröbner basis. `res` will +have the same number of elements as `I`, even if they are zero. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(GF(11), [:x, :y]); + +julia> I = ideal(R, [x]); + +julia> R, (x, y) = polynomial_ring(GF(11), [:x, :y]); + +julia> I = ideal(R, [x]); + +julia> J = ideal(R, [x+1]); + +julia> unit, M, res = reduce_with_quotients_and_unit(I.gens, J.gens, ordering = neglex(R)) +([x+1], [x], FqMPolyRingElem[0]) + +julia> M * gens(J) + res == unit * gens(I) +true + +julia> f = x^3*y^2-y^4-10 +x^3*y^2 + 10*y^4 + 1 + +julia> F = [x^2*y-y^3, x^3-y^4] +2-element Vector{FqMPolyRingElem}: + x^2*y + 10*y^3 + x^3 + 10*y^4 + +julia> reduce_with_quotients_and_unit(f, F) +([1], [x*y 10*x+1], x^4 + 10*x^3 + 1) + +julia> unit, M, res = reduce_with_quotients_and_unit(f, F, ordering=lex(R)) +([1], [x*y 0], x*y^4 + 10*y^4 + 1) + +julia> M * F + [res] == unit * [f] +true +``` +""" +function reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + return _reduce_with_quotients_and_unit(I, J, ordering, complete_reduction) +end + + +@doc raw""" + reduce_with_quotients(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + +Return a `Tuple` consisting of a `Generic.MatSpaceElem` `M` and a +`Vector` `res` whose elements are the underlying elements of `I` +reduced by the underlying generators of `J` w.r.t. the monomial +ordering `ordering` such that `M * gens(J) + res == gens(I)` if `ordering` is global. +If `ordering` is local then this equality holds after `gens(I)` has been multiplied +with an unknown diagonal matrix of units, see `reduce_with_quotients_and_unit` to +obtain this matrix. `J` need not be a Gröbner basis. `res` will have the same number +of elements as `I`, even if they are zero. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(GF(11), [:x, :y, :z]); + +julia> J = ideal(R, [x^2, x*y - y^2]); + +julia> I = ideal(R, [x*y, y^3]); + +julia> gb = groebner_basis(J) +Gröbner basis with elements + 1 -> x*y + 10*y^2 + 2 -> x^2 + 3 -> y^3 +with respect to the ordering + degrevlex([x, y, z]) + +julia> M, res = reduce_with_quotients(I.gens, gb) +([1 0 0; 0 0 1], FqMPolyRingElem[y^2, 0]) + +julia> M * gens(gb) + res == gens(I) +true + +julia> f = x^3*y^2-y^4-10 +x^3*y^2 + 10*y^4 + 1 + +julia> F = [x^2*y-y^3, x^3-y^4] +2-element Vector{FqMPolyRingElem}: + x^2*y + 10*y^3 + x^3 + 10*y^4 + +julia> reduce_with_quotients_and_unit(f, F) +([1], [x*y 10*x+1], x^4 + 10*x^3 + 1) + +julia> unit, M, res = reduce_with_quotients_and_unit(f, F, ordering=lex(R)) +([1], [x*y 0], x*y^4 + 10*y^4 + 1) + +julia> M * F + [res] == unit * [f] +true +``` +""" +function reduce_with_quotients(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + _, q, r = _reduce_with_quotients_and_unit(I, J, ordering, complete_reduction) + return q, r +end + +@doc raw""" + reduce_with_quotients(g::T, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(parent(g)), complete_reduction::Bool = false) where T <: MPolyRingElem + +If `ordering` is global, return the quotients and the remainder in a standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. +Otherwise, return the quotients and the remainder in a *weak* standard representation for `g` on division by the polynomials in `F` with respect to `ordering`. + + reduce_with_quotients(G::Vector{T}, F::Union{Vector{T}, IdealGens{T}}; + ordering::MonomialOrdering = default_ordering(parent(G[1])), complete_reduction::Bool = false) where T <: MPolyRingElem + +Return a `Vector` which contains, for each element `g` of `G`, quotients and a remainder as above. + +!!! note + The returned remainders are fully reduced if `complete_reduction` is set to `true` and `ordering` is global. + +# Examples + +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> f1 = x^2+x^2*y; f2 = y^3+x*y*z; f3 = x^3*y^2+z^4; + +julia> g = x^3*y+x^5+x^2*y^2*z^2+z^6; + +julia> Q, h = reduce_with_quotients(g, [f1,f2, f3], ordering = lex(R)); + +julia> h +x^5 - x^3 + y^6 + z^6 + +julia> g == Q[1]*f1+Q[2]*f2+Q[3]*f3+h +true +``` +""" +function reduce_with_quotients(f::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + isempty(F) && return zero_matrix(parent(f), 1, 0), f + J = IdealGens(parent(F[1]), F, ordering) + return reduce_with_quotients(f, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce_with_quotients(F::Vector{T}, G::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + @assert !isempty(F) + isempty(G) && return zero_matrix(parent(F[1]), length(F), 0), F + J = IdealGens(parent(G[1]), G, ordering) + return reduce_with_quotients(F, J; ordering=ordering, complete_reduction=complete_reduction) +end + +function reduce_with_quotients(f::T, F::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(f)), complete_reduction::Bool = false) where {T <: MPolyRingElem} + isempty(F) && return zero_matrix(parent(f), 1, 0), f + @assert parent(f) == parent(F[1]) + R = parent(f) + I = IdealGens(R, [f], ordering) + _, q, r = _reduce_with_quotients_and_unit(I, F, ordering, complete_reduction) + return q, r[1] +end + +function reduce_with_quotients(F::Vector{T}, G::IdealGens{T}; ordering::MonomialOrdering = default_ordering(parent(F[1])), complete_reduction::Bool = false) where {T <: MPolyRingElem} + @assert !isempty(F) + isempty(G) && return zero_matrix(parent(F[1]), length(F), 0), F + @assert parent(F[1]) == parent(G[1]) + R = parent(F[1]) + I = IdealGens(R, F, ordering) + _, q, r = _reduce_with_quotients_and_unit(I, G, ordering, complete_reduction) + return q, r +end + +function _reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens, ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = complete_reduction) + @assert base_ring(J) == base_ring(I) + sI = singular_generators(I, ordering) + sJ = singular_generators(J, ordering) + res = Singular.divrem2(sI, sJ, complete_reduction=complete_reduction) + return matrix(base_ring(I), res[3]), matrix(base_ring(I), res[1]), [J.gens.Ox(x) for x = gens(res[2])] +end + +@doc raw""" + normal_form(g::T, I::MPolyIdeal; + ordering::MonomialOrdering = default_ordering(base_ring(I))) where T <: MPolyRingElem + +Compute the normal form of `g` mod `I` with respect to `ordering`. + + normal_form(G::Vector{T}, I::MPolyIdeal; + ordering::MonomialOrdering = default_ordering(base_ring(I))) where T <: MPolyRingElem + +Return a `Vector` which contains for each element `g` of `G` a normal form as above. + +# Examples +```jldoctest +julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) +(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) + +julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) +Ideal generated by + b + c - 1 + 2*a*b + a*c + b - 1 + +julia> gens(groebner_basis(J)) +2-element Vector{QQMPolyRingElem}: + b + c - 1 + a*c - 2*a + c + +julia> normal_form(-1+c+b+a^3, J) +a^3 + +julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) +(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) + +julia> A = [-1+c+b+a^3,-1+b+c*a+2*a^3,5+c*b+c^2*a] +3-element Vector{QQMPolyRingElem}: + a^3 + b + c - 1 + 2*a^3 + a*c + b - 1 + a*c^2 + b*c + 5 + +julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) +Ideal generated by + b + c - 1 + 2*a*b + a*c + b - 1 + +julia> gens(groebner_basis(J)) +2-element Vector{QQMPolyRingElem}: + b + c - 1 + a*c - 2*a + c + +julia> normal_form(A, J) +3-element Vector{QQMPolyRingElem}: + a^3 + 2*a^3 + 2*a - 2*c + 4*a - 2*c^2 - c + 5 +``` +""" +function normal_form(f::T, J::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(J))) where { T <: MPolyRingElem } + res = normal_form([f], J, ordering = ordering) + + return res[1] +end + +function normal_form(A::Vector{T}, J::MPolyIdeal; ordering::MonomialOrdering=default_ordering(base_ring(J))) where { T <: MPolyRingElem } + @req is_exact_type(elem_type(base_ring(J))) "This functionality is only supported over exact fields." + if is_normal_form_f4_applicable(J, ordering) + res = _normal_form_f4(A, J) + else + res = _normal_form_singular(A, J, ordering) + end + + return res +end + +function is_normal_form_f4_applicable(I::MPolyIdeal, ordering::MonomialOrdering) + return (ordering == degrevlex(base_ring(I)) && !is_graded(base_ring(I)) + && ((coefficient_ring(I) isa FqField + && absolute_degree(coefficient_ring(I)) == 1 + && characteristic(coefficient_ring(I)) < 2^31))) +end + +@doc raw""" + _normal_form_f4(A::Vector{T}, J::MPolyIdeal) where { T <: MPolyRingElem } + +**Note**: Internal function, subject to change, do not use. + +Compute the normal form of the elements of `A` w.r.t. a +Gröbner basis of `J` and the monomial ordering `degrevlex` using the F4 Algorithm from AlgebraicSolving. + +CAVEAT: This computation needs a Gröbner basis of `J` and the monomial ordering +`ordering. If this Gröbner basis is not available, one is computed automatically. +This may take some time. This function only works in polynomial rings over prime fields +with the degree reverse lexicographical ordering. + +# Examples +```jldoctest +julia> R,(a,b,c) = polynomial_ring(GF(65521),[:a,:b,:c]) +(Multivariate polynomial ring in 3 variables over GF(65521), FqMPolyRingElem[a, b, c]) + +julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) +Ideal generated by + b + c + 65520 + 2*a*b + a*c + b + 65520 + +julia> A = [-1+c+b+a^3, -1+b+c*a+2*a^3, 5+c*b+c^2*a] +3-element Vector{FqMPolyRingElem}: + a^3 + b + c + 65520 + 2*a^3 + a*c + b + 65520 + a*c^2 + b*c + 5 + +julia> Oscar._normal_form_f4(A, J) +3-element Vector{FqMPolyRingElem}: + a^3 + 2*a^3 + 2*a + 65519*c + 4*a + 65519*c^2 + 65520*c + 5 +``` +""" +function _normal_form_f4(A::Vector{T}, J::MPolyIdeal) where { T <: MPolyRingElem } + if !haskey(J.gb, degrevlex(base_ring(J))) + groebner_basis_f4(J, complete_reduction = true) + end + + AJ = AlgebraicSolving.Ideal(J.gens.O) + AJ.gb[0] = oscar_groebner_generators(J, degrevlex(base_ring(J)), true) + + return AlgebraicSolving.normal_form(A, AJ) +end + +@doc raw""" + _normal_form_singular(A::Vector{T}, J::MPolyIdeal, ordering::MonomialOrdering) where { T <: MPolyRingElem } + +**Note**: Internal function, subject to change, do not use. + +Compute the normal form of the elements of `A` w.r.t. a +Gröbner basis of `J` and the monomial ordering `ordering` using Singular. + +CAVEAT: This computation needs a Gröbner basis of `J` and the monomial ordering +`ordering. If this Gröbner basis is not available, one is computed automatically. +This may take some time. + +# Examples +```jldoctest +julia> R,(a,b,c) = polynomial_ring(QQ,[:a,:b,:c]) +(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[a, b, c]) + +julia> J = ideal(R,[-1+c+b,-1+b+c*a+2*a*b]) +Ideal generated by + b + c - 1 + 2*a*b + a*c + b - 1 + +julia> gens(groebner_basis(J)) +2-element Vector{QQMPolyRingElem}: + b + c - 1 + a*c - 2*a + c + +julia> A = [-1+c+b+a^3, -1+b+c*a+2*a^3, 5+c*b+c^2*a] +3-element Vector{QQMPolyRingElem}: + a^3 + b + c - 1 + 2*a^3 + a*c + b - 1 + a*c^2 + b*c + 5 + +julia> Oscar._normal_form_singular(A, J, default_ordering(base_ring(J))) +3-element Vector{QQMPolyRingElem}: + a^3 + 2*a^3 + 2*a - 2*c + 4*a - 2*c^2 - c + 5 +``` +""" +function _normal_form_singular(A::Vector{T}, J::MPolyIdeal, ordering::MonomialOrdering) where { T <: MPolyRingElem } + GS = singular_groebner_generators(J, ordering) + SR = base_ring(GS) + tmp = map(SR, A) + IS = Singular.Ideal(SR, tmp) + K = reduce(IS, GS) + OR = base_ring(J) + return map(OR, gens(K)) +end + +@doc raw""" + is_standard_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) + +Tests if a given IdealGens `F` is a standard basis w.r.t. the given monomial ordering `ordering`. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> I = ideal(R,[x^2+y,x*y-y]) +Ideal generated by + x^2 + y + x*y - y + +julia> is_standard_basis(I.gens, ordering=neglex(R)) +false + +julia> standard_basis(I, ordering=neglex(R)) +Standard basis with elements + 1 -> y + 2 -> x^2 +with respect to the ordering + neglex([x, y]) + +julia> is_standard_basis(I.gb[neglex(R)], ordering=neglex(R)) +true +``` +""" +function is_standard_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) + @req is_exact_type(elem_type(base_ring(F))) "This functionality is only supported over exact fields." + if F.isGB && F.ord == ordering + return true + else + # Try to reduce all possible s-polynomials, i.e. Buchberger's criterion + R = base_ring(F) + for i in 1:length(F) + lt_i = leading_term(F[i], ordering=ordering) + for j in i+1:length(F) + lt_j = leading_term(F[j], ordering=ordering) + lcm_ij = lcm(lt_i, lt_j) + sp_ij = div(lcm_ij, lt_i) * F[i] - div(lcm_ij, lt_j) * F[j] + if reduce(IdealGens([sp_ij], ordering), F, ordering=ordering) != [R(0)] + return false + end + end + end + F.isGB = true + F.ord = ordering + return true + end +end + +@doc raw""" + is_groebner_basis(F::IdealGens; ordering::MonomialOrdering=default_ordering(base_ring(F))) + +Tests if a given IdealGens `F` is a Gröbner basis w.r.t. the given monomial ordering `ordering`. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> I = ideal(R,[x^2+y,x*y-y]) +Ideal generated by + x^2 + y + x*y - y + +julia> is_groebner_basis(I.gens, ordering=lex(R)) +false + +julia> groebner_basis(I, ordering=lex(R)) +Gröbner basis with elements + 1 -> y^2 + y + 2 -> x*y - y + 3 -> x^2 + y +with respect to the ordering + lex([x, y]) + +julia> is_groebner_basis(I.gb[lex(R)], ordering=lex(R)) +true +``` +""" +function is_groebner_basis(F::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(F))) + is_global(ordering) || error("Ordering must be global") + return is_standard_basis(F, ordering=ordering) +end diff --git a/src/Rings/groebner/transformation-matrix.jl b/src/Rings/groebner/transformation-matrix.jl new file mode 100644 index 000000000000..f6d392a77e1d --- /dev/null +++ b/src/Rings/groebner/transformation-matrix.jl @@ -0,0 +1,89 @@ +@doc raw""" + _compute_standard_basis_with_transform(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) + +**Note**: Internal function, subject to change, do not use. + +Given an `IdealGens` `B` and optional parameters `ordering` for a monomial ordering and `complete_reduction` +this function computes a standard basis (if `ordering` is a global monomial ordering and `complete_reduction = true` +the reduced Gröbner basis) of the ideal spanned by the elements in `B` w.r.t. the given monomial ordering `ordering` +and the transformation matrix from the ideal to the standard basis. Return value is a IdealGens together with a map. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) +(Multivariate polynomial ring in 2 variables over QQ, QQMPolyRingElem[x, y]) + +julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) +Ideal generating system with elements + 1 -> x*y - 3*x + 2 -> -2*x^2*y + y^3 + +julia> B,m = Oscar._compute_standard_basis_with_transform(A, degrevlex(R)) +(Ideal generating system with 3 elements with associated ordering degrevlex([x, y]), [1 2*x -2*x^2+y^2+3*y+9; 0 1 -x]) +``` +""" +function _compute_standard_basis_with_transform(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) + istd, m = Singular.lift_std(singular_generators(B, ordering), complete_reduction = complete_reduction) + return IdealGens(B.Ox, istd), map_entries(B.Ox, m) +end + +@doc raw""" + standard_basis_with_transformation_matrix(I::MPolyIdeal; + ordering::MonomialOrdering = default_ordering(base_ring(I)), + complete_reduction::Bool=false) + +Return a pair `G`, `T`, say, where `G` is a standard basis of `I` with respect to `ordering`, and `T` +is a transformation matrix from `gens(I)` to `G`. That is, `gens(I)*T == G`. + +!!! note + The returned Gröbner basis is reduced if `ordering` is a global monomial odering and `complete_reduction = true`. + +# Examples +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ,[:x,:y]); + +julia> I = ideal([x*y^2-1,x^3+y^2+x*y]); + +julia> G, T = standard_basis_with_transformation_matrix(I, ordering=neglex(R)) +(Standard basis with 1 element w.r.t. neglex([x, y]), [-1; 0]) + +julia> gens(I)*T == gens(G) +true +``` +""" +function standard_basis_with_transformation_matrix(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) + complete_reduction && @assert is_global(ordering) + G, m = _compute_standard_basis_with_transform(I.gens, ordering, complete_reduction) + G.isGB = true + I.gb[ordering] = G + return G, m +end + +@doc raw""" + groebner_basis_with_transformation_matrix(I::MPolyIdeal; + ordering::MonomialOrdering = default_ordering(base_ring(I)), + complete_reduction::Bool=false) + +Return a pair `G`, `T`, say, where `G` is a Gröbner basis of `I` with respect to `ordering`, and `T` +is a transformation matrix from `gens(I)` to `G`. That is, `gens(I)*T == G`. + +!!! note + The returned Gröbner basis is reduced if `complete_reduction = true`. + +# Examples +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ,[:x,:y]); + +julia> I = ideal([x*y^2-1,x^3+y^2+x*y]); + +julia> G, T = groebner_basis_with_transformation_matrix(I) +(Gröbner basis with 3 elements w.r.t. degrevlex([x, y]), [1 0 -x^2-y; 0 1 y^2]) + +julia> gens(I)*T == gens(G) +true +``` +""" +function groebner_basis_with_transformation_matrix(I::MPolyIdeal; ordering::MonomialOrdering = default_ordering(base_ring(I)), complete_reduction::Bool = false) + is_global(ordering) || error("Ordering must be global") + return standard_basis_with_transformation_matrix(I, ordering=ordering, complete_reduction=complete_reduction) +end From f9b517b7e129e97c35b3030de5f37fdfc702c736 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 29 Oct 2024 11:23:02 +0100 Subject: [PATCH 19/84] README.md: remove month in OSCAR book bib (#4252) --- CITATION.cff | 1 - README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index ae3fec3e3f8e..91973eafb428 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -41,7 +41,6 @@ preferred-citation: interested researchers from graduate students through established experts. year: 2024 - month: 8 edition: 1 issn: "1431-1550" url: "https://link.springer.com/book/9783031621260" diff --git a/README.md b/README.md index 3936e5575f81..afc3347279f7 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,6 @@ If you are using BibTeX, you can use the following BibTeX entries: volume = {32}, edition = {1}, url = {https://link.springer.com/book/9783031621260}, - month = {8}, issn = {1431-1550}, } From b056c72c1195ff663e745900e978deb07393d8bd Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Tue, 29 Oct 2024 13:50:17 +0100 Subject: [PATCH 20/84] Saturation for modules (#4249) * Saturation for modules * remove whitespace * Correction * reacting to the discussion --- .../subquotients.md | 11 +- .../src/CommutativeAlgebra/affine_algebras.md | 1 - src/Modules/UngradedModules/SubquoModule.jl | 281 +++++++++++++++++- 3 files changed, 276 insertions(+), 17 deletions(-) diff --git a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md index 695bf907fe85..5dfd40282bf0 100644 --- a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md +++ b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md @@ -363,9 +363,18 @@ quotient(M::SubquoModule{T}, N::SubquoModule{T}) where T ``` ```@docs -quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T +quotient(M::SubquoModule, J::Ideal) ``` +```@docs +saturation(M:: SubquoModule, J::Ideal) +``` + +```@docs +saturation_with_index(M:: SubquoModule, J::Ideal) +``` + + ## Submodules and Quotients ```@docs diff --git a/docs/src/CommutativeAlgebra/affine_algebras.md b/docs/src/CommutativeAlgebra/affine_algebras.md index 9e65ec7002be..4c48d54b1aa0 100644 --- a/docs/src/CommutativeAlgebra/affine_algebras.md +++ b/docs/src/CommutativeAlgebra/affine_algebras.md @@ -296,7 +296,6 @@ minimal_generating_set(I::MPolyQuoIdeal{<:MPolyDecRingElem}) ```@docs intersect(a::MPolyQuoIdeal{T}, bs::MPolyQuoIdeal{T}...) where T -intersect(V::Vector{MPolyQuoIdeal{T}}) where T ``` #### Ideal Quotients diff --git a/src/Modules/UngradedModules/SubquoModule.jl b/src/Modules/UngradedModules/SubquoModule.jl index e44268dc784a..6ac9ebeccc04 100644 --- a/src/Modules/UngradedModules/SubquoModule.jl +++ b/src/Modules/UngradedModules/SubquoModule.jl @@ -1779,8 +1779,7 @@ end (::Colon)(M::SubquoModule{T}, N::SubquoModule{T}) where T = quotient(M, N) @doc raw""" - quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T - + quotient(M::SubquoModule, J::Ideal) Return the quotient of `M` by `J`. @@ -1819,12 +1818,10 @@ Ideal generated by z^2 julia> L = quotient(M, J) -Subquotient of Submodule with 5 generators +Subquotient of Submodule with 3 generators 1 -> x*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -4 -> y*z^3*e[1] -5 -> y^2*z^2*e[1] +2 -> y*z^3*e[1] +3 -> y^2*z^2*e[1] by Submodule with 3 generators 1 -> x^2*e[1] 2 -> y^3*e[1] @@ -1833,32 +1830,286 @@ by Submodule with 3 generators julia> ambient_free_module(L) == ambient_free_module(M) true +``` + +```jldoctest +julia> S, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> R, _ = quo(S, ideal(S, [x+y+z])); + +julia> F = free_module(R, 1); + +julia> AM = R[x;]; + +julia> BM = R[x^2; y^3; z^4]; + +julia> M = SubquoModule(F, AM, BM) +Subquotient of Submodule with 1 generator +1 -> (-y - z)*e[1] +by Submodule with 3 generators +1 -> (y^2 + 2*y*z + z^2)*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> J = ideal(R, [x, y, z])^2 +Ideal generated by + x^2 + x*y + x*z + y^2 + y*z + z^2 + +julia> quotient(M, J) +Subquotient of Submodule with 2 generators +1 -> z*e[1] +2 -> y*e[1] +by Submodule with 3 generators +1 -> (y^2 + 2*y*z + z^2)*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + ``` """ -function quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T +function quotient(M::SubquoModule, J::Ideal) + @assert base_ring(M) == base_ring(J) + M_quo = isdefined(M, :quo) ? M.quo : SubModuleOfFreeModule(ambient_free_module(M), Vector{elem_type(ambient_free_module(M))}()) + U = M.sub+M.quo + UF = _quotient(U, J) + res = SubquoModule(UF) + res.quo = M.quo + return simplify_light(res)[1] +end + +(::Colon)(M::SubquoModule, J::Ideal) = quotient(M, N) + +function _quotient(U::SubModuleOfFreeModule, J::Ideal) ### TODO Replace by generic method error("not implemented for the given types of modules.") end -(::Colon)(M::SubquoModule{T}, J::MPolyIdeal{T}) where T = quotient(M, N) +function _quotient(U::SubModuleOfFreeModule, J::Ideal{T}) where T <: Union{MPolyRingElem, MPolyQuoRingElem} + F = ambient_free_module(U) + SgU = singular_generators(U.gens) + SgJ = singular_generators(J.gens) + SQ = Singular.quotient(SgU, SgJ) + MG = ModuleGens(F, SQ) + return SubModuleOfFreeModule(F, MG) +end -function quotient(M::SubquoModule{T}, J::MPolyIdeal{T}) where T <: MPolyRingElem +######################################## +### saturation for modules +######################################## + +@doc raw""" + saturation(M::SubquoModule, + J::Ideal = ideal(base_ring(M), gens(base_ring(M))); + iteration::Bool = false) + +Return the saturation $M:J^{\infty}$ of `M` with respect to `J`. + +If the ideal `J` is not given, the ideal generated by the generators (variables) of `base_ring(M)` is used. + +Setting `iteration` to `true` only has an effect over rings of type `MPolyRing` or `MPolyQuoRing`. Over such rings, +if `iteration` is set to `true`, the saturation is done by carrying out successive ideal quotient computations as +suggested by the definition of saturation. Otherwise, a more sophisticated Gröbner basis approach is used which is typically +faster. Applying the two approaches may lead to different generating sets of the saturation. + +!!! note + By definition, + $M:J^{\infty} = \{ a \in A \mid J^ka \subset M \text{ for some } k\geq 1 \} = \bigcup\limits_{k=1}^{\infty} (M:J^k).$ + Here, $A$ is the ambient module of $M$. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> F = free_module(R, 1); + +julia> AM = R[x;]; + +julia> BM = R[x^2; y^3; z^4]; + +julia> M = SubquoModule(F, AM, BM) +Subquotient of Submodule with 1 generator +1 -> x*e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> J = ideal(R, [x, y, z])^2 +Ideal generated by + x^2 + x*y + x*z + y^2 + y*z + z^2 + +julia> saturation(M, J) +Subquotient of Submodule with 1 generator +1 -> e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +``` +""" +function saturation(M::SubquoModule, J::Ideal = ideal(base_ring(M), gens(base_ring(M))); iteration::Bool = false) @assert base_ring(M) == base_ring(J) - F = ambient_free_module(M) + M_quo = isdefined(M, :quo) ? M.quo : SubModuleOfFreeModule(ambient_free_module(M), Vector{elem_type(ambient_free_module(M))}()) U = M.sub+M.quo + UF = _saturation(U, J; iteration = iteration) + res = SubquoModule(UF) + res.quo = M.quo + return simplify_light(res)[1] +end + +function _saturation(U::SubModuleOfFreeModule, J::Ideal) ### TODO Replace by generic method + error("not implemented for the given types of modules.") +end + +function _saturation(U::SubModuleOfFreeModule, J::Ideal{T}; iteration::Bool = false) where T <: Union{MPolyRingElem, MPolyQuoRingElem} + F = ambient_free_module(U) SgU = singular_generators(U.gens) - singular_assure(J) - SQ = Singular.quotient(SgU, J.gens.S) + SgJ = singular_generators(J.gens) + SQ, _ = Singular.saturation(SgU, SgJ) MG = ModuleGens(F, SQ) - UF = SubModuleOfFreeModule(F, MG) + return SubModuleOfFreeModule(F, MG) +end + +@doc raw""" + saturation_with_index(M::SubquoModule, + J::MPolyIdeal = ideal(base_ring(M), gens(base_ring(M))); + iteration::Bool = false) + + +Return the saturation $M:J^{\infty}$ of $M$ with respect to $J$ and the smallest integer $k$ such that $I:J^k = I:J^{\infty}$ (saturation index). + +If the ideal `J` is not given, the ideal generated by the generators (variables) of `base_ring(M)` is used. + +Setting `iteration` to `true` only has an effect over rings of type `MPolyRing` or `MPolyQuoRing`. Over such rings, +if `iteration` is set to `true`, the saturation is done by carrying out successive module quotient computations as +suggested by the definition of saturation. Otherwise, a more sophisticated Gröbner basis approach is used which is typically +faster. Applying the two approaches may lead to different generating sets of the saturation. + +!!! note + By definition, + $M:J^{\infty} = \{ a \in A \mid J^ka \subset M \text{ for some } k\geq 1 \} = \bigcup\limits_{k=1}^{\infty} (M:J^k).$ + Here, $A$ is the ambient module of $M$. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> F = free_module(R, 1); + +julia> AM = R[x;]; + +julia> BM = R[x^2; y^3; z^4]; + +julia> M = SubquoModule(F, AM, BM) +Subquotient of Submodule with 1 generator +1 -> x*e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> J = ideal(R, [x, y, z])^2 +Ideal generated by + x^2 + x*y + x*z + y^2 + y*z + z^2 + +julia> L = saturation_with_index(M, J); + +julia> L[1] +Subquotient of Submodule with 1 generator +1 -> e[1] +by Submodule with 3 generators +1 -> x^2*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> L[2] +3 + +``` + +```jldoctest +julia> S, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); + +julia> R, _ = quo(S, ideal(S, [x+y+z])); + +julia> F = free_module(R, 1); + +julia> AM = R[x;]; + +julia> BM = R[x^2; y^3; z^4]; + +julia> M = SubquoModule(F, AM, BM) +Subquotient of Submodule with 1 generator +1 -> (-y - z)*e[1] +by Submodule with 3 generators +1 -> (y^2 + 2*y*z + z^2)*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> J = ideal(R, [x, y, z])^2 +Ideal generated by + x^2 + x*y + x*z + y^2 + y*z + z^2 + +julia> L = saturation_with_index(M, J); + +julia> L[1] +Subquotient of Submodule with 1 generator +1 -> e[1] +by Submodule with 3 generators +1 -> (y^2 + 2*y*z + z^2)*e[1] +2 -> y^3*e[1] +3 -> z^4*e[1] + +julia> L[2] +2 + +``` +""" +function saturation_with_index(M::SubquoModule, J::Ideal = ideal(base_ring(M), gens(base_ring(M))); iteration::Bool = false) +@assert base_ring(M) == base_ring(J) + M_quo = isdefined(M, :quo) ? M.quo : SubModuleOfFreeModule(ambient_free_module(M), Vector{elem_type(ambient_free_module(M))}()) + U = M.sub+M.quo + UF, k = _saturation_with_index(U, J; iteration = iteration) res = SubquoModule(UF) res.quo = M.quo - return res + return simplify_light(res)[1], k end +function _saturation_with_index(U::SubModuleOfFreeModule, J::Ideal) ### TODO Replace by generic method + error("not implemented for the given types of modules.") +end +function _saturation_with_index(U::SubModuleOfFreeModule, J::Ideal{T}; iteration::Bool = false) where T <: Union{MPolyRingElem, MPolyQuoRingElem} + F = ambient_free_module(U) + SgU = singular_generators(U.gens) + SgJ = singular_generators(J.gens) + SQ, k = Singular.saturation(SgU, SgJ) + MG = ModuleGens(F, SQ) + return SubModuleOfFreeModule(F, MG), k +end ######################################## + @doc raw""" represents_element(a::FreeModElem, SQ::SubquoModule) From 84ad20862d4abe9c1d3a36ded3a79c310a5d957e Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 29 Oct 2024 19:08:41 +0100 Subject: [PATCH 21/84] Yet another attempt to revise printing for modules (#4251) * Revise printing of modules * Adjust test case in test/Modules/ExteriorPowers.jl for new printing * update jldoctest * Also adjust printing of ideals * update doctests * Also update README.md * Update code snippet, change to jldoctest * Update code snippets which are not doctests * Update booktests --- README.md | 38 +- .../GroebnerBases/groebner_bases_integers.md | 10 +- .../ModulesOverMultivariateRings/complexes.md | 24 +- .../subquotients.md | 68 +- experimental/GroebnerWalk/src/common.jl | 14 +- experimental/Schemes/src/Tjurina.jl | 22 +- .../Schemes/Sheaves/CoherentSheaves.jl | 16 +- .../AdjunctionProcess/AdjunctionProcess.jl | 18 +- src/Modules/ModuleTypes.jl | 16 +- src/Modules/ModulesGraded.jl | 161 +-- src/Modules/UngradedModules/FreeMod.jl | 25 +- src/Modules/UngradedModules/FreeModuleHom.jl | 190 ++-- .../UngradedModules/FreeResolutions.jl | 52 +- src/Modules/UngradedModules/Hom_and_ext.jl | 56 +- .../UngradedModules/HomologicalAlgebra.jl | 118 +-- src/Modules/UngradedModules/Methods.jl | 8 +- src/Modules/UngradedModules/ModuleGens.jl | 2 +- src/Modules/UngradedModules/Presentation.jl | 203 ++-- .../UngradedModules/SubModuleOfFreeModule.jl | 16 +- src/Modules/UngradedModules/SubQuoHom.jl | 404 ++++---- src/Modules/UngradedModules/SubquoModule.jl | 967 +++++++++--------- .../UngradedModules/SubquoModuleElem.jl | 89 +- src/Modules/homological-algebra.jl | 116 +-- src/Rings/FreeAssociativeAlgebraIdeal.jl | 8 +- src/Rings/groebner/f4.jl | 8 +- src/Rings/groebner/fglm.jl | 40 +- src/Rings/groebner/general.jl | 36 +- src/Rings/groebner/generators.jl | 6 +- src/Rings/groebner/modular.jl | 6 +- src/Rings/groebner/reduce.jl | 16 +- src/Rings/groebner/transformation-matrix.jl | 4 +- src/Rings/mpoly-affine-algebras.jl | 6 +- src/Rings/mpoly.jl | 12 +- src/TropicalGeometry/groebner_fan.jl | 4 +- test/Modules/ExteriorPowers.jl | 4 +- .../algebraic-geometry/circlepar.jlcon | 12 +- .../algebraic-geometry/ex11.jlcon | 6 +- .../algebraic-geometry/ex314.jlcon | 60 +- .../algebraic-geometry/exres.jlcon | 38 +- .../bertin.jlcon | 16 +- .../gbeliminate.jlcon | 14 +- .../eder-mohr-ideal-theoretic/lcis.jlcon | 22 +- 42 files changed, 1404 insertions(+), 1547 deletions(-) diff --git a/README.md b/README.md index afc3347279f7..7e6dc79ccc37 100644 --- a/README.md +++ b/README.md @@ -70,34 +70,28 @@ Free module of rank 1 over R julia> s = sub(F, [f*F[1]])[1] Submodule with 1 generator -1 -> (x1^2 + x2)*e[1] -represented as subquotient with no relations. + 1: (x1^2 + x2)*e[1] +represented as subquotient with no relations julia> H, mH = hom(s, quo(F, s)[1]) -(hom of (s, Subquotient of -1 -> e[1] -by -1 -> (x1^2 + x2)*e[1]), Map: H -> set of all homomorphisms from s to subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 1 generator -1 -> (x1^2 + x2)*e[1]) +(hom of (s, Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 1 generator + 1: (x1^2 + x2)*e[1]), Map: H -> set of all homomorphisms from s to subquotient of submodule with 1 generator + 1: e[1] +by submodule with 1 generator + 1: (x1^2 + x2)*e[1]) julia> mH(H[1]) -Map with following data -Domain: -======= -Submodule with 1 generator -1 -> (x1^2 + x2)*e[1] -represented as subquotient with no relations. -Codomain: -========= -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 1 generator -1 -> (x1^2 + x2)*e[1] +Module homomorphism + from s + to subquotient of submodule with 1 generator + 1: e[1] + by submodule with 1 generator + 1: (x1^2 + x2)*e[1] ``` -Of course, the cornerstones are also available directly: +Of course, the cornerstones are also available directly. For example: ``` julia> C = Polymake.polytope.cube(3); diff --git a/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases_integers.md b/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases_integers.md index 8fdb31cd4611..5bc623d2c42c 100644 --- a/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases_integers.md +++ b/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases_integers.md @@ -60,11 +60,11 @@ Ideal generated by julia> G = groebner_basis(I, ordering = lex(R)) Gröbner basis with elements - 1 -> 28*y^3 - 35*y - 2 -> 4*x*y^2 - 5*x - 3 -> 15*x^2 + 28*y^2 - 4 -> 3*x^2*y + 7*y - 5 -> x^2*y^2 - 5*x^2 - 7*y^2 + 1: 28*y^3 - 35*y + 2: 4*x*y^2 - 5*x + 3: 15*x^2 + 28*y^2 + 4: 3*x^2*y + 7*y + 5: x^2*y^2 - 5*x^2 - 7*y^2 with respect to the ordering lex([x, y]) ``` diff --git a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/complexes.md b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/complexes.md index a3735244c6ab..0a2f3b20922d 100644 --- a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/complexes.md +++ b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/complexes.md @@ -50,25 +50,15 @@ julia> range(C) 5:-1:3 julia> C[5] -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 1 generator -1 -> x^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 1 generator + 1: x^4*e[1] julia> delta = map(C, 5) -Map with following data -Domain: -======= -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 1 generator -1 -> x^4*e[1] -Codomain: -========= -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 1 generator -1 -> x^3*e[1] +Module homomorphism + from A + to B julia> matrix(delta) [x^2] diff --git a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md index 5dfd40282bf0..45ee02b52e85 100644 --- a/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md +++ b/docs/src/CommutativeAlgebra/ModulesOverMultivariateRings/subquotients.md @@ -96,13 +96,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> base_ring(M) Multivariate polynomial ring in 3 variables x, y, z @@ -134,12 +134,12 @@ julia> relations(M) z^4*e[1] julia> ambient_module(M) -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` In the graded case, we also have: @@ -191,13 +191,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> m = M(sparse_row(R, [(1,z),(2,one(R))])) (x*z + y)*e[1] @@ -247,25 +247,25 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> m = z*M[1] + M[2] (x*z + y)*e[1] julia> parent(m) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> coordinates(m) Sparse row with positions [1, 2] and values QQMPolyRingElem[z, 1] diff --git a/experimental/GroebnerWalk/src/common.jl b/experimental/GroebnerWalk/src/common.jl index bf19b1053ce4..6f4d08f99920 100644 --- a/experimental/GroebnerWalk/src/common.jl +++ b/experimental/GroebnerWalk/src/common.jl @@ -30,15 +30,15 @@ julia> I = ideal([y^4+ x^3-x^2+x,x^4]); julia> groebner_walk(I, lex(R)) Gröbner basis with elements - 1 -> x + y^12 - y^8 + y^4 - 2 -> y^16 + 1: x + y^12 - y^8 + y^4 + 2: y^16 with respect to the ordering lex([x, y]) julia> groebner_walk(I, lex(R); algorithm=:generic) Gröbner basis with elements - 1 -> y^16 - 2 -> x + y^12 - y^8 + y^4 + 1: y^16 + 2: x + y^12 - y^8 + y^4 with respect to the ordering lex([x, y]) @@ -46,15 +46,15 @@ julia> set_verbosity_level(:groebner_walk, 1); julia> groebner_walk(I, lex(R)) Results for standard_walk -Crossed Cones in: +Crossed Cones in: ZZRingElem[1, 1] ZZRingElem[4, 3] ZZRingElem[4, 1] ZZRingElem[12, 1] Cones crossed: 4 Gröbner basis with elements - 1 -> x + y^12 - y^8 + y^4 - 2 -> y^16 + 1: x + y^12 - y^8 + y^4 + 2: y^16 with respect to the ordering lex([x, y]) diff --git a/experimental/Schemes/src/Tjurina.jl b/experimental/Schemes/src/Tjurina.jl index 91e20575a4f3..48c5d5bae723 100644 --- a/experimental/Schemes/src/Tjurina.jl +++ b/experimental/Schemes/src/Tjurina.jl @@ -769,17 +769,17 @@ Spectrum at complement of maximal ideal of point (0, 0, 0) julia> T = tjurina_module(X) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 7 generators -1 -> 2*x*e[1] + y*e[2] -2 -> 2*y*e[1] + x*e[2] -3 -> -2*z*e[1] -4 -> (x^2 + y^2 - z^2)*e[1] -5 -> (x^2 + y^2 - z^2)*e[2] -6 -> x*y*e[1] -7 -> x*y*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 7 generators + 1: 2*x*e[1] + y*e[2] + 2: 2*y*e[1] + x*e[2] + 3: -2*z*e[1] + 4: (x^2 + y^2 - z^2)*e[1] + 5: (x^2 + y^2 - z^2)*e[2] + 6: x*y*e[1] + 7: x*y*e[2] julia> vector_space_basis(T) 5-element Vector{Any}: diff --git a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl index 57f50b262b50..2bf4fb32bdc8 100644 --- a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl +++ b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl @@ -264,10 +264,10 @@ Coherent sheaf of modules 3: [(s0//s2), (s1//s2), (s3//s2)] affine 3-space 4: [(s0//s3), (s1//s3), (s2//s3)] affine 3-space with restrictions - 1: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 2: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 3: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 4: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ + 1: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 2: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 3: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 4: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ ``` """ function twisting_sheaf(IP::AbsProjectiveScheme{<:Field}, d::Int) @@ -325,10 +325,10 @@ Coherent sheaf of modules 3: [(s0//s2), (s1//s2), (s3//s2)] affine 3-space 4: [(s0//s3), (s1//s3), (s2//s3)] affine 3-space with restrictions - 1: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 2: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 3: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ - 4: free module of rank 1 over Multivariate polynomial ring in 3 variables over QQ + 1: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 2: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 3: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ + 4: free module of rank 1 over multivariate polynomial ring in 3 variables over QQ ``` """ function tautological_bundle(IP::AbsProjectiveScheme{<:Field}) diff --git a/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl b/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl index 872035f826e5..3e5dd6aa8118 100644 --- a/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl +++ b/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl @@ -141,18 +141,17 @@ Given a smooth projective variety `X`, return a module whose sheafification is t # Examples -``` +```jldoctest julia> R, x = graded_polynomial_ring(QQ, :x => (1:6)); julia> I = ideal(R, [x[1]*x[6] - x[2]*x[5] + x[3]*x[4]]); -julia> GRASSMANNIAN = variety(I); +julia> GRASSMANNIAN = variety(I); julia> Omega = canonical_bundle(GRASSMANNIAN) -Graded subquotient of submodule of R^1 generated by -1 -> e[1] -by submodule of R^1 generated by -1 -> (x[1]*x[6] - x[2]*x[5] + x[3]*x[4])*e[1] +Graded submodule of R^1 with 1 generator + 1: e[1] +represented as subquotient with no relations julia> degrees_of_generators(Omega) 1-element Vector{FinGenAbGroupElem}: @@ -167,10 +166,9 @@ julia> I = ideal(R, [y^2*z + x*y*z - x^3 - x*z^2 - z^3]); julia> ELLCurve = variety(I); julia> Omega = canonical_bundle(ELLCurve) -Graded subquotient of submodule of R^1 generated by -1 -> e[1] -by submodule of R^1 generated by -1 -> (x^3 - x*y*z + x*z^2 - y^2*z + z^3)*e[1] +Graded submodule of R^1 with 1 generator + 1: e[1] +represented as subquotient with no relations julia> degrees_of_generators(Omega) 1-element Vector{FinGenAbGroupElem}: diff --git a/src/Modules/ModuleTypes.jl b/src/Modules/ModuleTypes.jl index 422e6021a1da..15f803c5bd59 100644 --- a/src/Modules/ModuleTypes.jl +++ b/src/Modules/ModuleTypes.jl @@ -294,14 +294,14 @@ julia> B = R[x^2; x*y; y^2; z^4] [z^4] julia> M = SubquoModule(A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 4 generators -1 -> x^2*e[1] -2 -> x*y*e[1] -3 -> y^2*e[1] -4 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 4 generators + 1: x^2*e[1] + 2: x*y*e[1] + 3: y^2*e[1] + 4: z^4*e[1] julia> f = SubquoModuleElem(sparse_row(R, [(1,z),(2,one(R))]),M) (x*z + y)*e[1] diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 08cf10fda0a7..9f53a64d670e 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -758,11 +758,13 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]] z*e[2] julia> a = hom(F, G, V) -F -> G -e[1] -> y*e[1] -e[2] -> x*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F + to G +defined by + e[1] -> y*e[1] + e[2] -> x*e[1] + y*e[2] + e[3] -> z*e[2] julia> degree(a) [1] @@ -832,11 +834,13 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]] z*e[2] julia> a = hom(F, G, V) -F -> G -e[1] -> y*e[1] -e[2] -> x*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F + to G +defined by + e[1] -> y*e[1] + e[2] -> x*e[1] + y*e[2] + e[3] -> z*e[2] julia> is_graded(a) true @@ -893,11 +897,13 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]] z*e[2] julia> a = hom(F, G, V) -F -> G -e[1] -> y*e[1] -e[2] -> x*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F + to G +defined by + e[1] -> y*e[1] + e[2] -> x*e[1] + y*e[2] + e[3] -> z*e[2] julia> is_graded(a) true @@ -935,11 +941,13 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]] z*e[2] julia> a = hom(F, G, V) -F -> G -e[1] -> y*e[1] -e[2] -> x*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F + to G +defined by + e[1] -> y*e[1] + e[2] -> x*e[1] + y*e[2] + e[3] -> z*e[2] julia> is_homogeneous(a) false @@ -1258,10 +1266,12 @@ julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> degree(a) [2] @@ -1337,10 +1347,12 @@ julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> grading_group(a) Z @@ -1375,10 +1387,12 @@ julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> is_homogeneous(a) false @@ -2092,8 +2106,9 @@ function show(io::IO, F::FreeMod_dec) @show_name(io, F) @show_special(io, F) + io = terse(io) print(io, "Decorated free module of rank $(rank(F)) over ") - print(IOContext(io, :compact =>true), base_ring(F)) + print(IOContext(io, :compact => true), base_ring(F)) i = 1 while i < dim(F) @@ -2718,21 +2733,21 @@ e[1] julia> MT = truncate(M, 3); julia> MT[1] -Graded subquotient of submodule of F generated by -1 -> z^3*e[1] -2 -> y*z^2*e[1] -3 -> y^2*z*e[1] -4 -> y^3*e[1] -5 -> x*z^2*e[1] -6 -> x*y*z*e[1] -7 -> x*y^2*e[1] -8 -> x^2*z*e[1] -9 -> x^2*y*e[1] -10 -> x^3*e[1] -by submodule of F generated by -1 -> x*e[1] -2 -> y^4*e[1] -3 -> z^5*e[1] +Graded subquotient of graded submodule of F with 10 generators + 1: z^3*e[1] + 2: y*z^2*e[1] + 3: y^2*z*e[1] + 4: y^3*e[1] + 5: x*z^2*e[1] + 6: x*y*z*e[1] + 7: x*y^2*e[1] + 8: x^2*z*e[1] + 9: x^2*y*e[1] + 10: x^3*e[1] +by graded submodule of F with 3 generators + 1: x*e[1] + 2: y^4*e[1] + 3: z^5*e[1] ``` """ function truncate(I::ModuleFP, g::FinGenAbGroupElem, task::Symbol=:with_morphism) @@ -2811,14 +2826,12 @@ julia> R, (x, y, z) = graded_polynomial_ring(QQ, [:x, :y, :z]); julia> F = graded_free_module(R, 1); julia> M, _ = quo(F, [x^2*F[1], y^2*F[1], z^2*F[1]]) -(Graded subquotient of submodule of F generated by -1 -> e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^2*e[1] -3 -> z^2*e[1], F -> M -e[1] -> e[1] -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of F with 1 generator + 1: e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^2*e[1] + 3: z^2*e[1], Hom: F -> M) julia> cm_regularity(M) 3 @@ -2890,11 +2903,11 @@ julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]); julia> IR = ideal(R, [x^2, y^3]); julia> quotient_ring_as_module(IR) -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 2 generators -1 -> x^2*e[1] -2 -> y^3*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 2 generators + 1: x^2*e[1] + 2: y^3*e[1] julia> base_ring(ans) Multivariate polynomial ring in 2 variables x, y @@ -2905,11 +2918,11 @@ julia> A, _ = quo(R, ideal(R,[x*y])); julia> AI = ideal(A, [x^2, y^3]); julia> quotient_ring_as_module(AI) -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 2 generators -1 -> x^2*e[1] -2 -> y^3*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 2 generators + 1: x^2*e[1] + 2: y^3*e[1] julia> base_ring(ans) Quotient @@ -2927,11 +2940,11 @@ Ideal generated by y^3 julia> quotient_ring_as_module(I) -Graded subquotient of submodule of S^1 generated by -1 -> e[1] -by submodule of S^1 generated by -1 -> x^2*e[1] -2 -> y^3*e[1] +Graded subquotient of graded submodule of S^1 with 1 generator + 1: e[1] +by graded submodule of S^1 with 2 generators + 1: x^2*e[1] + 2: y^3*e[1] ``` """ @@ -2964,9 +2977,9 @@ Ideal generated by julia> ideal_as_module(I) Submodule with 2 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -represented as subquotient with no relations. + 1: x^2*e[1] + 2: y^3*e[1] +represented as subquotient with no relations ``` ```jldoctest julia> S, (x, y) = graded_polynomial_ring(QQ, [:x, :y]); @@ -2977,9 +2990,9 @@ Ideal generated by y^3 julia> ideal_as_module(I) -Graded submodule of S^1 -1 -> x^2*e[1] -2 -> y^3*e[1] +Graded submodule of S^1 with 2 generators + 1: x^2*e[1] + 2: y^3*e[1] represented as subquotient with no relations ``` """ @@ -3020,16 +3033,16 @@ Ideal generated by 0 julia> M = quotient_ring_as_module(I) -Graded submodule of R^1 -1 -> e[1] +Graded submodule of R^1 with 1 generator + 1: e[1] represented as subquotient with no relations julia> degree(gen(M, 1)) [0] julia> N = twist(M, 2) -Graded submodule of R^1 -1 -> e[1] +Graded submodule of R^1 with 1 generator + 1: e[1] represented as subquotient with no relations julia> degree(gen(N, 1)) diff --git a/src/Modules/UngradedModules/FreeMod.jl b/src/Modules/UngradedModules/FreeMod.jl index 151225e454e2..8aff69ed7568 100644 --- a/src/Modules/UngradedModules/FreeMod.jl +++ b/src/Modules/UngradedModules/FreeMod.jl @@ -49,7 +49,7 @@ julia> U = complement_of_prime_ideal(P); julia> RL, _ = localization(R, U); julia> FRL = free_module(RL, 2, "f") -Free module of rank 2 over Localization of R at complement of prime ideal (x, y, z) +Free module of rank 2 over localization of R at complement of prime ideal (x, y, z) julia> RL(x)*FRL[1] x*f[1] @@ -65,7 +65,7 @@ x*g[1] julia> RQL, _ = localization(RQ, U); julia> FRQL = free_module(RQL, 2, "h") -Free module of rank 2 over Localization of RQ at complement of prime ideal +Free module of rank 2 over localization of RQ at complement of prime ideal julia> RQL(x)*FRQL[1] x*h[1] @@ -108,8 +108,9 @@ end function show(io::IO, F::FreeMod) @show_name(io, F) @show_special(io, F) + io = pretty(io) compact = get(io, :compact, false) - io_compact = IOContext(io, :compact => true) + io = IOContext(io, :compact => true) if is_graded(F) if !compact print(io, "Graded free module ") @@ -121,8 +122,8 @@ function show(io::IO, F::FreeMod) while i+j <= dim(F) && d == F.d[i+j] j += 1 end - print(io_compact, base_ring(F), "^$j") - print(io_compact, "(", -d, ")") + print(io, base_ring(F), "^$j") + print(io, "(", -d, ")") if i+j <= dim(F) print(io, " + ") end @@ -130,21 +131,21 @@ function show(io::IO, F::FreeMod) end if rank(F)==0 - print(io_compact, base_ring(F), "^0") + print(io, base_ring(F), "^0") end if !compact - print(io," of rank $(rank(F)) over ") - print(io_compact, base_ring(F)) + print(io," of rank $(rank(F)) over ", Lowercase()) + print(io, base_ring(F)) end else if !compact #Todo: Use once the printing of rings is fixed - #print(io_compact, "Free module ", base_ring(F), "^$(F.n) of rank $(F.n) over ") - print(io_compact, "Free module of rank $(F.n) over ") - print(io_compact, F.R) + #print(io, "Free module ", base_ring(F), "^$(F.n) of rank $(F.n) over ") + print(io, "Free module of rank $(rank(F)) over ", Lowercase()) + print(io, F.R) else - print(io_compact, base_ring(F), "^$(F.n)") + print(io, base_ring(F), "^$(rank(F))") end end end diff --git a/src/Modules/UngradedModules/FreeModuleHom.jl b/src/Modules/UngradedModules/FreeModuleHom.jl index eb90491055c3..9e725f0e9b26 100644 --- a/src/Modules/UngradedModules/FreeModuleHom.jl +++ b/src/Modules/UngradedModules/FreeModuleHom.jl @@ -113,13 +113,9 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]] z*e[2] julia> a = hom(F, G, V) -Map with following data -Domain: -======= -Free module of rank 3 over R -Codomain: -========= -Free module of rank 2 over R +Module homomorphism + from F + to G julia> a(F[2]) x*e[1] + y*e[2] @@ -130,13 +126,9 @@ julia> B = R[y 0; x y; 0 z] [0 z] julia> b = hom(F, G, B) -Map with following data -Domain: -======= -Free module of rank 3 over R -Codomain: -========= -Free module of rank 2 over R +Module homomorphism + from F + to G julia> a == b true @@ -158,11 +150,13 @@ julia> V1 = [y*G1[1], (x+y)*G1[1]+y*G1[2], z*G1[2]] z*e[2] julia> a1 = hom(F1, G1, V1) -F1 -> G1 -e[1] -> y*e[1] -e[2] -> (x + y)*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F1 + to G1 +defined by + e[1] -> y*e[1] + e[2] -> (x + y)*e[1] + y*e[2] + e[3] -> z*e[2] julia> F2 = graded_free_module(Rg, [1,1,1]) Graded free module Rg^3([-1]) of rank 3 over Rg @@ -177,11 +171,13 @@ julia> V2 = [y*G2[1], (x+y)*G2[1]+y*G2[2], z*G2[2]] z*e[2] julia> a2 = hom(F2, G2, V2) -F2 -> G2 -e[1] -> y*e[1] -e[2] -> (x + y)*e[1] + y*e[2] -e[3] -> z*e[2] Homogeneous module homomorphism + from F2 + to G2 +defined by + e[1] -> y*e[1] + e[2] -> (x + y)*e[1] + y*e[2] + e[3] -> z*e[2] julia> B = Rg[y 0; x+y y; 0 z] [ y 0] @@ -189,11 +185,13 @@ julia> B = Rg[y 0; x+y y; 0 z] [ 0 z] julia> b = hom(F2, G2, B) -F2 -> G2 -e[1] -> y*e[1] -e[2] -> (x + y)*e[1] + y*e[2] -e[3] -> z*e[2] Homogeneous module homomorphism + from F2 + to G2 +defined by + e[1] -> y*e[1] + e[2] -> (x + y)*e[1] + y*e[2] + e[3] -> z*e[2] julia> a2 == b true @@ -277,40 +275,42 @@ function morphism_type( end function Base.show(io::IO, ::MIME"text/plain", fmh::FreeModuleHom{T1, T2, RingMapType}) where {T1 <: AbstractFreeMod, T2 <: ModuleFP, RingMapType} - # HACK - show(io, fmh) + println(terse(io), fmh) + io = pretty(io) + io_compact = IOContext(io, :compact => true) + println(io_compact, Indent(), "from ", Lowercase(), domain(fmh)) + print(io_compact, "to ", Lowercase(), codomain(fmh), Dedent()) + + if is_graded(fmh) + println(io) + print(io, "defined by", Indent()) + io = terse(io) + domain_gens = gens(domain(fmh)) + for g in domain_gens + println(io) + print(io, g, " -> ", fmh(g)) + end + end end function Base.show(io::IO, fmh::FreeModuleHom{T1, T2, RingMapType}) where {T1 <: AbstractFreeMod, T2 <: ModuleFP, RingMapType} - compact = get(io, :compact, false) - io_compact = IOContext(io, :compact => true) - if is_graded(fmh) - print(io_compact, domain(fmh)) - print(io, " -> ") - print(io_compact, codomain(fmh)) - if !compact - print(io, "\n") - for i in 1:ngens(domain(fmh)) - print(io, domain(fmh)[i], " -> ") - print(io_compact, fmh(domain(fmh)[i])) - print(io, "\n") - end + if is_terse(io) + if is_graded(fmh) A = grading_group(fmh) if degree(fmh) == A[0] print(io, "Homogeneous module homomorphism") else - print(io_compact, "Graded module homomorphism of degree ", degree(fmh)) - print(io, "\n") + print(io, "Graded module homomorphism of degree ", degree(fmh)) end + else + print(io, "Module homomorphism") end else - println(io, "Map with following data") - println(io, "Domain:") - println(io, "=======") - println(io, domain(fmh)) - println(io, "Codomain:") - println(io, "=========") - print(io, codomain(fmh)) + io = pretty(io) + print(io, "Hom: ") + io = terse(io) + print(io, Lowercase(), domain(fmh), " -> ") + print(io, Lowercase(), codomain(fmh)) end end @@ -335,13 +335,9 @@ julia> V, f = hom(F1, F2) (hom of (F1, F2), Map: V -> set of all homomorphisms from F1 to F2) julia> f(V[1]) -Map with following data -Domain: -======= -Free module of rank 3 over R -Codomain: -========= -Free module of rank 2 over R +Module homomorphism + from F1 + to F2 ``` @@ -358,11 +354,13 @@ julia> V, f = hom(F1, F2) (hom of (F1, F2), Map: V -> set of all homomorphisms from F1 to F2) julia> f(V[1]) -F1 -> F2 -e[1] -> e[1] -e[2] -> 0 -e[3] -> 0 Graded module homomorphism of degree [2] + from F1 + to F2 +defined by + e[1] -> e[1] + e[2] -> 0 + e[3] -> 0 ``` """ @@ -427,16 +425,10 @@ julia> a = hom(F, G, V); julia> kernel(a) (Submodule with 1 generator -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations., Map with following data -Domain: -======= -Submodule with 1 generator -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations. -Codomain: -========= -Free module of rank 3 over R) + 1: x*z*e[1] - y*z*e[2] + y^2*e[3] +represented as subquotient with no relations, Hom: submodule with 1 generator + 1: x*z*e[1] - y*z*e[2] + y^2*e[3] +represented as subquotient with no relations -> F) ``` ```jldoctest @@ -451,13 +443,11 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]]; julia> a = hom(F, G, V); julia> kernel(a) -(Graded submodule of F -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations, Graded submodule of F -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations -> F -x*z*e[1] - y*z*e[2] + y^2*e[3] -> x*z*e[1] - y*z*e[2] + y^2*e[3] -Homogeneous module homomorphism) +(Graded submodule of F with 1 generator + 1: x*z*e[1] - y*z*e[2] + y^2*e[3] +represented as subquotient with no relations, Hom: graded submodule of F with 1 generator + 1: x*z*e[1] - y*z*e[2] + y^2*e[3] +represented as subquotient with no relations -> F) ``` """ @@ -560,20 +550,14 @@ julia> a = hom(F, G, V); julia> image(a) (Submodule with 3 generators -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations., Map with following data -Domain: -======= -Submodule with 3 generators -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations. -Codomain: -========= -Free module of rank 2 over R) + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] +represented as subquotient with no relations, Hom: submodule with 3 generators + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] +represented as subquotient with no relations -> G) ``` ```jldoctest @@ -588,19 +572,15 @@ julia> V = [y*G[1], x*G[1]+y*G[2], z*G[2]]; julia> a = hom(F, G, V); julia> image(a) -(Graded submodule of G -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations, Graded submodule of G -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations -> G -y*e[1] -> y*e[1] -x*e[1] + y*e[2] -> x*e[1] + y*e[2] -z*e[2] -> z*e[2] -Homogeneous module homomorphism) +(Graded submodule of G with 3 generators + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] +represented as subquotient with no relations, Hom: graded submodule of G with 3 generators + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] +represented as subquotient with no relations -> G) ``` """ diff --git a/src/Modules/UngradedModules/FreeResolutions.jl b/src/Modules/UngradedModules/FreeResolutions.jl index 1e7073a478e7..e74778908f60 100644 --- a/src/Modules/UngradedModules/FreeResolutions.jl +++ b/src/Modules/UngradedModules/FreeResolutions.jl @@ -26,6 +26,7 @@ function free_show(io::IO, C::ComplexOfMorphisms) push!(rank_mod, rank(M)) end + io = terse(io) io = IOContext(io, :compact => true) N = get_attribute(C, :free_res) if N !== nothing @@ -96,14 +97,14 @@ julia> B = R[x^2; x*y; y^2; z^4] [z^4] julia> M = SubquoModule(A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 4 generators -1 -> x^2*e[1] -2 -> x*y*e[1] -3 -> y^2*e[1] -4 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 4 generators + 1: x^2*e[1] + 2: x*y*e[1] + 3: y^2*e[1] + 4: z^4*e[1] julia> fr = free_resolution(M, length=1) Free resolution of M @@ -275,14 +276,14 @@ julia> B = R[x^2; x*y; y^2; z^4] [z^4] julia> M = SubquoModule(A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 4 generators -1 -> x^2*e[1] -2 -> x*y*e[1] -3 -> y^2*e[1] -4 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 4 generators + 1: x^2*e[1] + 2: x*y*e[1] + 3: y^2*e[1] + 4: z^4*e[1] julia> fr = free_resolution(M, length=1) Free resolution of M @@ -318,14 +319,14 @@ julia> B = [Z Z Z O; w*y w*z-x*y x*z-y^2 Z]; julia> A = transpose(matrix(B)); julia> M = graded_cokernel(A) -Graded subquotient of submodule of R^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of R^2 generated by -1 -> w*y*e[2] -2 -> (w*z - x*y)*e[2] -3 -> (x*z - y^2)*e[2] -4 -> e[1] +Graded subquotient of graded submodule of R^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of R^2 with 4 generators + 1: w*y*e[2] + 2: (w*z - x*y)*e[2] + 3: (x*z - y^2)*e[2] + 4: e[1] julia> FM1 = free_resolution(M) Free resolution of M @@ -342,7 +343,6 @@ julia> betti_table(FM1) ----------------- total: 2 7 8 3 - julia> matrix(map(FM1, 1)) [1 0] [0 -x*z + y^2] @@ -367,7 +367,6 @@ julia> betti_table(FM2) ----------------- total: 2 4 4 2 - julia> matrix(map(FM2, 1)) [1 0] [0 -x*z + y^2] @@ -388,7 +387,6 @@ julia> betti_table(FM3) ----------------- total: 1 3 4 2 - julia> matrix(map(FM3, 1)) [-x*z + y^2] [-w*z + x*y] diff --git a/src/Modules/UngradedModules/Hom_and_ext.jl b/src/Modules/UngradedModules/Hom_and_ext.jl index 00cdb25d0ec6..e1a3bd587c80 100644 --- a/src/Modules/UngradedModules/Hom_and_ext.jl +++ b/src/Modules/UngradedModules/Hom_and_ext.jl @@ -25,12 +25,12 @@ julia> F = FreeMod(R, 2); julia> V = [x*F[1], y^2*F[2]]; julia> M = quo_object(F, V) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y^2*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 2 generators + 1: x*e[1] + 2: y^2*e[2] julia> H = hom(M, M)[1] hom of (M, M) @@ -374,12 +374,12 @@ julia> F = FreeMod(R, 2); julia> V = [x*F[1], y^2*F[2]]; julia> M = quo_object(F, V) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y^2*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 2 generators + 1: x*e[1] + 2: y^2*e[2] julia> H = hom(M, M)[1]; @@ -396,23 +396,9 @@ julia> relations(H) y^2*(e[2] -> e[2]) julia> a = element_to_homomorphism(H[1]+y*H[2]) -Map with following data -Domain: -======= -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y^2*e[2] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y^2*e[2] +Module homomorphism + from M + to M julia> matrix(a) [1 0] @@ -440,12 +426,12 @@ julia> F = FreeMod(R, 2); julia> V = [x*F[1], y^2*F[2]]; julia> M = quo_object(F, V) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y^2*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 2 generators + 1: x*e[1] + 2: y^2*e[2] julia> H = hom(M, M)[1]; diff --git a/src/Modules/UngradedModules/HomologicalAlgebra.jl b/src/Modules/UngradedModules/HomologicalAlgebra.jl index b0597da4e97c..21a77b1d3286 100644 --- a/src/Modules/UngradedModules/HomologicalAlgebra.jl +++ b/src/Modules/UngradedModules/HomologicalAlgebra.jl @@ -231,27 +231,27 @@ julia> F = free_module(R, 1); julia> Q, _ = quo(F, [x*F[1]]); julia> T0 = tor(Q, M, 0) -Subquotient of Submodule with 2 generators -1 -> x*e[1] \otimes e[1] -2 -> y*e[1] \otimes e[1] -by Submodule with 4 generators -1 -> x^2*e[1] \otimes e[1] -2 -> y^3*e[1] \otimes e[1] -3 -> z^4*e[1] \otimes e[1] -4 -> x*y*e[1] \otimes e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] \otimes e[1] + 2: y*e[1] \otimes e[1] +by submodule with 4 generators + 1: x^2*e[1] \otimes e[1] + 2: y^3*e[1] \otimes e[1] + 3: z^4*e[1] \otimes e[1] + 4: x*y*e[1] \otimes e[1] julia> T1 = tor(Q, M, 1) -Subquotient of Submodule with 2 generators -1 -> x*e[1] \otimes e[1] -2 -> x*y*e[1] \otimes e[1] -by Submodule with 3 generators -1 -> x^2*e[1] \otimes e[1] -2 -> y^3*e[1] \otimes e[1] -3 -> z^4*e[1] \otimes e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] \otimes e[1] + 2: x*y*e[1] \otimes e[1] +by submodule with 3 generators + 1: x^2*e[1] \otimes e[1] + 2: y^3*e[1] \otimes e[1] + 3: z^4*e[1] \otimes e[1] julia> T2 = tor(Q, M, 2) Submodule with 0 generators -represented as subquotient with no relations. +represented as subquotient with no relations ``` """ function tor(M::ModuleFP, N::ModuleFP, i::Int) @@ -487,20 +487,20 @@ julia> C = ComplexOfMorphisms(ModuleFP, [a, b]); julia> H = homology(C) 3-element Vector{SubquoModule{QQMPolyRingElem}}: - Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 1 generator -1 -> x^4*e[1] - Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 2 generators -1 -> x^3*e[1] -2 -> x^2*e[1] - Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 2 generators -1 -> x^3*e[1] -2 -> x^2*e[1] + Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 1 generator + 1: x^4*e[1] + Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 2 generators + 1: x^3*e[1] + 2: x^2*e[1] + Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 2 generators + 1: x^3*e[1] + 2: x^2*e[1] ``` """ function homology(C::Hecke.ComplexOfMorphisms{<:ModuleFP}) @@ -534,11 +534,11 @@ julia> b = hom(B, B, [x^2*B[1]]); julia> C = ComplexOfMorphisms(ModuleFP, [a, b]); julia> H = homology(C, 1) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 2 generators -1 -> x^3*e[1] -2 -> x^2*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 2 generators + 1: x^3*e[1] + 2: x^2*e[1] ``` """ function homology(C::Hecke.ComplexOfMorphisms{<:ModuleFP}, i::Int) @@ -578,39 +578,39 @@ julia> F = FreeMod(R, 1); julia> V = [x*F[1], y*F[1]]; julia> M = quo_object(F, V) -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 2 generators + 1: x*e[1] + 2: y*e[1] julia> ext(M, M, 0) -Subquotient of Submodule with 1 generator -1 -> (e[1] -> e[1]) -by Submodule with 2 generators -1 -> y*(e[1] -> e[1]) -2 -> x*(e[1] -> e[1]) +Subquotient of submodule with 1 generator + 1: (e[1] -> e[1]) +by submodule with 2 generators + 1: y*(e[1] -> e[1]) + 2: x*(e[1] -> e[1]) julia> ext(M, M, 1) -Subquotient of Submodule with 2 generators -1 -> (e[1] -> e[1]) -2 -> (e[2] -> e[1]) -by Submodule with 4 generators -1 -> y*(e[1] -> e[1]) -2 -> x*(e[1] -> e[1]) -3 -> y*(e[2] -> e[1]) -4 -> x*(e[2] -> e[1]) +Subquotient of submodule with 2 generators + 1: (e[1] -> e[1]) + 2: (e[2] -> e[1]) +by submodule with 4 generators + 1: y*(e[1] -> e[1]) + 2: x*(e[1] -> e[1]) + 3: y*(e[2] -> e[1]) + 4: x*(e[2] -> e[1]) julia> ext(M, M, 2) -Subquotient of Submodule with 1 generator -1 -> (e[1] -> e[1]) -by Submodule with 2 generators -1 -> y*(e[1] -> e[1]) -2 -> x*(e[1] -> e[1]) +Subquotient of submodule with 1 generator + 1: (e[1] -> e[1]) +by submodule with 2 generators + 1: y*(e[1] -> e[1]) + 2: x*(e[1] -> e[1]) julia> ext(M, M, 3) Submodule with 0 generators -represented as subquotient with no relations. +represented as subquotient with no relations ``` """ function ext(M::ModuleFP, N::ModuleFP, i::Int) diff --git a/src/Modules/UngradedModules/Methods.jl b/src/Modules/UngradedModules/Methods.jl index fba8152ab01b..822b9036ea85 100644 --- a/src/Modules/UngradedModules/Methods.jl +++ b/src/Modules/UngradedModules/Methods.jl @@ -19,10 +19,12 @@ julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B); julia> is_equal_with_morphism(M, M) -M -> M -x*e[1] -> x*e[1] -y*e[1] -> y*e[1] Homogeneous module homomorphism + from M + to M +defined by + x*e[1] -> x*e[1] + y*e[1] -> y*e[1] ``` """ function is_equal_with_morphism(M::SubquoModule{T}, N::SubquoModule{T}, task::Symbol = :none) where {T} diff --git a/src/Modules/UngradedModules/ModuleGens.jl b/src/Modules/UngradedModules/ModuleGens.jl index bca35fa7e220..f8853391eadd 100644 --- a/src/Modules/UngradedModules/ModuleGens.jl +++ b/src/Modules/UngradedModules/ModuleGens.jl @@ -127,7 +127,7 @@ function show(io::IO, F::ModuleGens) print(io, "Module generating system of length ", length(F)) for i=1:length(F) if isassigned(F.O, i) - print(io, "\n", i, " -> ", F.O[i]) + print(io, "\n", i, ": ", F.O[i]) end end end diff --git a/src/Modules/UngradedModules/Presentation.jl b/src/Modules/UngradedModules/Presentation.jl index 569d82936558..dd04827f498c 100644 --- a/src/Modules/UngradedModules/Presentation.jl +++ b/src/Modules/UngradedModules/Presentation.jl @@ -208,14 +208,14 @@ julia> A = transpose(matrix(B)) [1 0] julia> M = graded_cokernel(A) -Graded subquotient of submodule of R^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of R^2 generated by -1 -> w*y*e[2] -2 -> (w*z - x*y)*e[2] -3 -> (x*z - y^2)*e[2] -4 -> e[1] +Graded subquotient of graded submodule of R^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of R^2 with 4 generators + 1: w*y*e[2] + 2: (w*z - x*y)*e[2] + 3: (x*z - y^2)*e[2] + 4: e[1] julia> PM1 = presentation(M) 0 <---- M <---- R^2 <---- R^4 @@ -246,22 +246,28 @@ julia> p[1] Graded free module Rg^0 of rank 0 over Rg julia> map(p,-1) -F -> 0 -e[1] -> 0 -e[2] -> 0 -e[3] -> 0 Homogeneous module homomorphism + from F + to 0 +defined by + e[1] -> 0 + e[2] -> 0 + e[3] -> 0 julia> map(p,0) -F -> F -e[1] -> e[1] -e[2] -> e[2] -e[3] -> e[3] Homogeneous module homomorphism + from F + to F +defined by + e[1] -> e[1] + e[2] -> e[2] + e[3] -> e[3] julia> map(p,1) -0 -> F Homogeneous module homomorphism + from 0 + to F +defined by julia> F = graded_free_module(Rg, 1); @@ -278,13 +284,13 @@ julia> P[-2] Graded free module Rg^0 of rank 0 over Rg julia> P[-1] -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> P[0] Graded free module Rg^2([-1]) of rank 2 over Rg @@ -293,25 +299,31 @@ julia> P[1] Graded free module Rg^2([-2]) + Rg^1([-3]) + Rg^2([-5]) of rank 5 over Rg julia> map(P,-1) -M -> 0 -x*e[1] -> 0 -y*e[1] -> 0 Homogeneous module homomorphism + from M + to 0 +defined by + x*e[1] -> 0 + y*e[1] -> 0 julia> map(P,0) -Rg^2 -> M -e[1] -> x*e[1] -e[2] -> y*e[1] Homogeneous module homomorphism + from Rg^2 + to M +defined by + e[1] -> x*e[1] + e[2] -> y*e[1] julia> map(P,1) -Rg^5 -> Rg^2 -e[1] -> x*e[1] -e[2] -> -y*e[1] + x*e[2] -e[3] -> y^2*e[2] -e[4] -> z^4*e[1] -e[5] -> z^4*e[2] Homogeneous module homomorphism + from Rg^5 + to Rg^2 +defined by + e[1] -> x*e[1] + e[2] -> -y*e[1] + x*e[2] + e[3] -> y^2*e[2] + e[4] -> z^4*e[1] + e[5] -> z^4*e[2] ``` """ function presentation(M::ModuleFP; minimal=false) @@ -340,24 +352,24 @@ julia> A = R[x; y]; julia> B = R[x^2; y^3; z^4]; julia> M = SubquoModule(A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> C = present_as_cokernel(M) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 5 generators -1 -> x*e[1] -2 -> -y*e[1] + x*e[2] -3 -> y^2*e[2] -4 -> z^4*e[1] -5 -> z^4*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 5 generators + 1: x*e[1] + 2: -y*e[1] + x*e[2] + 3: y^2*e[2] + 4: z^4*e[1] + 5: z^4*e[2] ``` ```jldoctest @@ -372,26 +384,23 @@ julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B); julia> present_as_cokernel(M, :with_morphism) -(Graded subquotient of submodule of Rg^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of Rg^2 generated by -1 -> x*e[1] -2 -> -y*e[1] + x*e[2] -3 -> y^2*e[2] -4 -> z^4*e[1] -5 -> z^4*e[2], Graded subquotient of submodule of Rg^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of Rg^2 generated by -1 -> x*e[1] -2 -> -y*e[1] + x*e[2] -3 -> y^2*e[2] -4 -> z^4*e[1] -5 -> z^4*e[2] -> M -e[1] -> x*e[1] -e[2] -> y*e[1] -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of Rg^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of Rg^2 with 5 generators + 1: x*e[1] + 2: -y*e[1] + x*e[2] + 3: y^2*e[2] + 4: z^4*e[1] + 5: z^4*e[2], Hom: graded subquotient of graded submodule of Rg^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of Rg^2 with 5 generators + 1: x*e[1] + 2: -y*e[1] + x*e[2] + 3: y^2*e[2] + 4: z^4*e[1] + 5: z^4*e[2] -> M) ``` """ function present_as_cokernel(SQ::SubquoModule, task::Symbol = :none) @@ -441,21 +450,17 @@ Free module of rank 2 over R julia> present_as_cokernel(F) Submodule with 2 generators -1 -> e[1] -2 -> e[2] -represented as subquotient with no relations. + 1: e[1] + 2: e[2] +represented as subquotient with no relations julia> present_as_cokernel(F, :only_morphism) -Map with following data -Domain: -======= -Free module of rank 2 over R -Codomain: -========= -Submodule with 2 generators -1 -> e[1] -2 -> e[2] -represented as subquotient with no relations. +Module homomorphism + from F + to submodule with 2 generators + 1: e[1] + 2: e[2] + represented as subquotient with no relations ``` """ function present_as_cokernel(F::FreeMod, task::Symbol = :none) @@ -502,24 +507,24 @@ julia> A = transpose(matrix(B)) [1 0] julia> M = graded_cokernel(A) -Graded subquotient of submodule of R^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of R^2 generated by -1 -> w*y*e[2] -2 -> (w*z - x*y)*e[2] -3 -> (x*z - y^2)*e[2] -4 -> e[1] +Graded subquotient of graded submodule of R^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of R^2 with 4 generators + 1: w*y*e[2] + 2: (w*z - x*y)*e[2] + 3: (x*z - y^2)*e[2] + 4: e[1] julia> N, phi = prune_with_map(M); julia> N -Graded subquotient of submodule of R^1 generated by -1 -> e[1] -by submodule of R^1 generated by -1 -> (-x*z + y^2)*e[1] -2 -> (-w*z + x*y)*e[1] -3 -> w*y*e[1] +Graded subquotient of graded submodule of R^1 with 1 generator + 1: e[1] +by graded submodule of R^1 with 3 generators + 1: (-x*z + y^2)*e[1] + 2: (-w*z + x*y)*e[1] + 3: w*y*e[1] julia> phi(first(gens(N))) e[2] diff --git a/src/Modules/UngradedModules/SubModuleOfFreeModule.jl b/src/Modules/UngradedModules/SubModuleOfFreeModule.jl index 840c761ad18a..ecf3de21aa25 100644 --- a/src/Modules/UngradedModules/SubModuleOfFreeModule.jl +++ b/src/Modules/UngradedModules/SubModuleOfFreeModule.jl @@ -306,27 +306,29 @@ end function show(io::IO, M::SubModuleOfFreeModule) @show_name(io, M) @show_special(io, M) - io_compact = IOContext(io, :compact => true) - compact = get(io, :compact, false) - if !compact +# if !is_terse(io) if is_graded(M) - print(io_compact, "Graded submodule of ", M.F) + io_compact = IOContext(io, :compact => true) + print(terse(io_compact), "Graded submodule of ", M.F) else #Todo: Use again once the printing of rings is fixed #print(io_compact, "Submodule of ", M.F) - print(io_compact, "Submodule") + print(io, "Submodule") end if ngens(M) == 1 print(io, " with ", ngens(M), " generator") else print(io, " with ", ngens(M), " generators") end - end +# end + io = pretty(io) + print(io, Indent()) for i=1:ngens(M) if isassigned(M.gens.O, i) - print(io, "\n", i, " -> ", M[i]) + print(io, "\n", i, ": ", M[i]) end end + print(io, Dedent()) end function length(M::SubModuleOfFreeModule) diff --git a/src/Modules/UngradedModules/SubQuoHom.jl b/src/Modules/UngradedModules/SubQuoHom.jl index 4dc3e6a10443..4fc4ea9ffbbe 100644 --- a/src/Modules/UngradedModules/SubQuoHom.jl +++ b/src/Modules/UngradedModules/SubQuoHom.jl @@ -42,41 +42,42 @@ function SubQuoHom(D::SubquoModule, C::ModuleFP{T}, mat::MatElem{T}, h::RingMapT end function Base.show(io::IO, ::MIME"text/plain", fmh::SubQuoHom{T1, T2, RingMapType}) where {T1 <: AbstractSubQuo, T2 <: ModuleFP, RingMapType} - # HACK - show(io, fmh) + println(terse(io), fmh) + io = pretty(io) + io_compact = IOContext(io, :compact => true) + println(io_compact, Indent(), "from ", Lowercase(), domain(fmh)) + print(io_compact, "to ", Lowercase(), codomain(fmh), Dedent()) + + if is_graded(fmh) + println(io) + print(io, "defined by", Indent()) + io = terse(io) + domain_gens = gens(domain(fmh)) + for g in domain_gens + println(io) + print(io, g, " -> ", fmh(g)) + end + end end function Base.show(io::IO, fmh::SubQuoHom{T1, T2, RingMapType}) where {T1 <: AbstractSubQuo, T2 <: ModuleFP, RingMapType} - compact = get(io, :compact, false) - io_compact = IOContext(io, :compact => true) - domain_gens = gens(domain(fmh)) - if is_graded(fmh) - print(io_compact, domain(fmh)) - print(io, " -> ") - print(io_compact, codomain(fmh)) - if !compact - print(io, "\n") - for i in 1:length(domain_gens) - print(io, domain_gens[i], " -> ") - print(io_compact, fmh(domain_gens[i])) - print(io, "\n") - end + if is_terse(io) + if is_graded(fmh) A = grading_group(fmh) if degree(fmh) == A[0] print(io, "Homogeneous module homomorphism") else - print(io_compact, "Graded module homomorphism of degree ", degree(fmh)) - print(io, "\n") + print(io, "Graded module homomorphism of degree ", degree(fmh)) end + else + print(io, "Module homomorphism") end else - println(io, "Map with following data") - println(io, "Domain:") - println(io, "=======") - println(io, domain(fmh)) - println(io, "Codomain:") - println(io, "=========") - print(io, codomain(fmh)) + io = pretty(io) + print(io, "Hom: ") + io = terse(io) + print(io, Lowercase(), domain(fmh), " -> ") + print(io, Lowercase(), codomain(fmh)) end end @@ -133,13 +134,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; @@ -149,25 +150,9 @@ julia> V = [y^2*N[1], x*N[2]] x*y*e[1] julia> a = hom(M, N, V) -Map with following data -Domain: -======= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from M + to M julia> is_welldefined(a) true @@ -218,23 +203,25 @@ julia> A = Rg[x; y]; julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> is_welldefined(a) true @@ -244,10 +231,12 @@ julia> W = Rg[y^2 0; 0 x^2] [ 0 x^2] julia> b = hom(M, N, W) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> a == b true @@ -258,10 +247,12 @@ julia> W = [y*N[1], x*N[2]] x*y*e[1] julia> c = hom(M, N, W) -M -> M -x*e[1] -> x*y*e[1] -y*e[1] -> x*y*e[1] Graded module homomorphism of degree [1] + from M + to M +defined by + x*e[1] -> x*y*e[1] + y*e[1] -> x*y*e[1] julia> is_welldefined(c) false @@ -395,13 +386,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; @@ -427,23 +418,25 @@ julia> A = Rg[x; y]; julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> matrix(a) [y^2 0] @@ -586,23 +579,15 @@ julia> I, incl = image(a); julia> I Submodule with 3 generators -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations. + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] +represented as subquotient with no relations julia> incl -Map with following data -Domain: -======= -Submodule with 3 generators -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] -represented as subquotient with no relations. -Codomain: -========= -Free module of rank 2 over R +Module homomorphism + from I + to G ``` ```jldoctest @@ -620,13 +605,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; @@ -640,34 +625,18 @@ julia> a = hom(M, N, V); julia> I, incl = image(a); julia> I -Subquotient of Submodule with 2 generators -1 -> x*y^2*e[1] -2 -> x*y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*y^2*e[1] + 2: x*y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> incl -Map with following data -Domain: -======= -Subquotient of Submodule with 2 generators -1 -> x*y^2*e[1] -2 -> x*y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from I + to M ``` ```jldoctest @@ -680,41 +649,40 @@ julia> A = Rg[x; y]; julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> image(a) -(Graded subquotient of submodule of F generated by -1 -> x*y^2*e[1] -2 -> x^2*y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], Graded subquotient of submodule of F generated by -1 -> x*y^2*e[1] -2 -> x^2*y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -> M -x*y^2*e[1] -> x*y^2*e[1] -x^2*y*e[1] -> x^2*y*e[1] -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of F with 2 generators + 1: x*y^2*e[1] + 2: x^2*y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: graded subquotient of graded submodule of F with 2 generators + 1: x*y^2*e[1] + 2: x^2*y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> M) ``` """ function image(a::ModuleFPHom) @@ -768,19 +736,13 @@ julia> K, incl = kernel(a); julia> K Submodule with 1 generator -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations. + 1: x*z*e[1] - y*z*e[2] + y^2*e[3] +represented as subquotient with no relations julia> incl -Map with following data -Domain: -======= -Submodule with 1 generator -1 -> x*z*e[1] - y*z*e[2] + y^2*e[3] -represented as subquotient with no relations. -Codomain: -========= -Free module of rank 3 over R +Module homomorphism + from K + to F ``` ```jldoctest @@ -798,13 +760,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; @@ -818,36 +780,19 @@ julia> a = hom(M, N, V); julia> K, incl = kernel(a); julia> K -Subquotient of Submodule with 3 generators -1 -> (-x + y^2)*e[1] -2 -> x*y*e[1] -3 -> -x*y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 3 generators + 1: (-x + y^2)*e[1] + 2: x*y*e[1] + 3: -x*y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> incl -Map with following data -Domain: -======= -Subquotient of Submodule with 3 generators -1 -> (-x + y^2)*e[1] -2 -> x*y*e[1] -3 -> -x*y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from K + to M ``` ```jldoctest @@ -860,41 +805,40 @@ julia> A = Rg[x; y]; julia> B = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, A, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; julia> V = [y^2*N[1], x^2*N[2]]; julia> a = hom(M, N, V) -M -> M -x*e[1] -> x*y^2*e[1] -y*e[1] -> x^2*y*e[1] Graded module homomorphism of degree [2] + from M + to M +defined by + x*e[1] -> x*y^2*e[1] + y*e[1] -> x^2*y*e[1] julia> kernel(a) -(Graded subquotient of submodule of F generated by -1 -> y*e[1] -2 -> -x*y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], Graded subquotient of submodule of F generated by -1 -> y*e[1] -2 -> -x*y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -> M -y*e[1] -> y*e[1] --x*y*e[1] -> -x*y*e[1] -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of F with 2 generators + 1: y*e[1] + 2: -x*y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: graded subquotient of graded submodule of F with 2 generators + 1: y*e[1] + 2: -x*y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> M) ``` """ diff --git a/src/Modules/UngradedModules/SubquoModule.jl b/src/Modules/UngradedModules/SubquoModule.jl index 6ac9ebeccc04..f0162b03c11d 100644 --- a/src/Modules/UngradedModules/SubquoModule.jl +++ b/src/Modules/UngradedModules/SubquoModule.jl @@ -51,9 +51,9 @@ julia> O = [x*F[1]+F[2],y*F[2]] julia> M = SubquoModule(F, O) Submodule with 2 generators -1 -> x*e[1] + e[2] -2 -> y*e[2] -represented as subquotient with no relations. + 1: x*e[1] + e[2] + 2: y*e[2] +represented as subquotient with no relations ``` """ @@ -134,14 +134,14 @@ julia> B = R[x^2; x*y; y^2; z^4] [z^4] julia> M = SubquoModule(A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 4 generators -1 -> x^2*e[1] -2 -> x*y*e[1] -3 -> y^2*e[1] -4 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 4 generators + 1: x^2*e[1] + 2: x*y*e[1] + 3: y^2*e[1] + 4: z^4*e[1] ``` """ function SubquoModule(A::MatElem{R}, B::MatElem{R}) where {R} @@ -199,13 +199,13 @@ julia> BR = R[x^2; y^3; z^4] [z^4] julia> MR = SubquoModule(FR, AR, BR) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> P = ideal(R, [x, y, z]); @@ -214,7 +214,7 @@ julia> U = complement_of_prime_ideal(P); julia> RL, _ = localization(R, U); julia> FRL = free_module(RL, 1) -Free module of rank 1 over Localization of R at complement of prime ideal (x, y, z) +Free module of rank 1 over localization of R at complement of prime ideal (x, y, z) julia> ARL = RL[x; y] [x] @@ -226,13 +226,13 @@ julia> BRL = RL[x^2; y^3; z^4] [z^4] julia> MRL = SubquoModule(FRL, ARL, BRL) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> RQ, _ = quo(R, ideal(R, [2*x^2-y^3, 2*x^2-y^5])); @@ -249,18 +249,18 @@ julia> BRQ = RQ[x^2; y^3; z^4] [z^4] julia> MRQ = SubquoModule(FRQ, ARQ, BRQ) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> 2*x^2*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: 2*x^2*e[1] + 3: z^4*e[1] julia> RQL, _ = localization(RQ, U); julia> FRQL = free_module(RQL, 1) -Free module of rank 1 over Localization of RQ at complement of prime ideal +Free module of rank 1 over localization of RQ at complement of prime ideal julia> ARQL = RQL[x; y] [x] @@ -272,13 +272,13 @@ julia> BRQL = RQL[x^2; y^3; z^4] [z^4] julia> MRQL = SubquoModule(FRQL, ARQL, BRQL) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> 0 -2 -> 0 -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: 0 + 2: 0 + 3: z^4*e[1] ``` ```jldoctest @@ -301,24 +301,28 @@ julia> V2 = [z*G[2]+y*G[1]] y*e[1] + z*e[2] julia> a1 = hom(F1, G, V1) -F1 -> G -e[1] -> y*e[1] -e[2] -> (x + y)*e[1] + y*e[2] -e[3] -> z*e[2] Homogeneous module homomorphism + from F1 + to G +defined by + e[1] -> y*e[1] + e[2] -> (x + y)*e[1] + y*e[2] + e[3] -> z*e[2] julia> a2 = hom(F2, G, V2) -F2 -> G -e[1] -> y*e[1] + z*e[2] Homogeneous module homomorphism + from F2 + to G +defined by + e[1] -> y*e[1] + z*e[2] julia> V = subquotient(a1,a2) -Graded subquotient of submodule of G generated by -1 -> y*e[1] -2 -> (x + y)*e[1] + y*e[2] -3 -> z*e[2] -by submodule of G generated by -1 -> y*e[1] + z*e[2] +Graded subquotient of graded submodule of G with 3 generators + 1: y*e[1] + 2: (x + y)*e[1] + y*e[2] + 3: z*e[2] +by graded submodule of G with 1 generator + 1: y*e[1] + z*e[2] julia> A1 = Rg[x y; 2*x^2 3*y^2] [ x y] @@ -335,11 +339,11 @@ julia> F2 = graded_free_module(Rg,[0,0]) Graded free module Rg^2([0]) of rank 2 over Rg julia> M1 = SubquoModule(F2, A1, B) -Graded subquotient of submodule of F2 generated by -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] -by submodule of F2 generated by -1 -> 4*x*y^3*e[1] + (16*x^4 + 32*x^3*y + 24*x^2*y^2 + 8*x*y^3 + y^4)*e[2] +Graded subquotient of graded submodule of F2 with 2 generators + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] +by graded submodule of F2 with 1 generator + 1: 4*x*y^3*e[1] + (16*x^4 + 32*x^3*y + 24*x^2*y^2 + 8*x*y^3 + y^4)*e[2] ``` """ function subquotient(a::FreeModuleHom, b::FreeModuleHom) @@ -356,32 +360,34 @@ subquotient(A::MatElem{T}, B::MatElem{T}) where {T} = SubquoModule(A, B) function show(io::IO, SQ::SubquoModule) @show_name(io, SQ) @show_special(io, SQ) - io_compact = IOContext(io, :compact => true) + io = pretty(io) if is_graded(SQ) + io_compact = IOContext(io, :compact => true) if isdefined(SQ, :quo) && !iszero(SQ.quo) print(io, "Graded subquotient") - print(io_compact, " of submodule of ", SQ.F, " generated by", SQ.sub, "\nby submodule of ", SQ.F, " generated by", SQ.quo) + println(io_compact, " of ", Lowercase(), SQ.sub) + print(io_compact, "by ", Lowercase(), SQ.quo) else - print(io_compact, "Graded submodule of ", SQ.F) - print(io_compact, SQ.sub, "\n") + println(io_compact, SQ.sub) print(io, "represented as subquotient with no relations") end else # Todo: Use again once the printing of rings is fixed # if isdefined(SQ, :quo) && !iszero(SQ.quo) # print(io, "Subquotient") - # print(io_compact, " of submodule of ", SQ.F, " generated by", SQ.sub, "\nby submodule of ", SQ.F, " generated by", SQ.quo) + # print(io_compact, " of submodule of ", SQ.F, " generated by ", SQ.sub, "\nby submodule of ", SQ.F, " generated by", SQ.quo) # else # print(io_compact, "Submodule of ", SQ.F) # print(io_compact, SQ.sub, "\n") # print(io, "represented as subquotient with no relations") # end if isdefined(SQ, :quo) && !iszero(SQ.quo) - print(io, "Subquotient of ", SQ.sub, "\nby ", SQ.quo) + println(io, "Subquotient of ", Lowercase(), SQ.sub) + print(io, "by ", Lowercase(), SQ.quo) else - print(io, SQ.sub, "\n") - print(io, "represented as subquotient with no relations.") + println(io, SQ.sub) + print(io, "represented as subquotient with no relations") end end end @@ -394,7 +400,6 @@ Show `SQ` as a subquotient of *matrices* `A` and `B`. function show_subquo(SQ::SubquoModule) #@show_name(io, SQ) #@show_special(io, SQ) - io_compact = IOContext(stdout, :compact => true) if isdefined(SQ, :quo) if is_generated_by_standard_unit_vectors(SQ.sub) if is_graded(SQ) @@ -421,11 +426,11 @@ function show_subquo(SQ::SubquoModule) end show(stdout, "text/plain", generator_matrix(SQ.sub)) end - print(io_compact, "\nwith ambient free module ", SQ.F) + print(terse(io), "\nwith ambient free module ", SQ.F) end function show_morphism_as_map(f::ModuleFPHom, print_non_zero_only = false) - io_compact = IOContext(stdout, :compact => true) + io_compact = terse(stdout) print(io_compact, domain(f), " -> ", codomain(f)) print("\n") D = domain(f) @@ -445,7 +450,7 @@ function show_morphism_as_map(f::ModuleFPHom, print_non_zero_only = false) if degree(f)==A[0] print("Homogeneous homomorphism") else - print(io_compact,"Graded homomorphism of degree ", degree(f)) + print(io_compact, "Graded homomorphism of degree ", degree(f)) end else print("Homomorphism") @@ -474,13 +479,13 @@ julia> W = R[y 0; x y; 0 z] julia> a = hom(F, G, W); julia> cokernel(a) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 3 generators -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 3 generators + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] ``` ```jldoctest @@ -498,13 +503,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> N = M; @@ -516,15 +521,15 @@ julia> V = [y^2*N[1], x*N[2]] julia> a = hom(M, N, V); julia> cokernel(a) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 5 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -4 -> x*y^2*e[1] -5 -> x*y*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 5 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] + 4: x*y^2*e[1] + 5: x*y*e[1] ``` ```jldoctest @@ -540,20 +545,22 @@ julia> W = Rg[y 0; x y; 0 z] [0 z] julia> a = hom(F, G, W) -F -> G -e[1] -> y*e[1] -e[2] -> x*e[1] + y*e[2] -e[3] -> z*e[2] Graded module homomorphism of degree [1] + from F + to G +defined by + e[1] -> y*e[1] + e[2] -> x*e[1] + y*e[2] + e[3] -> z*e[2] julia> M = cokernel(a) -Graded subquotient of submodule of G generated by -1 -> e[1] -2 -> e[2] -by submodule of G generated by -1 -> y*e[1] -2 -> x*e[1] + y*e[2] -3 -> z*e[2] +Graded subquotient of graded submodule of G with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of G with 3 generators + 1: y*e[1] + 2: x*e[1] + y*e[2] + 3: z*e[2] ``` """ @@ -578,12 +585,12 @@ julia> A = R[x y; 2*x^2 3*y^2] [2*x^2 3*y^2] julia> M = cokernel(F, A) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 2 generators + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] julia> ambient_free_module(M) === F true @@ -601,12 +608,12 @@ julia> A = Rg[x y; 2*x^2 3*y^2] [2*x^2 3*y^2] julia> M = cokernel(F, A) -Graded subquotient of submodule of F generated by -1 -> e[1] -2 -> e[2] -by submodule of F generated by -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] +Graded subquotient of graded submodule of F with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of F with 2 generators + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] julia> ambient_free_module(M) === F true @@ -635,12 +642,12 @@ julia> A = R[x y; 2*x^2 3*y^2] [2*x^2 3*y^2] julia> M = cokernel(A) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 2 generators -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 2 generators + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] ``` """ @@ -666,9 +673,9 @@ julia> A = R[x y; 2*x^2 3*y^2] julia> M = image(F, A) Submodule with 2 generators -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] -represented as subquotient with no relations. + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] +represented as subquotient with no relations julia> ambient_free_module(M) === F true @@ -685,9 +692,9 @@ julia> A = Rg[x y; 2*x^2 3*y^2] [2*x^2 3*y^2] julia> M = image(F, A) -Graded submodule of F -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] +Graded submodule of F with 2 generators + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] represented as subquotient with no relations julia> ambient_free_module(M) === F @@ -718,9 +725,9 @@ julia> A = R[x y; 2*x^2 3*y^2] julia> M = image(A) Submodule with 2 generators -1 -> x*e[1] + y*e[2] -2 -> 2*x^2*e[1] + 3*y^2*e[2] -represented as subquotient with no relations. + 1: x*e[1] + y*e[2] + 2: 2*x^2*e[1] + 3*y^2*e[2] +represented as subquotient with no relations ``` """ function image(A::MatElem) @@ -853,12 +860,12 @@ julia> BM = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[x; y] [x] @@ -870,13 +877,13 @@ julia> BN = R[x^2+y^4; y^3; z^4] [ z^4] julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> (x^2 + y^4)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: (x^2 + y^4)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> is_subset(M, N) true @@ -894,20 +901,20 @@ julia> O1a = [x*F[1],y*F[2]]; julia> O2 = [x^2*F[1]+y^2*F[2],y^2*F[2]]; julia> M1 = SubquoModule(F, O1, O2) -Graded subquotient of submodule of F generated by -1 -> x*e[1] + y*e[2] -2 -> y*e[2] -by submodule of F generated by -1 -> x^2*e[1] + y^2*e[2] -2 -> y^2*e[2] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + y*e[2] + 2: y*e[2] +by graded submodule of F with 2 generators + 1: x^2*e[1] + y^2*e[2] + 2: y^2*e[2] julia> M2 = SubquoModule(F, O1a, O2) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[2] -by submodule of F generated by -1 -> x^2*e[1] + y^2*e[2] -2 -> y^2*e[2] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[2] +by graded submodule of F with 2 generators + 1: x^2*e[1] + y^2*e[2] + 2: y^2*e[2] julia> is_subset(M1,M2) true @@ -981,12 +988,12 @@ julia> BM = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[x; y] [x] @@ -998,13 +1005,13 @@ julia> BN = R[x^2+y^4; y^3; z^4] [ z^4] julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> (x^2 + y^4)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: (x^2 + y^4)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> M == N false @@ -1022,20 +1029,20 @@ julia> O1a = [x*F[1],y*F[2]]; julia> O2 = [x^2*F[1]+y^2*F[2],y^2*F[2]]; julia> M1 = SubquoModule(F, O1, O2) -Graded subquotient of submodule of F generated by -1 -> x*e[1] + y*e[2] -2 -> y*e[2] -by submodule of F generated by -1 -> x^2*e[1] + y^2*e[2] -2 -> y^2*e[2] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + y*e[2] + 2: y*e[2] +by graded submodule of F with 2 generators + 1: x^2*e[1] + y^2*e[2] + 2: y^2*e[2] julia> M2 = SubquoModule(F, O1a, O2) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[2] -by submodule of F generated by -1 -> x^2*e[1] + y^2*e[2] -2 -> y^2*e[2] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[2] +by graded submodule of F with 2 generators + 1: x^2*e[1] + y^2*e[2] + 2: y^2*e[2] julia> M1 == M2 true @@ -1063,9 +1070,9 @@ julia> A1 = Rg[x^3 x^2 x; (2*x^2+x*y)*x^2 (2*y^2+x^2)*x x^2] [2*x^4 + x^3*y x^3 + 2*x*y^2 x^2] julia> M1 = image(F1, A1) -Graded submodule of F1 -1 -> x^3*e[1] + x^2*e[2] + x*e[3] -2 -> (2*x^4 + x^3*y)*e[1] + (x^3 + 2*x*y^2)*e[2] + x^2*e[3] +Graded submodule of F1 with 2 generators + 1: x^3*e[1] + x^2*e[2] + x*e[3] + 2: (2*x^4 + x^3*y)*e[1] + (x^3 + 2*x*y^2)*e[2] + x^2*e[3] represented as subquotient with no relations julia> F2 = graded_free_module(Rg,[2,4, 3]) @@ -1076,9 +1083,9 @@ julia> A2 = Rg[x^3 x x^2; (2*x^2+x*y)*x^2 x^2 (2*y^2+x^2)*x] [2*x^4 + x^3*y x^2 x^3 + 2*x*y^2] julia> M2 = image(F2, A2) -Graded submodule of F2 -1 -> x^3*e[1] + x*e[2] + x^2*e[3] -2 -> (2*x^4 + x^3*y)*e[1] + x^2*e[2] + (x^3 + 2*x*y^2)*e[3] +Graded submodule of F2 with 2 generators + 1: x^3*e[1] + x*e[2] + x^2*e[3] + 2: (2*x^4 + x^3*y)*e[1] + x^2*e[2] + (x^3 + 2*x*y^2)*e[3] represented as subquotient with no relations julia> is_canonically_isomorphic(M1, M2) @@ -1114,9 +1121,9 @@ julia> A1 = Rg[x^3 x^2 x; (2*x^2+x*y)*x^2 (2*y^2+x^2)*x x^2] [2*x^4 + x^3*y x^3 + 2*x*y^2 x^2] julia> M1 = image(F1, A1) -Graded submodule of F1 -1 -> x^3*e[1] + x^2*e[2] + x*e[3] -2 -> (2*x^4 + x^3*y)*e[1] + (x^3 + 2*x*y^2)*e[2] + x^2*e[3] +Graded submodule of F1 with 2 generators + 1: x^3*e[1] + x^2*e[2] + x*e[3] + 2: (2*x^4 + x^3*y)*e[1] + (x^3 + 2*x*y^2)*e[2] + x^2*e[3] represented as subquotient with no relations julia> F2 = graded_free_module(Rg,[2,4, 3]) @@ -1127,16 +1134,13 @@ julia> A2 = Rg[x^3 x x^2; (2*x^2+x*y)*x^2 x^2 (2*y^2+x^2)*x] [2*x^4 + x^3*y x^2 x^3 + 2*x*y^2] julia> M2 = image(F2, A2) -Graded submodule of F2 -1 -> x^3*e[1] + x*e[2] + x^2*e[3] -2 -> (2*x^4 + x^3*y)*e[1] + x^2*e[2] + (x^3 + 2*x*y^2)*e[3] +Graded submodule of F2 with 2 generators + 1: x^3*e[1] + x*e[2] + x^2*e[3] + 2: (2*x^4 + x^3*y)*e[1] + x^2*e[2] + (x^3 + 2*x*y^2)*e[3] represented as subquotient with no relations julia> is_canonically_isomorphic_with_map(M1, M2) -(true, M1 -> M2 -x^3*e[1] + x^2*e[2] + x*e[3] -> x^3*e[1] + x*e[2] + x^2*e[3] -(2*x^4 + x^3*y)*e[1] + (x^3 + 2*x*y^2)*e[2] + x^2*e[3] -> (2*x^4 + x^3*y)*e[1] + x^2*e[2] + (x^3 + 2*x*y^2)*e[3] -Homogeneous module homomorphism) +(true, Hom: M1 -> M2) ``` """ @@ -1180,12 +1184,12 @@ julia> BM = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[y;] [y] @@ -1196,63 +1200,45 @@ julia> BN = R[x^2; y^3; z^4] [z^4] julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> O = sum(M, N); julia> O[1] -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> O[2] -Map with following data -Domain: -======= -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from M + to subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] + by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> O[3] -Map with following data -Domain: -======= -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from N + to subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] + by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` ```jldoctest @@ -1265,49 +1251,45 @@ julia> AM = Rg[x;]; julia> BM = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: x*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = Rg[y;]; julia> BN = Rg[x^2; y^3; z^4]; julia> N = SubquoModule(F, AN, BN) -Graded subquotient of submodule of F generated by -1 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> sum(M, N) -(Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], M -> Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -x*e[1] -> x*e[1] -Homogeneous module homomorphism, N -> Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -y*e[1] -> y*e[1] -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: M -> graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: N -> graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1]) ``` """ @@ -1362,12 +1344,12 @@ julia> BM = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[y;] [y] @@ -1378,21 +1360,21 @@ julia> BN = R[x^2; y^3; z^4] [z^4] julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> O = M + N -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` ```jldoctest @@ -1405,33 +1387,33 @@ julia> AM = Rg[x;]; julia> BM = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: x*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = Rg[y;]; julia> BN = Rg[x^2; y^3; z^4]; julia> N = SubquoModule(F, AN, BN) -Graded subquotient of submodule of F generated by -1 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> M + N -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` """ @@ -1465,12 +1447,12 @@ julia> BM = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[y;] [y] @@ -1481,55 +1463,33 @@ julia> BN = R[x^2; y^3; z^4] [z^4] julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> intersect(M, N) -(Subquotient of Submodule with 2 generators -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], Map with following data -Domain: -======= -Subquotient of Submodule with 2 generators -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], Map with following data -Domain: -======= -Subquotient of Submodule with 2 generators -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -Codomain: -========= -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1]) +(Subquotient of submodule with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: subquotient of submodule with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> M, Hom: subquotient of submodule with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> N) ``` ```jldoctest @@ -1542,51 +1502,45 @@ julia> AM = Rg[x;]; julia> BM = Rg[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: x*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = Rg[y;]; julia> BN = Rg[x^2; y^3; z^4]; julia> N = SubquoModule(F, AN, BN) -Graded subquotient of submodule of F generated by -1 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> intersect(M, N) -(Graded subquotient of submodule of F generated by -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1], Graded subquotient of submodule of F generated by -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -> M --x*y*e[1] -> -x*y*e[1] -x*z^4*e[1] -> x*z^4*e[1] -Homogeneous module homomorphism, Graded subquotient of submodule of F generated by -1 -> -x*y*e[1] -2 -> x*z^4*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -> N --x*y*e[1] -> x*y*e[1] -x*z^4*e[1] -> 0 -Homogeneous module homomorphism) +(Graded subquotient of graded submodule of F with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1], Hom: graded subquotient of graded submodule of F with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> M, Hom: graded subquotient of graded submodule of F with 2 generators + 1: -x*y*e[1] + 2: x*z^4*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] -> N) ``` """ @@ -1648,12 +1602,12 @@ julia> AN = R[y;]; julia> BN = R[x^2; y^3; z^4]; julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = annihilator(N) Ideal generated by @@ -1663,7 +1617,7 @@ Ideal generated by ``` -``` +```jldoctest julia> S, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]); julia> I = ideal(S, [x*y*z]) @@ -1680,12 +1634,12 @@ julia> AN = R[y;]; julia> BN = R[x^2; y^3; z^4]; julia> N = SubquoModule(F, AN, BN) -Subquotient of Submodule with 1 generator -1 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = annihilator(N) Ideal generated by @@ -1693,7 +1647,6 @@ Ideal generated by y^2 x^2 z^4 - ``` """ function annihilator(M::SubquoModule{T}) where T @@ -1743,22 +1696,22 @@ julia> B = R[x^2; y^3; z^4]; julia> AM = R[x;]; julia> M = SubquoModule(F, AM, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: x*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> AN = R[y;]; julia> N = SubquoModule(F, AN, B) -Graded subquotient of submodule of F generated by -1 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 1 generator + 1: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> L = quotient(M, N) Ideal generated by @@ -1801,12 +1754,12 @@ julia> AM = R[x;]; julia> BM = R[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = ideal(R, [x, y, z])^2 Ideal generated by @@ -1818,14 +1771,14 @@ Ideal generated by z^2 julia> L = quotient(M, J) -Subquotient of Submodule with 3 generators -1 -> x*e[1] -2 -> y*z^3*e[1] -3 -> y^2*z^2*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 3 generators + 1: x*e[1] + 2: y*z^3*e[1] + 3: y^2*z^2*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> ambient_free_module(L) == ambient_free_module(M) true @@ -1844,12 +1797,12 @@ julia> AM = R[x;]; julia> BM = R[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> (-y - z)*e[1] -by Submodule with 3 generators -1 -> (y^2 + 2*y*z + z^2)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: (-y - z)*e[1] +by submodule with 3 generators + 1: (y^2 + 2*y*z + z^2)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = ideal(R, [x, y, z])^2 Ideal generated by @@ -1861,13 +1814,13 @@ Ideal generated by z^2 julia> quotient(M, J) -Subquotient of Submodule with 2 generators -1 -> z*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> (y^2 + 2*y*z + z^2)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: z*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: (y^2 + 2*y*z + z^2)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` """ @@ -1930,12 +1883,12 @@ julia> AM = R[x;]; julia> BM = R[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = ideal(R, [x, y, z])^2 Ideal generated by @@ -1947,12 +1900,12 @@ Ideal generated by z^2 julia> saturation(M, J) -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] ``` """ @@ -2010,12 +1963,12 @@ julia> AM = R[x;]; julia> BM = R[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> x*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: x*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = ideal(R, [x, y, z])^2 Ideal generated by @@ -2029,12 +1982,12 @@ Ideal generated by julia> L = saturation_with_index(M, J); julia> L[1] -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> L[2] 3 @@ -2053,12 +2006,12 @@ julia> AM = R[x;]; julia> BM = R[x^2; y^3; z^4]; julia> M = SubquoModule(F, AM, BM) -Subquotient of Submodule with 1 generator -1 -> (-y - z)*e[1] -by Submodule with 3 generators -1 -> (y^2 + 2*y*z + z^2)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: (-y - z)*e[1] +by submodule with 3 generators + 1: (y^2 + 2*y*z + z^2)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> J = ideal(R, [x, y, z])^2 Ideal generated by @@ -2072,12 +2025,12 @@ Ideal generated by julia> L = saturation_with_index(M, J); julia> L[1] -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> (y^2 + 2*y*z + z^2)*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 3 generators + 1: (y^2 + 2*y*z + z^2)*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> L[2] 2 @@ -2156,11 +2109,11 @@ julia> mat = matrix(QQ, [0 -1; 0 0]); julia> U = image(mat) Submodule with 1 generator -1 -> -e[2] -represented as subquotient with no relations. + 1: -e[2] +represented as subquotient with no relations julia> ambient_module(U) -Free module of rank 2 over Rational field +Free module of rank 2 over rational field ``` """ function ambient_module(M::SubquoModule, task = :none) diff --git a/src/Modules/UngradedModules/SubquoModuleElem.jl b/src/Modules/UngradedModules/SubquoModuleElem.jl index d0256cf415a7..93dcd238f8a1 100644 --- a/src/Modules/UngradedModules/SubquoModuleElem.jl +++ b/src/Modules/UngradedModules/SubquoModuleElem.jl @@ -607,23 +607,15 @@ julia> N, incl = sub(F, V); julia> N Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -represented as subquotient with no relations. + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] +represented as subquotient with no relations julia> incl -Map with following data -Domain: -======= -Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] -represented as subquotient with no relations. -Codomain: -========= -Free module of rank 1 over R +Module homomorphism + from N + to F ``` """ function sub(M::ModuleFP{T}, V::Vector{<:ModuleFPElem{T}}; cache_morphism::Bool=false) where T @@ -810,26 +802,17 @@ julia> V = [x^2*F[1]; y^3*F[1]; z^4*F[1]]; julia> N, proj = quo(F, V); julia> N -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> proj -Map with following data -Domain: -======= -Free module of rank 1 over R -Codomain: -========= -Subquotient of Submodule with 1 generator -1 -> e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Module homomorphism + from F + to N ``` """ function quo(M::ModuleFP{T}, V::Vector{<:ModuleFPElem{T}}; cache_morphism::Bool=false) where T @@ -994,12 +977,12 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 1 generator -1 -> (x^2 + y^2)*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 1 generator + 1: (x^2 + y^2)*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> is_zero(M) false @@ -1051,13 +1034,13 @@ julia> B = R[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Subquotient of Submodule with 2 generators -1 -> x*e[1] -2 -> y*e[1] -by Submodule with 3 generators -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Subquotient of submodule with 2 generators + 1: x*e[1] + 2: y*e[1] +by submodule with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> is_zero(M[1]) false @@ -1082,13 +1065,13 @@ julia> B = Rg[x^2; y^3; z^4] [z^4] julia> M = SubquoModule(F, A, B) -Graded subquotient of submodule of F generated by -1 -> x*e[1] -2 -> y*e[1] -by submodule of F generated by -1 -> x^2*e[1] -2 -> y^3*e[1] -3 -> z^4*e[1] +Graded subquotient of graded submodule of F with 2 generators + 1: x*e[1] + 2: y*e[1] +by graded submodule of F with 3 generators + 1: x^2*e[1] + 2: y^3*e[1] + 3: z^4*e[1] julia> is_zero(M[1]) false diff --git a/src/Modules/homological-algebra.jl b/src/Modules/homological-algebra.jl index 0610f5e49c22..e748b96c7add 100644 --- a/src/Modules/homological-algebra.jl +++ b/src/Modules/homological-algebra.jl @@ -31,14 +31,14 @@ julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x]) [ -y x] julia> M = quo_object(F,U) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 4 generators -1 -> (x^3 - y^2)*e[1] -2 -> (x^3 - y^2)*e[2] -3 -> -x^2*e[1] + y*e[2] -4 -> -y*e[1] + x*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 4 generators + 1: (x^3 - y^2)*e[1] + 2: (x^3 - y^2)*e[2] + 3: -x^2*e[1] + y*e[2] + 4: -y*e[1] + x*e[2] julia> fitting_ideal(M, -1) Ideal generated by @@ -106,14 +106,14 @@ julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x]) [ -y x] julia> M = quo_object(F,U) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 4 generators -1 -> (x^3 - y^2)*e[1] -2 -> (x^3 - y^2)*e[2] -3 -> -x^2*e[1] + y*e[2] -4 -> -y*e[1] + x*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 4 generators + 1: (x^3 - y^2)*e[1] + 2: (x^3 - y^2)*e[2] + 3: -x^2*e[1] + y*e[2] + 4: -y*e[1] + x*e[2] julia> is_flat(M) false @@ -163,14 +163,14 @@ julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x]) [ -y x] julia> M = quo_object(F,U) -Subquotient of Submodule with 2 generators -1 -> e[1] -2 -> e[2] -by Submodule with 4 generators -1 -> (x^3 - y^2)*e[1] -2 -> (x^3 - y^2)*e[2] -3 -> -x^2*e[1] + y*e[2] -4 -> -y*e[1] + x*e[2] +Subquotient of submodule with 2 generators + 1: e[1] + 2: e[2] +by submodule with 4 generators + 1: (x^3 - y^2)*e[1] + 2: (x^3 - y^2)*e[2] + 3: -x^2*e[1] + y*e[2] + 4: -y*e[1] + x*e[2] julia> non_flat_locus(M) Ideal generated by @@ -295,27 +295,27 @@ julia> V = [x*y, x*z, y*z] y*z julia> koszul_homology(V, F, 0) -Subquotient of Submodule with 1 generator -1 -> e[1]^e[2]^e[3] -by Submodule with 3 generators -1 -> y*z*e[1]^e[2]^e[3] -2 -> -x*z*e[1]^e[2]^e[3] -3 -> x*y*e[1]^e[2]^e[3] +Subquotient of submodule with 1 generator + 1: e[1]^e[2]^e[3] +by submodule with 3 generators + 1: y*z*e[1]^e[2]^e[3] + 2: -x*z*e[1]^e[2]^e[3] + 3: x*y*e[1]^e[2]^e[3] julia> koszul_homology(V, F, 1) -Subquotient of Submodule with 2 generators -1 -> y*e[1]^e[3] \otimes e[1] + z*e[2]^e[3] \otimes e[1] -2 -> x*e[1]^e[2] \otimes e[1] - z*e[2]^e[3] \otimes e[1] -by Submodule with 3 generators -1 -> -x*z*e[1]^e[2] \otimes e[1] - y*z*e[1]^e[3] \otimes e[1] -2 -> x*y*e[1]^e[2] \otimes e[1] - y*z*e[2]^e[3] \otimes e[1] -3 -> x*y*e[1]^e[3] \otimes e[1] + x*z*e[2]^e[3] \otimes e[1] +Subquotient of submodule with 2 generators + 1: y*e[1]^e[3] \otimes e[1] + z*e[2]^e[3] \otimes e[1] + 2: x*e[1]^e[2] \otimes e[1] - z*e[2]^e[3] \otimes e[1] +by submodule with 3 generators + 1: -x*z*e[1]^e[2] \otimes e[1] - y*z*e[1]^e[3] \otimes e[1] + 2: x*y*e[1]^e[2] \otimes e[1] - y*z*e[2]^e[3] \otimes e[1] + 3: x*y*e[1]^e[3] \otimes e[1] + x*z*e[2]^e[3] \otimes e[1] julia> koszul_homology(V, F, 2) -Subquotient of Submodule with 1 generator -1 -> x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1] -by Submodule with 1 generator -1 -> x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1] +Subquotient of submodule with 1 generator + 1: x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1] +by submodule with 1 generator + 1: x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1] ``` ```jldoctest @@ -326,27 +326,27 @@ julia> TC = ideal(R, [x*z-y^2, w*z-x*y, w*y-x^2]); julia> F = free_module(R, 1); julia> koszul_homology(gens(TC), F, 0) -Subquotient of Submodule with 1 generator -1 -> e[1]^e[2]^e[3] -by Submodule with 3 generators -1 -> (w*y - x^2)*e[1]^e[2]^e[3] -2 -> (-w*z + x*y)*e[1]^e[2]^e[3] -3 -> (x*z - y^2)*e[1]^e[2]^e[3] +Subquotient of submodule with 1 generator + 1: e[1]^e[2]^e[3] +by submodule with 3 generators + 1: (w*y - x^2)*e[1]^e[2]^e[3] + 2: (-w*z + x*y)*e[1]^e[2]^e[3] + 3: (x*z - y^2)*e[1]^e[2]^e[3] julia> koszul_homology(gens(TC), F, 1) -Subquotient of Submodule with 2 generators -1 -> z*e[1]^e[2] \otimes e[1] + y*e[1]^e[3] \otimes e[1] + x*e[2]^e[3] \otimes e[1] -2 -> y*e[1]^e[2] \otimes e[1] + x*e[1]^e[3] \otimes e[1] + w*e[2]^e[3] \otimes e[1] -by Submodule with 3 generators -1 -> (-w*z + x*y)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[1]^e[3] \otimes e[1] -2 -> (x*z - y^2)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[2]^e[3] \otimes e[1] -3 -> (x*z - y^2)*e[1]^e[3] \otimes e[1] + (w*z - x*y)*e[2]^e[3] \otimes e[1] +Subquotient of submodule with 2 generators + 1: z*e[1]^e[2] \otimes e[1] + y*e[1]^e[3] \otimes e[1] + x*e[2]^e[3] \otimes e[1] + 2: y*e[1]^e[2] \otimes e[1] + x*e[1]^e[3] \otimes e[1] + w*e[2]^e[3] \otimes e[1] +by submodule with 3 generators + 1: (-w*z + x*y)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[1]^e[3] \otimes e[1] + 2: (x*z - y^2)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[2]^e[3] \otimes e[1] + 3: (x*z - y^2)*e[1]^e[3] \otimes e[1] + (w*z - x*y)*e[2]^e[3] \otimes e[1] julia> koszul_homology(gens(TC), F, 2) -Subquotient of Submodule with 1 generator -1 -> (-x*z + y^2)*e[1] \otimes e[1] + (-w*z + x*y)*e[2] \otimes e[1] + (-w*y + x^2)*e[3] \otimes e[1] -by Submodule with 1 generator -1 -> (x*z - y^2)*e[1] \otimes e[1] + (w*z - x*y)*e[2] \otimes e[1] + (w*y - x^2)*e[3] \otimes e[1] +Subquotient of submodule with 1 generator + 1: (-x*z + y^2)*e[1] \otimes e[1] + (-w*z + x*y)*e[2] \otimes e[1] + (-w*y + x^2)*e[3] \otimes e[1] +by submodule with 1 generator + 1: (x*z - y^2)*e[1] \otimes e[1] + (w*z - x*y)*e[2] \otimes e[1] + (w*y - x^2)*e[3] \otimes e[1] ``` """ function koszul_homology(V::Vector{T}, F::ModuleFP{T}, i::Int) where T <: MPolyRingElem diff --git a/src/Rings/FreeAssociativeAlgebraIdeal.jl b/src/Rings/FreeAssociativeAlgebraIdeal.jl index 3547db25a148..60d41b63e628 100644 --- a/src/Rings/FreeAssociativeAlgebraIdeal.jl +++ b/src/Rings/FreeAssociativeAlgebraIdeal.jl @@ -173,10 +173,10 @@ julia> I = ideal([f1, f2]); julia> gb = groebner_basis(I, 3; protocol=false) Ideal generating system with elements - 1 -> x*y + y*z - 2 -> x^2 + y^2 - 3 -> y^3 + y*z^2 - 4 -> y^2*x + y*z*y + 1: x*y + y*z + 2: x^2 + y^2 + 3: y^3 + y*z^2 + 4: y^2*x + y*z*y ``` """ function groebner_basis(I::FreeAssociativeAlgebraIdeal, diff --git a/src/Rings/groebner/f4.jl b/src/Rings/groebner/f4.jl index 4e889a5425ac..5ed611bbf64a 100644 --- a/src/Rings/groebner/f4.jl +++ b/src/Rings/groebner/f4.jl @@ -39,10 +39,10 @@ Ideal generated by julia> groebner_basis_f4(I) Gröbner basis with elements - 1 -> x + 2*y + 2*z + 100 - 2 -> y*z + 82*z^2 + 10*y + 40*z - 3 -> y^2 + 60*z^2 + 20*y + 81*z - 4 -> z^3 + 28*z^2 + 64*y + 13*z + 1: x + 2*y + 2*z + 100 + 2: y*z + 82*z^2 + 10*y + 40*z + 3: y^2 + 60*z^2 + 20*y + 81*z + 4: z^3 + 28*z^2 + 64*y + 13*z with respect to the ordering degrevlex([x, y, z]) ``` diff --git a/src/Rings/groebner/fglm.jl b/src/Rings/groebner/fglm.jl index 7e7b05754c14..b8ded21f0efd 100644 --- a/src/Rings/groebner/fglm.jl +++ b/src/Rings/groebner/fglm.jl @@ -24,22 +24,22 @@ Ideal generated by julia> groebner_basis(J, ordering=degrevlex(R), complete_reduction=true) Gröbner basis with elements - 1 -> x1 + 2*x2 + 2*x3 + 2*x4 + 100 - 2 -> x3^2 + 2*x2*x4 + 19*x3*x4 + 76*x4^2 + 72*x2 + 86*x3 + 42*x4 - 3 -> x2*x3 + 99*x2*x4 + 40*x3*x4 + 11*x4^2 + 65*x2 + 58*x3 + 30*x4 - 4 -> x2^2 + 2*x2*x4 + 30*x3*x4 + 45*x4^2 + 43*x2 + 72*x3 + 86*x4 - 5 -> x3*x4^2 + 46*x4^3 + 28*x2*x4 + 16*x3*x4 + 7*x4^2 + 58*x2 + 63*x3 + 15*x4 - 6 -> x2*x4^2 + 67*x4^3 + 56*x2*x4 + 58*x3*x4 + 45*x4^2 + 14*x2 + 86*x3 - 7 -> x4^4 + 65*x4^3 + 26*x2*x4 + 47*x3*x4 + 71*x4^2 + 37*x2 + 79*x3 + 100*x4 + 1: x1 + 2*x2 + 2*x3 + 2*x4 + 100 + 2: x3^2 + 2*x2*x4 + 19*x3*x4 + 76*x4^2 + 72*x2 + 86*x3 + 42*x4 + 3: x2*x3 + 99*x2*x4 + 40*x3*x4 + 11*x4^2 + 65*x2 + 58*x3 + 30*x4 + 4: x2^2 + 2*x2*x4 + 30*x3*x4 + 45*x4^2 + 43*x2 + 72*x3 + 86*x4 + 5: x3*x4^2 + 46*x4^3 + 28*x2*x4 + 16*x3*x4 + 7*x4^2 + 58*x2 + 63*x3 + 15*x4 + 6: x2*x4^2 + 67*x4^3 + 56*x2*x4 + 58*x3*x4 + 45*x4^2 + 14*x2 + 86*x3 + 7: x4^4 + 65*x4^3 + 26*x2*x4 + 47*x3*x4 + 71*x4^2 + 37*x2 + 79*x3 + 100*x4 with respect to the ordering degrevlex([x1, x2, x3, x4]) julia> Oscar._fglm(J.gb[degrevlex(R)], lex(R)) Gröbner basis with elements - 1 -> x4^8 + 36*x4^7 + 95*x4^6 + 39*x4^5 + 74*x4^4 + 7*x4^3 + 45*x4^2 + 98*x4 - 2 -> x3 + 53*x4^7 + 93*x4^6 + 74*x4^5 + 26*x4^4 + 56*x4^3 + 15*x4^2 + 88*x4 - 3 -> x2 + 25*x4^7 + 57*x4^6 + 13*x4^5 + 16*x4^4 + 78*x4^3 + 31*x4^2 + 16*x4 - 4 -> x1 + 46*x4^7 + 3*x4^6 + 28*x4^5 + 17*x4^4 + 35*x4^3 + 9*x4^2 + 97*x4 + 100 + 1: x4^8 + 36*x4^7 + 95*x4^6 + 39*x4^5 + 74*x4^4 + 7*x4^3 + 45*x4^2 + 98*x4 + 2: x3 + 53*x4^7 + 93*x4^6 + 74*x4^5 + 26*x4^4 + 56*x4^3 + 15*x4^2 + 88*x4 + 3: x2 + 25*x4^7 + 57*x4^6 + 13*x4^5 + 16*x4^4 + 78*x4^3 + 31*x4^2 + 16*x4 + 4: x1 + 46*x4^7 + 3*x4^6 + 28*x4^5 + 17*x4^4 + 35*x4^3 + 9*x4^2 + 97*x4 + 100 with respect to the ordering lex([x1, x2, x3, x4]) ``` @@ -127,25 +127,25 @@ Ideal generated by julia> Oscar._compute_groebner_basis_using_fglm(I, lex(R)) Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y + 1: y^2 + y + 2: x*y - y + 3: x^2 + y with respect to the ordering lex([x, y]) julia> I.gb[lex(R)] Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y + 1: y^2 + y + 2: x*y - y + 3: x^2 + y with respect to the ordering lex([x, y]) julia> I.gb[degrevlex(R)] Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y + 1: y^2 + y + 2: x*y - y + 3: x^2 + y with respect to the ordering degrevlex([x, y]) ``` diff --git a/src/Rings/groebner/general.jl b/src/Rings/groebner/general.jl index a082f9512d13..d033831fd63f 100644 --- a/src/Rings/groebner/general.jl +++ b/src/Rings/groebner/general.jl @@ -17,14 +17,14 @@ julia> R,(x,y) = polynomial_ring(QQ, [:x,:y]) julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) Ideal generating system with elements - 1 -> x*y - 3*x - 2 -> -2*x^2*y + y^3 + 1: x*y - 3*x + 2: -2*x^2*y + y^3 julia> B = Oscar._compute_standard_basis(A, degrevlex(R)) Gröbner basis with elements - 1 -> x*y - 3*x - 2 -> y^3 - 6*x^2 - 3 -> 2*x^3 - 9*x + 1: x*y - 3*x + 2: y^3 - 6*x^2 + 3: 2*x^3 - 9*x with respect to the ordering degrevlex([x, y]) ``` @@ -72,8 +72,8 @@ julia> I = ideal([x*(x+1), x^2-y^2+(x-2)*y]); julia> standard_basis(I, ordering = negdegrevlex(R)) Standard basis with elements - 1 -> x - 2 -> y + 1: x + 2: y with respect to the ordering negdegrevlex([x, y]) ``` @@ -176,9 +176,9 @@ julia> I = ideal(R, [y-x^2, z-x^3]); julia> G = groebner_basis(I) Gröbner basis with elements - 1 -> y^2 - x*z - 2 -> x*y - z - 3 -> x^2 - y + 1: y^2 - x*z + 2: x*y - z + 3: x^2 - y with respect to the ordering degrevlex([x, y, z]) @@ -193,10 +193,10 @@ true julia> groebner_basis(I, ordering = lex(R)) Gröbner basis with elements - 1 -> y^3 - z^2 - 2 -> x*z - y^2 - 3 -> x*y - z - 4 -> x^2 - y + 1: y^3 - z^2 + 2: x*z - y^2 + 3: x*y - z + 4: x^2 - y with respect to the ordering lex([x, y, z]) ``` @@ -207,10 +207,10 @@ julia> I = ideal(R, [x*y-3*x^4,y^3-2*x^6*y]); julia> groebner_basis(I) Gröbner basis with elements - 1 -> 3*x^4 - x*y - 2 -> 2*x^3*y^2 - 3*y^3 - 3 -> x*y^3 - 4 -> y^4 + 1: 3*x^4 - x*y + 2: 2*x^3*y^2 - 3*y^3 + 3: x*y^3 + 4: y^4 with respect to the ordering wdegrevlex([x, y], [1, 3]) ``` diff --git a/src/Rings/groebner/generators.jl b/src/Rings/groebner/generators.jl index fe140d494c90..80b61cc0d19d 100644 --- a/src/Rings/groebner/generators.jl +++ b/src/Rings/groebner/generators.jl @@ -24,9 +24,9 @@ julia> Oscar.groebner_assure(I, degrevlex(R)); julia> I.gb[degrevlex(R)] Gröbner basis with elements - 1 -> x*y - 3*x - 2 -> y^3 - 6*x^2 - 3 -> 2*x^3 - 9*x + 1: x*y - 3*x + 2: y^3 - 6*x^2 + 3: 2*x^3 - 9*x with respect to the ordering degrevlex([x, y]) ``` diff --git a/src/Rings/groebner/modular.jl b/src/Rings/groebner/modular.jl index c0a67059fc6d..73f3c01ab610 100644 --- a/src/Rings/groebner/modular.jl +++ b/src/Rings/groebner/modular.jl @@ -18,9 +18,9 @@ Ideal generated by julia> groebner_basis_modular(I) Gröbner basis with elements - 1 -> y^3 + 403//3583947*y - 2 -> x^2 + 1209 - 3 -> x*y + 3279*y^2 + 1: y^3 + 403//3583947*y + 2: x^2 + 1209 + 3: x*y + 3279*y^2 with respect to the ordering degrevlex([x, y, z]) ``` diff --git a/src/Rings/groebner/reduce.jl b/src/Rings/groebner/reduce.jl index 6d200333b0d6..2cdbfadef5fe 100644 --- a/src/Rings/groebner/reduce.jl +++ b/src/Rings/groebner/reduce.jl @@ -289,9 +289,9 @@ julia> I = ideal(R, [x*y, y^3]); julia> gb = groebner_basis(J) Gröbner basis with elements - 1 -> x*y + 10*y^2 - 2 -> x^2 - 3 -> y^3 + 1: x*y + 10*y^2 + 2: x^2 + 3: y^3 with respect to the ordering degrevlex([x, y, z]) @@ -593,8 +593,8 @@ false julia> standard_basis(I, ordering=neglex(R)) Standard basis with elements - 1 -> y - 2 -> x^2 + 1: y + 2: x^2 with respect to the ordering neglex([x, y]) @@ -646,9 +646,9 @@ false julia> groebner_basis(I, ordering=lex(R)) Gröbner basis with elements - 1 -> y^2 + y - 2 -> x*y - y - 3 -> x^2 + y + 1: y^2 + y + 2: x*y - y + 3: x^2 + y with respect to the ordering lex([x, y]) diff --git a/src/Rings/groebner/transformation-matrix.jl b/src/Rings/groebner/transformation-matrix.jl index f6d392a77e1d..301a6be0c6ac 100644 --- a/src/Rings/groebner/transformation-matrix.jl +++ b/src/Rings/groebner/transformation-matrix.jl @@ -15,8 +15,8 @@ julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]) julia> A = Oscar.IdealGens([x*y-3*x,y^3-2*x^2*y]) Ideal generating system with elements - 1 -> x*y - 3*x - 2 -> -2*x^2*y + y^3 + 1: x*y - 3*x + 2: -2*x^2*y + y^3 julia> B,m = Oscar._compute_standard_basis_with_transform(A, degrevlex(R)) (Ideal generating system with 3 elements with associated ordering degrevlex([x, y]), [1 2*x -2*x^2+y^2+3*y+9; 0 1 -x]) diff --git a/src/Rings/mpoly-affine-algebras.jl b/src/Rings/mpoly-affine-algebras.jl index ea73898c699c..56c9c926fb0d 100644 --- a/src/Rings/mpoly-affine-algebras.jl +++ b/src/Rings/mpoly-affine-algebras.jl @@ -89,9 +89,9 @@ Ideal generated by julia> groebner_basis(I, ordering = lex(base_ring(I))) Gröbner basis with elements - 1 -> z^3 - z^2 - 2 -> y^2 + y*z - y + z^2 - z - 3 -> x + y + z - 1 + 1: z^3 - z^2 + 2: y^2 + y*z - y + z^2 - z + 3: x + y + z - 1 with respect to the ordering lex([x, y, z]) ``` diff --git a/src/Rings/mpoly.jl b/src/Rings/mpoly.jl index 5bb259b88b82..ce2543562705 100644 --- a/src/Rings/mpoly.jl +++ b/src/Rings/mpoly.jl @@ -253,7 +253,7 @@ function show(io::IO, ::MIME"text/plain", I::IdealGens) end print(io, " with elements", Indent()) for (i, g) in enumerate(gens(I)) - print(io, "\n", i, " -> ", OscarPair(g, I.ord)) + print(io, "\n", i, ": ", OscarPair(g, I.ord)) end print(io, Dedent()) print(io, "\nwith respect to the ordering") @@ -262,7 +262,7 @@ function show(io::IO, ::MIME"text/plain", I::IdealGens) print(io, "Ideal generating system with elements") print(io, Indent()) for (i,g) in enumerate(gens(I)) - print(io, "\n", i, " -> ", g) + print(io, "\n", i, ": ", g) end print(io, Dedent()) if isdefined(I, :ord) @@ -405,8 +405,8 @@ julia> g = generating_system(I); julia> set_ordering(g, degrevlex(gens(R))) Ideal generating system with elements - 1 -> x0*x1 - 2 -> x2 + 1: x0*x1 + 2: x2 with associated ordering degrevlex([x0, x1, x2]) ``` @@ -821,8 +821,8 @@ julia> I = ideal([x*(x+1), x^2-y^2+(x-2)*y]); julia> generating_system(I) Ideal generating system with elements - 1 -> x^2 + x - 2 -> x^2 + x*y - y^2 - 2*y + 1: x^2 + x + 2: x^2 + x*y - y^2 - 2*y ``` """ function generating_system(I::MPolyIdeal) diff --git a/src/TropicalGeometry/groebner_fan.jl b/src/TropicalGeometry/groebner_fan.jl index df58dd72723a..0ba7939fb4f7 100644 --- a/src/TropicalGeometry/groebner_fan.jl +++ b/src/TropicalGeometry/groebner_fan.jl @@ -172,8 +172,8 @@ Ideal generated by julia> G = groebner_basis(I,ordering=lex(Qx)) Gröbner basis with elements - 1 -> x2 + x3 - 2 -> x1 + 1: x2 + x3 + 2: x1 with respect to the ordering lex([x1, x2, x3]) diff --git a/test/Modules/ExteriorPowers.jl b/test/Modules/ExteriorPowers.jl index 32cf891f4436..25a81d8ee1ce 100644 --- a/test/Modules/ExteriorPowers.jl +++ b/test/Modules/ExteriorPowers.jl @@ -130,8 +130,8 @@ end u = (F[1], F[4], F[3]) @test "$(mm(v))" == "e[1]^e[3]^e[4]" - #@test "$(F3)" == "⋀^3(Free module of rank 5 over Multivariate polynomial ring in 5 variables over QQ)" - @test "$(F3)" == "3rd exterior power of Free module of rank 5 over Multivariate polynomial ring in 5 variables over QQ" + #@test "$(F3)" == "⋀^3(Free module of rank 5 over multivariate polynomial ring in 5 variables over QQ)" + @test "$(F3)" == "3rd exterior power of Free module of rank 5 over multivariate polynomial ring in 5 variables over QQ" eu = sum(f*e for (f, e) in zip(gens(R), gens(F))) K = koszul_complex(eu) diff --git a/test/book/cornerstones/algebraic-geometry/circlepar.jlcon b/test/book/cornerstones/algebraic-geometry/circlepar.jlcon index 3e92366f3372..de91b649e54c 100644 --- a/test/book/cornerstones/algebraic-geometry/circlepar.jlcon +++ b/test/book/cornerstones/algebraic-geometry/circlepar.jlcon @@ -4,8 +4,8 @@ julia> I = ideal(R, [x^2 + y^2 - 1, y - t*x - 1]); julia> Gy = groebner_basis(I, ordering = lex([y, x, t]), complete_reduction=true) Gröbner basis with elements - 1 -> x^2*t^2 + x^2 + 2*x*t - 2 -> y - x*t - 1 + 1: x^2*t^2 + x^2 + 2*x*t + 2: y - x*t - 1 with respect to the ordering lex([y, x, t]) @@ -14,10 +14,10 @@ julia> factor(Gy[1]) julia> Gx = groebner_basis(I, ordering = lex([x, y, t]), complete_reduction=true) Gröbner basis with elements - 1 -> y^2*t^2 + y^2 - 2*y - t^2 + 1 - 2 -> x*t - y + 1 - 3 -> x*y - x + y^2*t - t - 4 -> x^2 + y^2 - 1 + 1: y^2*t^2 + y^2 - 2*y - t^2 + 1 + 2: x*t - y + 1 + 3: x*y - x + y^2*t - t + 4: x^2 + y^2 - 1 with respect to the ordering lex([x, y, t]) diff --git a/test/book/cornerstones/algebraic-geometry/ex11.jlcon b/test/book/cornerstones/algebraic-geometry/ex11.jlcon index 3f903771e98b..ff56bef0b7de 100644 --- a/test/book/cornerstones/algebraic-geometry/ex11.jlcon +++ b/test/book/cornerstones/algebraic-geometry/ex11.jlcon @@ -4,8 +4,8 @@ julia> I = ideal(R, [x^2+y^2+2*z^2-8, x^2-y^2-z^2+1, x-y+z]); julia> groebner_basis(I, ordering = lex(R), complete_reduction = true) Gröbner basis with elements - 1 -> 6*z^4 - 18*z^2 + 1 - 2 -> y + 3*z^3 - 9*z - 3 -> x + 3*z^3 - 8*z + 1: 6*z^4 - 18*z^2 + 1 + 2: y + 3*z^3 - 9*z + 3: x + 3*z^3 - 8*z with respect to the ordering lex([x, y, z]) diff --git a/test/book/cornerstones/algebraic-geometry/ex314.jlcon b/test/book/cornerstones/algebraic-geometry/ex314.jlcon index 3a15caec9592..dc5c7b375da0 100644 --- a/test/book/cornerstones/algebraic-geometry/ex314.jlcon +++ b/test/book/cornerstones/algebraic-geometry/ex314.jlcon @@ -25,21 +25,21 @@ julia> p1 == ideal(minors(m3x3[1:3, 1:2],2)) true julia> mp1 = ideal_as_module(p1) -Graded submodule of S^1 -1 -> (-x_1*x_3 + x_2^2)*e[1] -2 -> (-x_0*x_3 + x_1*x_2)*e[1] -3 -> (-x_0*x_2 + x_1^2)*e[1] +Graded submodule of S^1 with 3 generators + 1: (-x_1*x_3 + x_2^2)*e[1] + 2: (-x_0*x_3 + x_1*x_2)*e[1] + 3: (-x_0*x_2 + x_1^2)*e[1] represented as subquotient with no relations julia> M1, _ = quo(ambient_free_module(mp1), mp1); julia> M1 -Graded subquotient of submodule of S^1 generated by -1 -> e[1] -by submodule of S^1 generated by -1 -> (-x_1*x_3 + x_2^2)*e[1] -2 -> (-x_0*x_3 + x_1*x_2)*e[1] -3 -> (-x_0*x_2 + x_1^2)*e[1] +Graded subquotient of graded submodule of S^1 with 1 generator + 1: e[1] +by graded submodule of S^1 with 3 generators + 1: (-x_1*x_3 + x_2^2)*e[1] + 2: (-x_0*x_3 + x_1*x_2)*e[1] + 3: (-x_0*x_2 + x_1^2)*e[1] julia> mJ = ideal_as_module(J); @@ -50,13 +50,13 @@ julia> homM1M, psi = hom(M1, M); julia> hom1, tohomM1M = prune_with_map(homM1M); julia> hom1 -Graded subquotient of submodule of S^2 generated by -1 -> e[1] -2 -> e[2] -by submodule of S^2 generated by -1 -> -x_2*e[1] + x_3*e[2] -2 -> x_0*e[1] - x_1*e[2] -3 -> -x_1*e[1] + x_2*e[2] +Graded subquotient of graded submodule of S^2 with 2 generators + 1: e[1] + 2: e[2] +by graded submodule of S^2 with 3 generators + 1: -x_2*e[1] + x_3*e[2] + 2: x_0*e[1] - x_1*e[2] + 3: -x_1*e[1] + x_2*e[2] julia> degrees_of_generators(hom1) 2-element Vector{FinGenAbGroupElem}: @@ -64,16 +64,18 @@ julia> degrees_of_generators(hom1) [2] julia> phi1 = psi(tohomM1M(hom1[1])) -M1 -> M -e[1] -> (-x_0*x_3 + x_1*x_2)*e[1] Graded module homomorphism of degree [2] - + from M1 + to M +defined by + e[1] -> (-x_0*x_3 + x_1*x_2)*e[1] julia> phi2 = psi(tohomM1M(hom1[2])) -M1 -> M -e[1] -> (-x_0*x_2 + x_1^2)*e[1] Graded module homomorphism of degree [2] - + from M1 + to M +defined by + e[1] -> (-x_0*x_2 + x_1^2)*e[1] julia> kerphi2, _ = kernel(phi2); @@ -81,12 +83,12 @@ julia> iszero(kerphi2) true julia> MmodM1 = cokernel(phi2) -Graded subquotient of submodule of S^1 generated by -1 -> e[1] -by submodule of S^1 generated by -1 -> (x_1*x_3 - x_2^2)*e[1] -2 -> (-x_0^2*x_3 + 2*x_0*x_1*x_2 - x_1^3)*e[1] -3 -> (-x_0*x_2 + x_1^2)*e[1] +Graded subquotient of graded submodule of S^1 with 1 generator + 1: e[1] +by graded submodule of S^1 with 3 generators + 1: (x_1*x_3 - x_2^2)*e[1] + 2: (-x_0^2*x_3 + 2*x_0*x_1*x_2 - x_1^3)*e[1] + 3: (-x_0*x_2 + x_1^2)*e[1] julia> p2 = ideal([x[1],x[2],x[3]]) Ideal generated by diff --git a/test/book/cornerstones/algebraic-geometry/exres.jlcon b/test/book/cornerstones/algebraic-geometry/exres.jlcon index b6d52ae6d476..8e046ee48250 100644 --- a/test/book/cornerstones/algebraic-geometry/exres.jlcon +++ b/test/book/cornerstones/algebraic-geometry/exres.jlcon @@ -19,26 +19,32 @@ julia> FA[3] Graded free module S^1([-4]) + S^1([-5]) of rank 2 over S julia> map(FA,1) -S^5 -> S^1 -e[1] -> (-w*z + y^2)*e[1] -e[2] -> (x*y - z^2)*e[1] -e[3] -> (-w*y + x^2)*e[1] -e[4] -> (w*x - y*z)*e[1] -e[5] -> (w^2 - x*z)*e[1] Homogeneous module homomorphism + from S^5 + to S^1 +defined by + e[1] -> (-w*z + y^2)*e[1] + e[2] -> (x*y - z^2)*e[1] + e[3] -> (-w*y + x^2)*e[1] + e[4] -> (w*x - y*z)*e[1] + e[5] -> (w^2 - x*z)*e[1] julia> map(FA,2) -S^6 -> S^5 -e[1] -> -x*e[1] + y*e[2] - z*e[4] -e[2] -> w*e[1] - x*e[2] + y*e[3] + z*e[5] -e[3] -> -w*e[3] + x*e[4] - y*e[5] -e[4] -> z*e[1] - w*e[2] + y*e[4] -e[5] -> z*e[3] - w*e[4] + x*e[5] -e[6] -> (-w^2 + x*z)*e[1] + (-w*z + y^2)*e[5] Homogeneous module homomorphism + from S^6 + to S^5 +defined by + e[1] -> -x*e[1] + y*e[2] - z*e[4] + e[2] -> w*e[1] - x*e[2] + y*e[3] + z*e[5] + e[3] -> -w*e[3] + x*e[4] - y*e[5] + e[4] -> z*e[1] - w*e[2] + y*e[4] + e[5] -> z*e[3] - w*e[4] + x*e[5] + e[6] -> (-w^2 + x*z)*e[1] + (-w*z + y^2)*e[5] julia> map(FA,3) -S^2 -> S^6 -e[1] -> -w*e[2] - y*e[3] + x*e[4] - e[6] -e[2] -> (-w^2 + x*z)*e[1] + y*z*e[2] + z^2*e[3] - w*y*e[4] + (w*z - y^2)*e[5] + x*e[6] Homogeneous module homomorphism + from S^2 + to S^6 +defined by + e[1] -> -w*e[2] - y*e[3] + x*e[4] - e[6] + e[2] -> (-w^2 + x*z)*e[1] + y*z*e[2] + z^2*e[3] - w*y*e[4] + (w*z - y^2)*e[5] + x*e[6] diff --git a/test/book/specialized/decker-schmitt-invariant-theory/bertin.jlcon b/test/book/specialized/decker-schmitt-invariant-theory/bertin.jlcon index 0403110b4fcd..2d3e88c4b5b6 100644 --- a/test/book/specialized/decker-schmitt-invariant-theory/bertin.jlcon +++ b/test/book/specialized/decker-schmitt-invariant-theory/bertin.jlcon @@ -20,11 +20,11 @@ julia> secondary_invariants(RG) julia> M, MtoR, StoR = module_syzygies(RG); julia> M -Subquotient of Submodule with 5 generators -1 -> e[1] -2 -> e[2] -3 -> e[3] -4 -> e[4] -5 -> e[5] -by Submodule with 1 generator -1 -> t2*e[2] + (t2 + t3)*e[3] + t1*e[4] +Subquotient of submodule with 5 generators + 1: e[1] + 2: e[2] + 3: e[3] + 4: e[4] + 5: e[5] +by submodule with 1 generator + 1: t2*e[2] + (t2 + t3)*e[3] + t1*e[4] diff --git a/test/book/specialized/eder-mohr-ideal-theoretic/gbeliminate.jlcon b/test/book/specialized/eder-mohr-ideal-theoretic/gbeliminate.jlcon index 285858f608cd..fc01033f2b82 100644 --- a/test/book/specialized/eder-mohr-ideal-theoretic/gbeliminate.jlcon +++ b/test/book/specialized/eder-mohr-ideal-theoretic/gbeliminate.jlcon @@ -6,10 +6,10 @@ julia> I = ideal(R, [x^2 + y + z - 1, x + y^2 + z - 1, x + y + z^2 - 1]); julia> groebner_basis(I, ordering = o) Gröbner basis with elements - 1 -> z^6 - 4*z^4 + 4*z^3 - z^2 - 2 -> 2*y*z^2 + z^4 - z^2 - 3 -> x + y + z^2 - 1 - 4 -> y^2 + x + z - 1 + 1: z^6 - 4*z^4 + 4*z^3 - z^2 + 2: 2*y*z^2 + z^4 - z^2 + 3: x + y + z^2 - 1 + 4: y^2 + x + z - 1 with respect to the ordering degrevlex([x, y])*degrevlex([z]) @@ -19,8 +19,8 @@ Ideal generated by julia> groebner_basis(I) Gröbner basis with elements - 1 -> z^2 + x + y - 1 - 2 -> y^2 + x + z - 1 - 3 -> x^2 + y + z - 1 + 1: z^2 + x + y - 1 + 2: y^2 + x + z - 1 + 3: x^2 + y + z - 1 with respect to the ordering degrevlex([x, y, z]) diff --git a/test/book/specialized/eder-mohr-ideal-theoretic/lcis.jlcon b/test/book/specialized/eder-mohr-ideal-theoretic/lcis.jlcon index af11cab95091..e661fe2bdbeb 100644 --- a/test/book/specialized/eder-mohr-ideal-theoretic/lcis.jlcon +++ b/test/book/specialized/eder-mohr-ideal-theoretic/lcis.jlcon @@ -3,17 +3,17 @@ julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); julia> I = ideal(R, [x*y, x*z, y*z]); julia> conorm = subquotient(matrix(gens(I)), matrix(gens(I^2))) -Subquotient of Submodule with 3 generators -1 -> x*y*e[1] -2 -> x*z*e[1] -3 -> y*z*e[1] -by Submodule with 6 generators -1 -> x^2*y^2*e[1] -2 -> x^2*y*z*e[1] -3 -> x*y^2*z*e[1] -4 -> x^2*z^2*e[1] -5 -> x*y*z^2*e[1] -6 -> y^2*z^2*e[1] +Subquotient of submodule with 3 generators + 1: x*y*e[1] + 2: x*z*e[1] + 3: y*z*e[1] +by submodule with 6 generators + 1: x^2*y^2*e[1] + 2: x^2*y*z*e[1] + 3: x*y^2*z*e[1] + 4: x^2*z^2*e[1] + 5: x*y*z^2*e[1] + 6: y^2*z^2*e[1] julia> fitting_ideal(conorm, 1) Ideal generated by From 852b6d1a33ed53581040b876a816bb33fac714aa Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Wed, 30 Oct 2024 10:14:55 +0100 Subject: [PATCH 22/84] PolyhedralGeometry: minor fixes (#4253) * Polyhedron: fix one scalar detection case * tests: move some helper functions to if block to avoid warnings on duplicate include --- src/PolyhedralGeometry/helpers.jl | 4 +++ test/PolyhedralGeometry/scalar_types.jl | 5 ++++ test/PolyhedralGeometry/setup_tests.jl | 34 ++++++++++++------------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/PolyhedralGeometry/helpers.jl b/src/PolyhedralGeometry/helpers.jl index dedeebea8ba9..45d876183d92 100644 --- a/src/PolyhedralGeometry/helpers.jl +++ b/src/PolyhedralGeometry/helpers.jl @@ -611,6 +611,10 @@ function _parent_or_coefficient_field(::Type{T}, x, y...) where {T<:scalar_types missing end +function _parent_or_coefficient_field(::Type{T}, c::Tuple) where {T<:FieldElem} + return _parent_or_coefficient_field(T, c...) +end + function _determine_parent_and_scalar(f::Union{Field,ZZRing}, x...) _check_field_polyhedral(elem_type(f)) return (f, elem_type(f)) diff --git a/test/PolyhedralGeometry/scalar_types.jl b/test/PolyhedralGeometry/scalar_types.jl index 450e0ccc6280..676e2645a7ad 100644 --- a/test/PolyhedralGeometry/scalar_types.jl +++ b/test/PolyhedralGeometry/scalar_types.jl @@ -96,6 +96,11 @@ ) @test number_field(coefficient_field(j)) == number_field(coefficient_field(jj)) end + let ng = n_gon(5) + (A,b) = halfspace_matrix_pair(facets(ng)) + @test typeof(polyhedron(A,b)) == typeof(ng) + @test coefficient_field(polyhedron(A,b)) == coefficient_field((ng)) + end end @testset "Cross scalar operations" begin diff --git a/test/PolyhedralGeometry/setup_tests.jl b/test/PolyhedralGeometry/setup_tests.jl index c6cc8613922f..af8e73b5f2e3 100644 --- a/test/PolyhedralGeometry/setup_tests.jl +++ b/test/PolyhedralGeometry/setup_tests.jl @@ -6,28 +6,28 @@ if !isdefined(Main, :_prepare_scalar_types) KK, (a1, a2) = embedded_number_field([x^2 - 2, x^3 - 5], [(0, 2), (0, 2)]) return [(f, elem_type(f)) for f in (QQ, K, KK)] end -end -function _check_im_perm_rows(inc::IncidenceMatrix, o) - oinc = incidence_matrix(o) - nr, nc = size(inc) - (nr, nc) == size(oinc) && - issetequal(Polymake.row.(Ref(inc), 1:nr), - Polymake.row.(Ref(oinc), 1:nr)) -end + function _check_im_perm_rows(inc::IncidenceMatrix, o) + oinc = incidence_matrix(o) + nr, nc = size(inc) + (nr, nc) == size(oinc) && + issetequal(Polymake.row.(Ref(inc), 1:nr), + Polymake.row.(Ref(oinc), 1:nr)) + end -_matrix_from_property(b::SubObjectIterator{<:Union{LinearHalfspace, LinearHyperplane}}) = permutedims(hcat([normal_vector(be) for be in b]...)) + _matrix_from_property(b::SubObjectIterator{<:Union{LinearHalfspace, LinearHyperplane}}) = permutedims(hcat([normal_vector(be) for be in b]...)) -_matrix_from_property(b::SubObjectIterator{<:Union{AffineHalfspace, AffineHyperplane}}) = permutedims(hcat([vcat(-negbias(be), normal_vector(be)) for be in b]...)) + _matrix_from_property(b::SubObjectIterator{<:Union{AffineHalfspace, AffineHyperplane}}) = permutedims(hcat([vcat(-negbias(be), normal_vector(be)) for be in b]...)) -# only used for cones that are linear halfspaces -_matrix_from_property(b::SubObjectIterator{Cone{T}}) where T = _matrix_from_property(SubObjectIterator{LinearHalfspace{T}}(b.Obj, b.Acc, b.n)) + # only used for cones that are linear halfspaces + _matrix_from_property(b::SubObjectIterator{Cone{T}}) where T = _matrix_from_property(SubObjectIterator{LinearHalfspace{T}}(b.Obj, b.Acc, b.n)) -_matrix_from_property(b::SubObjectIterator) = permutedims(hcat(b...)) + _matrix_from_property(b::SubObjectIterator) = permutedims(hcat(b...)) -_oscar_matrix_from_property(a, b::SubObjectIterator) = matrix(a, _matrix_from_property(b)) + _oscar_matrix_from_property(a, b::SubObjectIterator) = matrix(a, _matrix_from_property(b)) -function _polymake_matrix_from_property(b::SubObjectIterator) - m = _matrix_from_property(b) - return Polymake.Matrix{Oscar._scalar_type_to_polymake(eltype(m))}(m) + function _polymake_matrix_from_property(b::SubObjectIterator) + m = _matrix_from_property(b) + return Polymake.Matrix{Oscar._scalar_type_to_polymake(eltype(m))}(m) + end end From 5ee96e7ddbbce1e8642f4bda6b75d08818b3ee2c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 30 Oct 2024 13:01:34 +0100 Subject: [PATCH 23/84] Fix show method for FreeMod_dec (#4254) --- src/Modules/ModulesGraded.jl | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 9f53a64d670e..2aebb616c870 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -2046,7 +2046,7 @@ julia> R, (x,y) = graded_polynomial_ring(QQ, [:x, :y]) (Graded multivariate polynomial ring in 2 variables over QQ, MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}[x, y]) julia> free_module_dec(R,3) -Decorated free module of rank 3 over RR^3([0]) +Decorated free module of rank 3 over R ``` """ @@ -2107,23 +2107,9 @@ function show(io::IO, F::FreeMod_dec) @show_special(io, F) io = terse(io) + io = pretty(io) print(io, "Decorated free module of rank $(rank(F)) over ") - print(IOContext(io, :compact => true), base_ring(F)) - - i = 1 - while i < dim(F) - d = F.d[i] - j = 1 - while i+j <= dim(F) && d == F.d[i+j] - j += 1 - end - print(IOContext(io, :compact => true), base_ring(F), "^$j") - print(IOContext(io, :compact => true), "(", -d, ")") - if i+j < dim(F) - print(io, " + ") - end - i += j - end + print(IOContext(io, :compact => true), Lowercase(), base_ring(F)) end # Generic specialized show methods (formerly in Hecke) From cfb03fdee36f8becbed031152d223cd15e452128 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 30 Oct 2024 14:52:04 +0100 Subject: [PATCH 24/84] Version 1.2.0 (#4255) --- Project.toml | 2 +- README.md | 7 +++---- gap/OscarInterface/PackageInfo.g | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 2af6c6f1974b..a8f6d8da0485 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Oscar" uuid = "f1435218-dba5-11e9-1e4d-f1a5fab5fc13" authors = ["The OSCAR Team "] -version = "1.2.0-DEV" +version = "1.2.0" [deps] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" diff --git a/README.md b/README.md index 7e6dc79ccc37..5dba2c6a242c 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,7 @@ julia> using Oscar / _ \ / ___| / ___| / \ | _ \ | Combining ANTIC, GAP, Polymake, Singular | | | |\___ \| | / _ \ | |_) | | Type "?Oscar" for more information | |_| | ___) | |___ / ___ \| _ < | Manual: https://docs.oscar-system.org - \___/ |____/ \____/_/ \_\_| \_\ | Version 1.2.0-DEV - + \___/ |____/ \____/_/ \_\_| \_\ | Version 1.2.0 julia> k, a = quadratic_field(-5) (Imaginary quadratic field defined by x^2 + 5, sqrt(-5)) @@ -114,7 +113,7 @@ pm::Array > If you have used OSCAR in the preparation of a paper please cite it as described below: [OSCAR] - OSCAR -- Open Source Computer Algebra Research system, Version 1.2.0-DEV, + OSCAR -- Open Source Computer Algebra Research system, Version 1.2.0, The OSCAR Team, 2024. (https://www.oscar-system.org) [OSCAR-book] Wolfram Decker, Christian Eder, Claus Fieker, Max Horn, Michael Joswig, eds. @@ -127,7 +126,7 @@ If you are using BibTeX, you can use the following BibTeX entries: key = {OSCAR}, organization = {The OSCAR Team}, title = {OSCAR -- Open Source Computer Algebra Research system, - Version 1.2.0-DEV}, + Version 1.2.0}, year = {2024}, url = {https://www.oscar-system.org}, } diff --git a/gap/OscarInterface/PackageInfo.g b/gap/OscarInterface/PackageInfo.g index 975c342cf939..8a7bbc2c1a8c 100644 --- a/gap/OscarInterface/PackageInfo.g +++ b/gap/OscarInterface/PackageInfo.g @@ -10,8 +10,8 @@ SetPackageInfo( rec( PackageName := "OscarInterface", Subtitle := "GAP interface to OSCAR", -Version := "1.2.0-DEV", -Date := "18/06/2024", # dd/mm/yyyy format +Version := "1.2.0", +Date := "30/10/2024", # dd/mm/yyyy format License := "GPL-2.0-or-later", Persons := [ From c428b99c7976a344434eebbf28a878c08488297c Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Wed, 30 Oct 2024 15:37:56 +0100 Subject: [PATCH 25/84] Version 1.3.0-DEV (#4258) --- Project.toml | 2 +- README.md | 7 ++++--- gap/OscarInterface/PackageInfo.g | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index a8f6d8da0485..eb46435051c4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Oscar" uuid = "f1435218-dba5-11e9-1e4d-f1a5fab5fc13" authors = ["The OSCAR Team "] -version = "1.2.0" +version = "1.3.0-DEV" [deps] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" diff --git a/README.md b/README.md index 5dba2c6a242c..70f0b3d3033a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ julia> using Oscar / _ \ / ___| / ___| / \ | _ \ | Combining ANTIC, GAP, Polymake, Singular | | | |\___ \| | / _ \ | |_) | | Type "?Oscar" for more information | |_| | ___) | |___ / ___ \| _ < | Manual: https://docs.oscar-system.org - \___/ |____/ \____/_/ \_\_| \_\ | Version 1.2.0 + \___/ |____/ \____/_/ \_\_| \_\ | Version 1.3.0-DEV + julia> k, a = quadratic_field(-5) (Imaginary quadratic field defined by x^2 + 5, sqrt(-5)) @@ -113,7 +114,7 @@ pm::Array > If you have used OSCAR in the preparation of a paper please cite it as described below: [OSCAR] - OSCAR -- Open Source Computer Algebra Research system, Version 1.2.0, + OSCAR -- Open Source Computer Algebra Research system, Version 1.3.0-DEV, The OSCAR Team, 2024. (https://www.oscar-system.org) [OSCAR-book] Wolfram Decker, Christian Eder, Claus Fieker, Max Horn, Michael Joswig, eds. @@ -126,7 +127,7 @@ If you are using BibTeX, you can use the following BibTeX entries: key = {OSCAR}, organization = {The OSCAR Team}, title = {OSCAR -- Open Source Computer Algebra Research system, - Version 1.2.0}, + Version 1.3.0-DEV}, year = {2024}, url = {https://www.oscar-system.org}, } diff --git a/gap/OscarInterface/PackageInfo.g b/gap/OscarInterface/PackageInfo.g index 8a7bbc2c1a8c..651dee91d3ea 100644 --- a/gap/OscarInterface/PackageInfo.g +++ b/gap/OscarInterface/PackageInfo.g @@ -10,7 +10,7 @@ SetPackageInfo( rec( PackageName := "OscarInterface", Subtitle := "GAP interface to OSCAR", -Version := "1.2.0", +Version := "1.3.0-DEV", Date := "30/10/2024", # dd/mm/yyyy format License := "GPL-2.0-or-later", From 4ab6f4d1db55dff11f43629867aa76c1196a63af Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Wed, 30 Oct 2024 22:51:34 +0100 Subject: [PATCH 26/84] add argument check to G-set constructor (#4259) - add `check` argument check th default value `true` - if `check` is `true` then test whether the given action function fits to the given `seeds` - set `check` to `false` in situations where the function is known to fit --- src/Groups/gsets.jl | 36 ++++++++++++++++++++---------------- test/Groups/gsets.jl | 1 + 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index 77b35f8755a6..f5ce457194a4 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -41,8 +41,9 @@ The fields are action_function::Function seeds - function GSetByElements(G::T, fun::Function, seeds; closed::Bool = false) where {T<:Union{GAPGroup, FinGenAbGroup}} - @assert !isempty(seeds) + function GSetByElements(G::T, fun::Function, seeds; closed::Bool = false, check::Bool = true) where {T<:Union{GAPGroup, FinGenAbGroup}} + @req !isempty(seeds) "seeds for G-set must be nonempty" + check && @req hasmethod(fun, (typeof(first(seeds)), elem_type(T))) "action function does not fit to seeds" Omega = new{T,eltype(seeds)}(G, fun, seeds, Dict{Symbol,Any}()) closed && set_attribute!(Omega, :elements => unique!(collect(seeds))) return Omega @@ -117,7 +118,7 @@ end ## general method with explicit action function """ - gset(G::Union{GAPGroup, FinGenAbGroup}[, fun::Function], seeds, closed::Bool = false) + gset(G::Union{GAPGroup, FinGenAbGroup}[, fun::Function], seeds, closed::Bool = false, check::Bool = true) Return the G-set `Omega` that consists of the closure of the seeds `seeds` under the action of `G` defined by `fun`. @@ -130,6 +131,9 @@ a reasonable default, for example, if `G` is a `PermGroup` and `seeds` is a `Vector{T}` where `T` is one of `Int`, `Set{Int}`, `Vector{Int}`. +If `check` is set to `false` then it is *not* checked whether the entries +of `seeds` are valid as the first argument of `fun`. + If `closed` is set to `true` then `seeds` is assumed to be closed under the action of `G`. In this case, `collect(Omega)` is guaranteed to be equal to `collect(seeds)`; @@ -151,8 +155,8 @@ julia> length(gset(G, on_sets, [[1, 2]])) # action on unordered pairs 6 ``` """ -function gset(G::Union{GAPGroup, FinGenAbGroup}, fun::Function, seeds; closed::Bool = false) - return GSetByElements(G, fun, seeds; closed = closed) +function gset(G::Union{GAPGroup, FinGenAbGroup}, fun::Function, seeds; closed::Bool = false, check::Bool = true) + return GSetByElements(G, fun, seeds; closed = closed, check = check) end @@ -169,47 +173,47 @@ gset(G::T, seeds; closed::Bool = false) where T<:GAPGroup = gset_by_type(G, seed ## natural action of permutations on positive integers function gset_by_type(G::PermGroup, Omega, ::Type{T}; closed::Bool = false) where T<:IntegerUnion - return GSetByElements(G, ^, Omega; closed = closed) + return GSetByElements(G, ^, Omega; closed = closed, check = false) end ## action of permutations on sets of positive integers function gset_by_type(G::PermGroup, Omega, ::Type{T}; closed::Bool = false) where T<:Set{T2} where T2<:IntegerUnion - return GSetByElements(G, on_sets, Omega; closed = closed) + return GSetByElements(G, on_sets, Omega; closed = closed, check = false) end ## action of permutations on vectors of positive integers function gset_by_type(G::PermGroup, Omega, ::Type{T}; closed::Bool = false) where T<:Vector{T2} where T2<:IntegerUnion - return GSetByElements(G, on_tuples, Omega; closed = closed) + return GSetByElements(G, on_tuples, Omega; closed = closed, check = false) end ## action of permutations on tuples of positive integers function gset_by_type(G::PermGroup, Omega, ::Type{T}; closed::Bool = false) where T<:Tuple{T2,Vararg{T2}} where T2<:IntegerUnion - return GSetByElements(G, on_tuples, Omega; closed = closed) + return GSetByElements(G, on_tuples, Omega; closed = closed, check = false) end ## action of matrices on vectors via right multiplication function gset_by_type(G::MatrixGroup{E, M}, Omega, ::Type{AbstractAlgebra.Generic.FreeModuleElem{E}}; closed::Bool = false) where E where M - return GSetByElements(G, *, Omega; closed = closed) + return GSetByElements(G, *, Omega; closed = closed, check = false) end ## action of matrices on sets of vectors via right multiplication function gset_by_type(G::MatrixGroup{E, M}, Omega, ::Type{T}; closed::Bool = false) where T <: Set{AbstractAlgebra.Generic.FreeModuleElem{E}} where E where M - return GSetByElements(G, on_sets, Omega; closed = closed) + return GSetByElements(G, on_sets, Omega; closed = closed, check = false) end ## action of matrices on vectors of vectors via right multiplication function gset_by_type(G::MatrixGroup{E, M}, Omega, ::Type{T}; closed::Bool = false) where T <: Vector{AbstractAlgebra.Generic.FreeModuleElem{E}} where E where M - return GSetByElements(G, on_tuples, Omega; closed = closed) + return GSetByElements(G, on_tuples, Omega; closed = closed, check = false) end ## action of matrices on subspaces via right multiplication function gset_by_type(G::MatrixGroup{E, M}, Omega, ::Type{T}; closed::Bool = false) where T <: AbstractAlgebra.Generic.Submodule{E} where E where M - return GSetByElements(G, ^, Omega; closed = closed) + return GSetByElements(G, ^, Omega; closed = closed, check = false) end ## action of matrices on polynomials via `on_indeterminates` function gset_by_type(G::MatrixGroup{E, M}, Omega, ::Type{T}; closed::Bool = false) where T <: MPolyRingElem{E} where E where M - return GSetByElements(G, on_indeterminates, Omega; closed = closed) + return GSetByElements(G, on_indeterminates, Omega; closed = closed, check = false) end ## (add more such actions: on sets of sets, on sets of tuples, ...) @@ -738,8 +742,8 @@ function action_homomorphism(G::PermGroup, Omega) return action_homomorphism(gset_by_type(G, Omega, eltype(Omega); closed = true)) end -function action_homomorphism(G::PermGroup, fun::Function, Omega) - return action_homomorphism(GSetByElements(G, fun, Omega, closed = true)) +function action_homomorphism(G::PermGroup, fun::Function, Omega; check = true) + return action_homomorphism(GSetByElements(G, fun, Omega, closed = true, check = check)) end diff --git a/test/Groups/gsets.jl b/test/Groups/gsets.jl index c69eabcb8e45..f8fd31d602d1 100644 --- a/test/Groups/gsets.jl +++ b/test/Groups/gsets.jl @@ -50,6 +50,7 @@ @test ! is_transitive(Omega) @test ! is_regular(Omega) @test ! is_semiregular(Omega) + @test_throws ArgumentError gset(G, permuted, omega) R, x = polynomial_ring(QQ, [:x1, :x2, :x3]); f = x[1]*x[2] + x[2]*x[3] From 24a31de2e535274fe49bfd042c563671d51904ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 1 Nov 2024 14:03:00 +0100 Subject: [PATCH 27/84] Add `isomorphism(::Type{PermGroup}, ::WeylGroup)` for some cases (#4264) Co-authored-by: Antony Della Vecchia --- experimental/LieAlgebras/src/LieAlgebras.jl | 1 + experimental/LieAlgebras/src/WeylGroup.jl | 53 +++++++++++++++++++ .../LieAlgebras/test/WeylGroup-test.jl | 47 ++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index d6cd094e36c6..e866dfbd8e64 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -74,6 +74,7 @@ import ..Oscar: ngens, order, parent_type, + permutation_group, rank, root, roots, diff --git a/experimental/LieAlgebras/src/WeylGroup.jl b/experimental/LieAlgebras/src/WeylGroup.jl index 821fe43e161e..e4d0fd98e021 100644 --- a/experimental/LieAlgebras/src/WeylGroup.jl +++ b/experimental/LieAlgebras/src/WeylGroup.jl @@ -474,6 +474,59 @@ function isomorphism(::Type{FPGroup}, W::WeylGroup; set_properties::Bool=true) return MapFromFunc(W, G, iso, isoinv) end +function permutation_group(W::WeylGroup; set_properties::Bool=true) + return codomain(isomorphism(PermGroup, W; set_properties)) +end + +function isomorphism(::Type{PermGroup}, W::WeylGroup; set_properties::Bool=true) + @req is_finite(W) "Weyl group is not finite" + R = root_system(W) + type, ordering = root_system_type_with_ordering(R) + + if length(type) != 1 + error("Not implemented (yet)") + end + if !issorted(ordering) + error("Not implemented (yet)") + end + coxeter_type, n = only(type) + if coxeter_type == :A + G = symmetric_group(n + 1) + + iso = function (w::WeylGroupElem) + reduce(*, [cperm(G, [i, i + 1]) for i in word(w)]; init=cperm(G)) + end + + isoinv = function (p::PermGroupElem) + word = UInt8[] + for cycle in cycles(p) + transpositions = [ + sort([c, cycle[i + 1]]) for (i, c) in enumerate(cycle) if i < length(cycle) + ] + for t in transpositions + word = reduce( + vcat, + [ + [i for i in t[1]:(t[2] - 1)], + [i for i in reverse(t[1]:(t[2] - 2))], + word, + ], + ) + end + end + return W(word) + end + else + error("Not implemented (yet)") + end + + if set_properties + set_order(G, order(W)) + end + + return MapFromFunc(W, G, iso, isoinv) +end + ############################################################################### # ReducedExpressionIterator diff --git a/experimental/LieAlgebras/test/WeylGroup-test.jl b/experimental/LieAlgebras/test/WeylGroup-test.jl index 76178206bce4..e78ad3d73ad5 100644 --- a/experimental/LieAlgebras/test/WeylGroup-test.jl +++ b/experimental/LieAlgebras/test/WeylGroup-test.jl @@ -52,6 +52,7 @@ include( @testset "WeylGroup Group conformace test for $(Wname)" for (Wname, W) in [ ("A1", weyl_group(:A, 1)), + ("A5", weyl_group(:A, 5)), ("B4", weyl_group(root_system(:B, 4))), ("D5", weyl_group(cartan_matrix(:D, 5))), ("F4+G2", weyl_group((:F, 4), (:G, 2))), @@ -111,9 +112,55 @@ include( if is_finite(W) # remove once rand(W) is implemented for infinite groups w = rand(W) @test w == inv(iso)(iso(w)) + v = rand(W) + @test iso(v * w) == iso(v) * iso(w) + @test v * w == inv(iso)(iso(v) * iso(w)) end g = rand_pseudo(G) @test g == iso(inv(iso)(g)) + h = rand_pseudo(G) + @test inv(iso)(h * g) == inv(iso)(h) * inv(iso)(g) + @test h * g == iso(inv(iso)(h) * inv(iso)(g)) + end + end + end + end + + if has_root_system_type(root_system(W)) + type, ordering = root_system_type_with_ordering(root_system(W)) + if length(type) == 1 && issorted(ordering) && only(type)[1] == :A # only implemented for A_n (yet) + @testset "isomorphism(PermGroup, ::WeylGroup; set_properties=$set_properties)" for set_properties in + [ + false, true + ] + G = permutation_group(W; set_properties) + if (is_finite(W) && ngens(W) < 6) || set_properties #= for sane runtime =# + @test is_finite(G) == is_finite(W) + is_finite(W) && @test order(G) == order(W) + end + + iso = isomorphism(PermGroup, W; set_properties) + @test W == domain(iso) + G = codomain(iso) + if (is_finite(W) && ngens(W) < 6) || set_properties #= for sane runtime =# + @test is_finite(G) == is_finite(W) + is_finite(W) && @test order(G) == order(W) + if ngens(W) < 10 #= for sane runtime =# + for _ in 1:5 + if is_finite(W) # remove once rand(W) is implemented for infinite groups + w = rand(W) + @test w == inv(iso)(iso(w)) + v = rand(W) + @test iso(v * w) == iso(v) * iso(w) + @test v * w == inv(iso)(iso(v) * iso(w)) + end + g = rand_pseudo(G) + @test g == iso(inv(iso)(g)) + h = rand_pseudo(G) + @test inv(iso)(h * g) == inv(iso)(h) * inv(iso)(g) + @test h * g == iso(inv(iso)(h) * inv(iso)(g)) + end + end end end end From eab758ae42d4d7d95203d00071070ca6e628f168 Mon Sep 17 00:00:00 2001 From: Stevell Muller <78619134+StevellM@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:05:03 +0100 Subject: [PATCH 28/84] `QuadFormAndIsom`: add a missing check and remove some unnecessary `@req` (#4262) * bug fix + remove annoying req's * fix obvious broken test --- experimental/QuadFormAndIsom/src/embeddings.jl | 14 ++++++++------ experimental/QuadFormAndIsom/src/enumeration.jl | 3 ++- experimental/QuadFormAndIsom/test/runtests.jl | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/experimental/QuadFormAndIsom/src/embeddings.jl b/experimental/QuadFormAndIsom/src/embeddings.jl index ee934c79666b..da388cd0424a 100644 --- a/experimental/QuadFormAndIsom/src/embeddings.jl +++ b/experimental/QuadFormAndIsom/src/embeddings.jl @@ -1286,25 +1286,27 @@ of $M$ into lattices in $G$. """ function primitive_embeddings(G::ZZGenus, M::ZZLat; classification::Symbol = :sub) @req is_integral(scale(G)) && is_integral(M) "Only available for integral lattices" - @req !iseven(G) || iseven(M) "Cannot embed an odd lattice into an even lattice" @req classification in Symbol[:none, :emb, :sub, :first] "Wrong symbol for classification" + results = Tuple{ZZLat, ZZLat, ZZLat}[] + + (iseven(G) && !iseven(M)) && return false, results + (rank(M) > rank(G)) && return false, results + even = is_even(G) posL, _, negL = signature_tuple(G) posM, _, negM = signature_tuple(M) posN = posL - posM negN = negL - negM - @req (posN >= 0 && negN >= 0) "Incompatible signatures for the embeddings" - results = Tuple{ZZLat, ZZLat, ZZLat}[] + (posN < 0 || negN < 0) && return false, results + if rank(M) == rank(G) genus(M) != G && return false, results push!(results, (M, M, orthogonal_submodule(M, M))) return true, results end - @req rank(M) < rank(G) "The rank of M must be smaller or equal than the one of the lattices in G" - if classification == :sub cs = :subsub elseif classification == :emb @@ -1666,7 +1668,7 @@ function admissible_equivariant_primitive_extensions(A::ZZLatWithIsom, p::IntegerUnion, q::IntegerUnion = p; check::Bool = true) # p and q can be equal, and they will be most of the time - @req is_prime(p) && is_prime(q) "p and q must be a prime number" + @req is_prime(p) && is_prime(q) "p and q must be prime numbers" # Requirements for [BH23] same_ambient = ambient_space(lattice(A)) === ambient_space(lattice(B)) === ambient_space(lattice(C)) diff --git a/experimental/QuadFormAndIsom/src/enumeration.jl b/experimental/QuadFormAndIsom/src/enumeration.jl index b1d85ed06f1c..219f02d15463 100644 --- a/experimental/QuadFormAndIsom/src/enumeration.jl +++ b/experimental/QuadFormAndIsom/src/enumeration.jl @@ -347,7 +347,7 @@ function admissible_triples(G::ZZGenus, p::IntegerUnion; pA::Int = -1, nA::Int = elseif nA >= 0 r1 >= nA || continue end - m = min(ep, r1) + m = Int(min(ep, r1)) D = _find_D(dG, m, p) while !is_empty(D) d1, dp = pop!(D) @@ -616,6 +616,7 @@ function representatives_of_hermitian_type(G::ZZGenus, chi::Union{ZZPolyRingElem append!(gene, hermitian_genera(E, rk, sign, dd; min_scale=inv(DE), max_scale=numerator(dd)*DE)) end unique!(gene) + isempty(gene) && return reps # In the cyclotomic case, the Galois group of the fixed field K acts on the # set of genera by "change of fixed primitive root of unity". diff --git a/experimental/QuadFormAndIsom/test/runtests.jl b/experimental/QuadFormAndIsom/test/runtests.jl index 766594875221..4ac217f81c89 100644 --- a/experimental/QuadFormAndIsom/test/runtests.jl +++ b/experimental/QuadFormAndIsom/test/runtests.jl @@ -301,7 +301,7 @@ end ok, sv = primitive_embeddings(rescale(E8, 2), rescale(k, QQ(1//2)); check=false) @test !ok @test is_empty(sv) - @test_throws ArgumentError primitive_embeddings(rescale(E8, -1), k; check=false) + @test isempty(primitive_embeddings(rescale(E8, -1), k; check=false)[2]) k = integer_lattice(; gram=matrix(QQ,1,1,[6])) E7 = root_lattice(:E, 7) From 03b927fdb1b6b235e95477bc57d53b01597893b9 Mon Sep 17 00:00:00 2001 From: Matthias Zach <85350711+HechtiDerLachs@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:24:50 +0100 Subject: [PATCH 29/84] Two neighbor step in char 0 (#4183) * Clean up reduction to positive characteristic. * Introduce a type of its own for sections and use it. * Use AbsWeilDivisor consistently. * enable computation of admissible transformations on the weierstrass chart * Speed up mappings from quotient rings. * Implement various reduction methods to pos. char.. * Add reduction method for automorphisms. * Disable caching for cheap_realizations for the moment. --------- Co-authored-by: Simon Brandhorst --- experimental/Schemes/src/BlowupMorphism.jl | 16 +- experimental/Schemes/src/elliptic_surface.jl | 452 ++++++++++++++---- .../MorphismFromRationalFunctions/Methods.jl | 13 +- .../Schemes/Divisors/WeilDivisor.jl | 18 +- .../Schemes/Sheaves/IdealSheaves.jl | 3 + src/Rings/MPolyMap/MPolyRing.jl | 6 + src/Rings/mpolyquo-localizations.jl | 1 + 7 files changed, 384 insertions(+), 125 deletions(-) diff --git a/experimental/Schemes/src/BlowupMorphism.jl b/experimental/Schemes/src/BlowupMorphism.jl index 9498e0586c56..9a0aa5a5dffd 100644 --- a/experimental/Schemes/src/BlowupMorphism.jl +++ b/experimental/Schemes/src/BlowupMorphism.jl @@ -644,18 +644,18 @@ function produce_object_on_affine_chart(I::StrictTransformIdealSheaf, U::AbsAffi f_loc = covering_morphism(f)[U] V = codomain(f_loc) IE_loc = IE(U) - @assert isone(ngens(IE_loc)) "ideal sheaf of exceptional locus is not principal" - tot = pullback(f_loc)(J(V)) - #return saturation_with_index(tot, IE_loc) # It is usually better to pass to the simplified covering to do the computations simp_cov = simplified_covering(X) U_simp = first([V for V in patches(simp_cov) if original(V) === U]) a, b = identification_maps(U_simp) - # This used to be the following line. But we don't use the index, so we - # switch to the more performant version - # result, _ = saturation_with_index(pullback(a)(tot), pullback(a)(IE_loc)) - result = _iterative_saturation(pullback(a)(tot), elem_type(OO(U_simp))[pullback(a)(u) for (u, _) in factor(lifted_numerator(first(gens(IE_loc))))]) - return pullback(b)(result) + tot = pullback(f_loc)(J(V)) + if isone(ngens(IE_loc)) + result = _iterative_saturation(pullback(a)(tot), elem_type(OO(U_simp))[pullback(a)(u) for (u, _) in factor(lifted_numerator(first(gens(IE_loc))))]) + return pullback(b)(result) + else + result, _ = saturation_with_index(pullback(a)(tot), pullback(a)(IE_loc)) + return result + end end @attr Bool function is_prime(I::StrictTransformIdealSheaf) diff --git a/experimental/Schemes/src/elliptic_surface.jl b/experimental/Schemes/src/elliptic_surface.jl index bb99e06b8639..594a9212e40c 100644 --- a/experimental/Schemes/src/elliptic_surface.jl +++ b/experimental/Schemes/src/elliptic_surface.jl @@ -243,7 +243,7 @@ Return $\chi(\mathcal{O}_X)$. euler_characteristic(X::EllipticSurface) = X.euler_characteristic @doc raw""" - algebraic_lattice(X) -> Vector{WeilDivisor}, ZZLat + algebraic_lattice(X) -> Vector{AbsWeilDivisor}, ZZLat Return the sublattice `L` of ``Num(X)`` spanned by fiber components, torsion sections and the sections provided at the construction of ``X``. @@ -944,7 +944,7 @@ function _trivial_lattice(S::EllipticSurface; reducible_singular_fibers_in_PP1=_ end @doc raw""" - trivial_lattice(X::EllipticSurface) -> Vector{WeilDivisor}, ZZMatrix + trivial_lattice(X::EllipticSurface) -> Vector{AbsWeilDivisor}, ZZMatrix Return a basis for the trivial lattice as well as its gram matrix. @@ -977,7 +977,7 @@ end @doc raw""" - standardize_fiber(S::EllipticSurface, f::Vector{<:WeilDivisor}) + standardize_fiber(S::EllipticSurface, f::Vector{<:AbsWeilDivisor}) Internal method. Used to prepare for [`reducible_fibers`](@ref). `f` must be the list of the components of the reducible fiber `F`. @@ -987,7 +987,7 @@ Output a list of tuples with each tuple as follows - the irreducible components `[F0,...Fn]` of `F` sorted such that the first entry `F0` is the one intersecting the zero section. The others are sorted in some standard way - gram matrix of the intersection of [F0,...,Fn], it is an extended ADE-lattice. """ -function standardize_fiber(S::EllipticSurface, f::Vector{<:WeilDivisor}) +function standardize_fiber(S::EllipticSurface, f::Vector{<:AbsWeilDivisor}) @hassert :EllipticSurface 2 all(is_prime(i) for i in f) f = copy(f) O = components(zero_section(S))[1] @@ -1087,7 +1087,7 @@ function fiber_cartier(S::EllipticSurface, P::Vector = ZZ.([0,1])) end @doc raw""" - fiber_components(S::EllipticSurface, P) -> Vector{<:WeilDivisor} + fiber_components(S::EllipticSurface, P) -> Vector{<:AbsWeilDivisor} Return the fiber components of the fiber over the point $P \in C$. """ @@ -1201,15 +1201,45 @@ end section(X::EllipticSurface, P::EllipticCurvePoint) Given a rational point $P\in E(C)$ of the generic fiber $E/C$ of $\pi\colon X \to C$, -return its closure in $X$ as a `WeilDivisor`. +return its closure in $X$ as a `AbsWeilDivisor`. """ function section(X::EllipticSurface, P::EllipticCurvePoint) if iszero(P[1])&&iszero(P[3]) return zero_section(X) end - return _section(X, P) + return EllipticSurfaceSection(X, P) end +@attributes mutable struct EllipticSurfaceSection{ + CoveredSchemeType<:AbsCoveredScheme, + CoefficientRingType<:AbstractAlgebra.Ring, + CoefficientRingElemType<:AbstractAlgebra.RingElem + } <: AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} + D::WeilDivisor{CoveredSchemeType, CoefficientRingType, CoefficientRingElemType} + P::EllipticCurvePoint + + function EllipticSurfaceSection(X::EllipticSurface, P::EllipticCurvePoint; coefficient_ring::Ring=ZZ) + @vprint :EllipticSurface 3 "Computing a section from a point on the generic fiber\n" + weierstrass_contraction(X) # trigger required computations + PX = _section_on_weierstrass_ambient_space(X, P) + for f in X.ambient_blowups + PX = strict_transform(f , PX) + end + PY = pullback(X.inc_Y, PX) + set_attribute!(PY, :name, string("section: (",P[1]," : ",P[2]," : ",P[3],")")) + set_attribute!(PY, :_self_intersection, -euler_characteristic(X)) + W = WeilDivisor(PY, check=false) + set_attribute!(W, :is_prime=>true) + I = first(components(W)) + set_attribute!(I, :is_prime=>true) + return new{typeof(X), typeof(coefficient_ring), elem_type(coefficient_ring)}(W, P) + end +end + +underlying_divisor(D::EllipticSurfaceSection) = D.D +rational_point(D::EllipticSurfaceSection) = D.P + + function _section_on_weierstrass_ambient_space(X::EllipticSurface, P::EllipticCurvePoint) S0,incS0 = weierstrass_model(X) X0 = codomain(incS0) @@ -1225,31 +1255,14 @@ function _section_on_weierstrass_ambient_space(X::EllipticSurface, P::EllipticCu return ideal_sheaf(X0,U,[OO(U)(i) for i in [x*denominator(b[1])(t)-numerator(b[1])(t),y*denominator(b[2])(t)-numerator(b[2])(t)]]; check=false) end -function _section(X::EllipticSurface, P::EllipticCurvePoint) - @vprint :EllipticSurface 3 "Computing a section from a point on the generic fiber\n" - weierstrass_contraction(X) # trigger required computations - PX = _section_on_weierstrass_ambient_space(X, P) - for f in X.ambient_blowups - PX = strict_transform(f , PX) - end - PY = pullback(X.inc_Y, PX) - set_attribute!(PY, :name, string("section: (",P[1]," : ",P[2]," : ",P[3],")")) - set_attribute!(PY, :_self_intersection, -euler_characteristic(X)) - W = WeilDivisor(PY, check=false) - set_attribute!(W, :is_prime=>true) - I = first(components(W)) - set_attribute!(I, :is_prime=>true) - set_attribute!(W, :point=>P) - return W -end @doc raw""" - zero_section(S::EllipticSurface) -> WeilDivisor + zero_section(S::EllipticSurface) -> AbsWeilDivisor Return the zero section of the relatively minimal elliptic fibration \pi\colon X \to C$. """ -@attr Any zero_section(S::EllipticSurface) = _section(S, generic_fiber(S)([0,1,0])) +@attr Any zero_section(S::EllipticSurface) = EllipticSurfaceSection(S, generic_fiber(S)([0,1,0])) ################################################################################ # @@ -1324,7 +1337,11 @@ function _prop217(E::EllipticCurve, P::EllipticCurvePoint, k) # collect the equations as a matrix cc = [[coeff(j, abi) for abi in ab] for j in eqns] - M = matrix(B, length(eqns), length(ab), reduce(vcat,cc, init=elem_type(base)[])) + mat = reduce(vcat,cc, init=elem_type(base)[]) + @assert all(is_one(denominator(x)) for x in mat) + @assert all(is_constant(numerator(x)) for x in mat) + mat2 = [constant_coefficient(numerator(x)) for x in mat] + M = matrix(B, length(eqns), length(ab), mat2) # @assert M == matrix(base, cc) # does not work if length(eqns)==0 K = kernel(M; side = :right) kerdim = ncols(K) @@ -1378,7 +1395,7 @@ function linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int64) I = saturated_ideal(defining_ideal(U)) IP = ideal([x*xd(t)-xn(t),y*yd(t)-yn(t)]) - issubset(I, IP) || error("P does not define a point on the Weierstrasschart") + @hassert :EllipticSurface 2 issubset(I, IP) || error("P does not define a point on the Weierstrasschart") @assert gcd(xn, xd)==1 @assert gcd(yn, yd)==1 @@ -1424,7 +1441,7 @@ function two_neighbor_step(X::EllipticSurface, F::Vector{QQFieldElem}) @assert scheme(parent(u)) === X pr = weierstrass_contraction(X) WX, _ = weierstrass_model(X) - # The following is a cheating version of the command u = pushforward(pr)(u) + # The following is a cheating version of the command u = pushforward(pr)(u) (the latter has now been deprecated!) u = function_field(WX)(u[weierstrass_chart_on_minimal_model(X)]) @assert scheme(parent(u)) === weierstrass_model(X)[1] @@ -1476,7 +1493,7 @@ function two_neighbor_step(X::EllipticSurface, F::Vector{QQFieldElem}) end @doc raw""" - horizontal_decomposition(X::EllipticSurface, L::Vector{QQFieldElem}) -> WeilDivisor, EllipticCurvePoint + horizontal_decomposition(X::EllipticSurface, L::Vector{QQFieldElem}) -> AbsWeilDivisor, EllipticCurvePoint Given a divisor ``L`` as a vector in the `algebraic_lattice(X)` find a linearly equivalent divisor ``(n-1) O + P + V = D ~ L`` where @@ -1553,8 +1570,7 @@ function horizontal_decomposition(X::EllipticSurface, F::Vector{QQFieldElem}) @assert all(F4[i]>=0 for i in 1:length(basisNS)) D = D + sum(ZZ(F4[i])*basisNS[i] for i in 1:length(basisNS)) @assert D<=D1 - l = Int(l) - return D1, D, P, l, c + return D1, D, P, Int(l), c end @doc raw""" @@ -1572,14 +1588,14 @@ function elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) end @doc raw""" - _elliptic_parameter(X::EllipticSurface, D::WeilDivisor, l, c) + _elliptic_parameter(X::EllipticSurface, D::AbsWeilDivisor, l, c) Compute the linear system of ``D = (n-1) O + P + V``. where V is vertical and `l` is the coefficient of the fiber class. Assumes `D` nef and `D^2=0`. Typically ``D`` is the output of `horizontal_decomposition`. """ -function _elliptic_parameter(X::EllipticSurface, D1::WeilDivisor, D::WeilDivisor, P::EllipticCurvePoint, l::Int, c) +function _elliptic_parameter(X::EllipticSurface, D1::AbsWeilDivisor, D::AbsWeilDivisor, P::EllipticCurvePoint, l::Int, c) S, piS = weierstrass_model(X); piX = weierstrass_contraction(X) c = function_field(X)(c) @@ -1634,39 +1650,166 @@ function extended_ade(ADE::Symbol, n::Int) return -G, kernel(G; side = :left) end -# This function allows to store a reduction map to positive characteristic, -# e.g. for computing intersection numbers. -function reduction_to_pos_char(X::EllipticSurface, red_map::Map) - return get_attribute!(X, :reduction_to_pos_char) do - kk0 = base_ring(X) - @assert domain(red_map) === kk0 - kkp = codomain(red_map) - @assert characteristic(kkp) > 0 - _, result = base_change(red_map, X) - return red_map, result - end::Tuple{<:Map, <:Map} +######################################################################## +# Reduction to positive characteristic +# +# We allow to store a reduction of an elliptic surface `X` to positive +# characteristic. The user needs to know what they're doing here! +# +# The functionality can be made available by specifying a reduction +# map for the `base_ring` (actually a field) of `X` to a field of +# positive characteristic. This can then be stored in `X` via +# `set_good_reduction_map!`. The latter unlocks certain features such +# as computation of intersection numbers in positive characteristic. +######################################################################## +function set_good_reduction_map!(X::EllipticSurface, red_map::Map) + has_attribute(X, :good_reduction_map) && error("reduction map has already been set") + kk0 = base_ring(X) + @assert domain(red_map) === kk0 + kkp = codomain(red_map) + @assert characteristic(kkp) > 0 + set_attribute!(X, :good_reduction_map=>red_map) +end + +function get_good_reduction_map(X::EllipticSurface) + is_zero(characteristic(base_ring(X))) || error("reduction to positive characteristic is only possible from characteristic zero") + has_attribute(X, :good_reduction_map) || error("no reduction map is available; please set it manually via `set_good_reduction_map!`") + return get_attribute(X, :good_reduction_map)::Map +end + +@attr Tuple{<:AbsCoveredScheme, <:AbsCoveredSchemeMorphism} function raw_good_reduction(X::EllipticSurface) + red_map = get_good_reduction_map(X) + X_red, bc_map = base_change(red_map, X) + set_attribute!(X_red, :is_irreducible=>true) + set_attribute!(X_red, :is_reduced=>true) + set_attribute!(X_red, :is_integral=>true) + set_attribute!(X_red, :is_equidimensional=>true) + return X_red, bc_map +end + +@attr Map function good_reduction_function_fields(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E = generic_fiber(X) + Ft = base_field(E) + Pt = base_ring(Ft) + kk = coefficient_ring(Pt) + kk_red = codomain(red_map) + Pt_red, _ = polynomial_ring(kk_red, first(symbols(Pt)); cached=false) + Ft_red = fraction_field(Pt_red) + Ft_to_Ft_red = map_from_func(fr->Ft_red(map_coefficients(red_map, numerator(fr); parent=Pt_red), map_coefficients(red_map, denominator(fr); parent=Pt_red)), Ft, Ft_red) + return Ft_to_Ft_red +end + +@attr EllipticCurve function good_reduction_generic_fiber(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E = generic_fiber(X) + Ft_to_Ft_red = good_reduction_function_fields(X) + E_red = base_change(Ft_to_Ft_red, E) + return E_red +end + +@attr Vector{<:EllipticCurvePoint} function good_reduction_rational_points(X::EllipticSurface) + red_map = good_reduction_function_fields(X) + result = Vector{EllipticCurvePoint}() + E_red = good_reduction_generic_fiber(X) + for P in X.MWL # TODO: Do we have a getter for this? + if is_infinite(P) + push!(result, infinity(E_red)) + continue + end + push!(result, E_red([red_map(P[1]), red_map(P[2])])) + end + return result +end + +@attr EllipticSurface function good_reduction(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E_red = good_reduction_generic_fiber(X) + mwl_red = good_reduction_rational_points(X) + X_red = EllipticSurface(E_red, 2, mwl_red; resolution_strategy=X.resolution_strategy) end +@attr Tuple{<:MorphismFromRationalFunctions, <:MorphismFromRationalFunctions} function identifications_with_raw_good_reduction(X::EllipticSurface) + X_red = good_reduction(X) + W_red = weierstrass_chart_on_minimal_model(X_red) + X_red_raw, red_raw = raw_good_reduction(X) + red_raw_cov = covering_morphism(red_raw) + W_red_raw = domain(first(maps_with_given_codomain(red_raw_cov, weierstrass_chart_on_minimal_model(X)))) + + R_red = ambient_coordinate_ring(W_red) + R_red_raw = ambient_coordinate_ring(W_red_raw) + raw_to_red = morphism_from_rational_functions(X_red_raw, X_red, W_red_raw, W_red, fraction_field(R_red_raw).(gens(R_red_raw)); check=false) + set_attribute!(raw_to_red, :is_isomorphism=>true) + red_to_raw = morphism_from_rational_functions(X_red, X_red_raw, W_red, W_red_raw, fraction_field(R_red).(gens(R_red)); check=false) + set_attribute!(red_to_raw, :is_isomorphism=>true) + return raw_to_red, red_to_raw +end + + +function raw_reduction_of_algebraic_lattice(X::EllipticSurface) + return get_attribute!(X, :raw_reduction_of_algebraic_lattice) do + X_red_raw, bc = raw_good_reduction(X) + basis_ambient, _, _= algebraic_lattice(X) + return red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) + end::IdDict +end + +@attr ZZMatrix function good_reduction_algebraic_lattice(X::EllipticSurface) + div_red = raw_reduction_of_algebraic_lattice(X) + from, to = identifications_with_raw_good_reduction(X) + div_red_pf = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>pushforward(from, E) for (D, E) in div_red) + basis, _, _= algebraic_lattice(X) + X_red = good_reduction(X) + red_basis, _, _= algebraic_lattice(X_red) + result = matrix(ZZ, [div_red_pf[D] == E ? one(ZZ) : zero(ZZ) for D in basis, E in red_basis]) + result[1, 1] = one(ZZ) # identify the generic fibers + return result +end + +@attr MorphismFromRationalFunctions function good_reduction( + f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface} + ) + X = domain(f) + @assert X === codomain(f) "reduction to positive characteristic is only implemented for automorphisms" + W = weierstrass_chart_on_minimal_model(X) + @assert W === domain_chart(f) === codomain_chart(f) "morphism must be defined on the Weierstrass charts" + img_gens = coordinate_images(f) + red_map = get_good_reduction_map(X) + + X_red = good_reduction(X) + W_red = weierstrass_chart_on_minimal_model(X_red) + R = ambient_coordinate_ring(W_red) + FR = fraction_field(R) + + psi = fr -> FR(map_coefficients(red_map, numerator(fr); parent=R), map_coefficients(red_map, denominator(fr); parent=R)) + + img_gens_red = psi.(img_gens) + result = morphism_from_rational_functions(X_red, X_red, W_red, W_red, img_gens_red; check=false) + has_attribute(f, :is_isomorphism) && get_attribute(f, :is_isomorphism)===true && set_attribute!(result, :is_isomorphism=>true) + return result +end + + @doc raw""" - basis_representation(X::EllipticSurface, D::WeilDivisor) + basis_representation(X::EllipticSurface, D::AbsWeilDivisor) Return the vector representing the numerical class of `D` with respect to the basis of the ambient space of `algebraic_lattice(X)`. """ -function basis_representation(X::EllipticSurface, D::WeilDivisor) +function basis_representation(X::EllipticSurface, D::AbsWeilDivisor) basis_ambient,_, NS = algebraic_lattice(X) G = gram_matrix(ambient_space(NS)) n = length(basis_ambient) v = zeros(ZZRingElem, n) @vprint :EllipticSurface 3 "computing basis representation of $D\n" kk = base_ring(X) - if iszero(characteristic(kk)) && has_attribute(X, :reduction_to_pos_char) - red_map, bc = get_attribute(X, :reduction_to_pos_char) - for i in 1:n - @vprintln :EllipticSurface 4 "intersecting with $(i): $(basis_ambient[i])" - - v[i] = intersect(base_change(red_map, basis_ambient[i]; scheme_base_change=bc), - base_change(red_map, D; scheme_base_change=bc)) + if iszero(characteristic(kk)) && has_attribute(X, :good_reduction_map) + X_red_raw, bc = raw_good_reduction(X) + red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) + D_red = _reduce_as_prime_divisor(bc, D) + for (i, E) in enumerate(basis_ambient) + @vprintln :EllipticSurface 4 "intersecting in positive characteristic with $(i): $(basis_ambient[i])" + v[i] = intersect(red_dict[E], D_red) end else for i in 1:n @@ -1679,11 +1822,56 @@ function basis_representation(X::EllipticSurface, D::WeilDivisor) return v*inv(G) end -################################################################################ -# -# patches for Oscar -# -################################################################################ +### Some functions to do custom pullback of divisors along reduction maps. +# We assume that primeness is preserved along the reduction. In particular, the +# user is responsible for this to hold for all cases used! +# They specify the "good reduction" in the end. +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::AbsWeilDivisor) + return WeilDivisor(domain(bc), coefficient_ring(D), + IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}( + _reduce_as_prime_divisor(bc, I) => c for (I, c) in coefficient_dict(D) + ) + ) +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::EllipticSurfaceSection) + P = rational_point(D) + is_infinite(P) && return _reduce_as_prime_divisor(bc, underlying_divisor(D)) + X = codomain(bc) + @assert parent(P) === generic_fiber(X) + W = weierstrass_chart_on_minimal_model(X) + R = ambient_coordinate_ring(W) + (x, y, t) = gens(R) + I = ideal(R, R.([evaluate(denominator(P[1]), t)*x-evaluate(numerator(P[1]), t), + evaluate(denominator(P[2]), t)*y-evaluate(numerator(P[2]), t)]) + ) + bc_loc = first(maps_with_given_codomain(covering_morphism(bc), W)) + bc_I = pullback(bc_loc)(I) + @assert is_one(dim(bc_I)) + set_attribute!(bc_I, :is_prime=>true) + J = PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), bc_I) + return WeilDivisor(domain(bc), coefficient_ring(D), + IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}(J => one(coefficient_ring(D))) + ) +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::AbsIdealSheaf) + result = pullback(bc, I) + has_attribute(I, :_self_intersection) && set_attribute!(result, :_self_intersection=> + (get_attribute(I, :_self_intersection)::Int)) + return result +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::PrimeIdealSheafFromChart) + U = original_chart(I) + bc_cov = covering_morphism(bc) + V = __find_chart(U, codomain(bc_cov)) + IV = I(V) + bc_loc = first(maps_with_given_codomain(bc_cov, V)) + J = pullback(bc_loc)(IV) + set_attribute!(J, :is_prime=>true) + return PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), J) +end ######################################################################## @@ -2242,6 +2430,7 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti end if dim(Q) == 0 + @vprint :EllipticSurface 3 "image will be a fiber component\n" # find the fiber if is_one(Q(UBY)) # fiber over infinity # collect all components @@ -2251,24 +2440,31 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti append!(comps, E[2:end]) end end + @vprint :EllipticSurface 3 "found total of $(length(comps)) possible components\n" # collect all charts codomain_charts = AbsAffineScheme[] if is_empty(comps) # The fiber over infinity + @vprint :EllipticSurface 3 "the image must be the fiber over infinity" codomain_charts = affine_charts(Y) # TODO: How can we restrict the charts then? else codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(D->!isone(first(components(D))(V)), comps)] end + @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible" if i > n - mwr # If D is a section + @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" pt = X.MWL[i-(n-mwr)] res = _pushforward_section(step, pt; divisor=D, codomain_charts) result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) else + @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; domain_chart = dom_chart, codomain_charts) loc_map === nothing && error("pushforward preparation did not succeed") + @assert !is_one(I(domain(loc_map))) K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) JJ = ideal(OO(cod_chart), gens(K)) res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) @@ -2277,6 +2473,7 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti end continue end + @vprint :EllipticSurface 3 "image will not be in the fiber over infinity\n" # fiber over some point ≂̸ ∞. t = first(gens(OO(UBY))) @@ -2293,16 +2490,21 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(I->!isone(I(V)), components(F))] break end + @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible\n" if i > n - mwr # If D is a section + @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" pt = X.MWL[i-(n-mwr)] res = _pushforward_section(step, pt; divisor=D, codomain_charts) result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) else + @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; codomain_charts) loc_map === nothing && error("preparation for pushforward did not succeed") + @assert !is_one(I(domain(loc_map))) K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) JJ = ideal(OO(cod_chart), gens(K)) res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) @@ -2329,7 +2531,9 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti continue end + @assert !is_one(I(domain(loc_map))) K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) JJ = ideal(OO(cod_chart), gens(K)) res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) @@ -2338,7 +2542,7 @@ function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFuncti end end - res = WeilDivisor[result[D] for D in lat_X] + res = AbsWeilDivisor[result[D] for D in lat_X] for a in res set_attribute!(first(components(a)), :_self_intersection, -2) end @@ -2354,13 +2558,13 @@ end =# function morphism_from_section( X::EllipticSurface, P::EllipticCurvePoint; - divisor::AbsWeilDivisor=_section(X, P) + divisor::AbsWeilDivisor=EllipticSurfaceSection(X, P) ) U = weierstrass_chart_on_minimal_model(X) II = first(components(divisor)) # For the zero section we can not use the Weierstrass chart - if P.is_infinite + if is_infinite(P) return identity_map(X) end @assert !is_one(II(U)) @@ -2389,7 +2593,7 @@ end ######################################################################## function translation_morphism(X::EllipticSurface, P::EllipticCurvePoint; - divisor::AbsWeilDivisor=_section(X, P) + divisor::AbsWeilDivisor=EllipticSurfaceSection(X, P) ) E = generic_fiber(X) @assert parent(P) === E "point does not lay on the underlying elliptic curve" @@ -2423,7 +2627,7 @@ end function _pushforward_section( phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}, P::EllipticCurvePoint; - divisor::AbsWeilDivisor=_section(domain(phi), P), + divisor::AbsWeilDivisor=EllipticSurfaceSection(domain(phi), P), codomain_charts::Vector{<:AbsAffineScheme} = affine_charts(codomain(phi)) ) X = domain(phi)::EllipticSurface @@ -2544,6 +2748,28 @@ function admissible_moebius_transformations( X::EllipticSurface, Y::EllipticSurface ) + result = MorphismFromRationalFunctions[] + for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=false) + push!(result, _moebius_to_morphism_from_rational_functions(X, Y, img_gens)) + end + return result +end + +function admissible_moebius_transformations_on_weierstrass_chart( + X::EllipticSurface, + Y::EllipticSurface + ) + result = MapFromFunc[] + for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=true) + push!(result, _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens)) + end + return result +end + +function _admissible_moebius_transformations( + X::EllipticSurface, + Y::EllipticSurface; on_weierstrass_model=true + ) EX = generic_fiber(X) EY = generic_fiber(Y) @@ -2561,21 +2787,22 @@ function admissible_moebius_transformations( vY = roots(dY) @assert all(is_one(degree(a)) for (a, k) in factor(dY)) "not all critical values are rational over the given ground field" - for (c, _) in reducible_fibers(X) - @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" - end - for (c, _) in reducible_fibers(Y) - @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" - end +# for (c, _) in reducible_fibers(X) +# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" +# end +# for (c, _) in reducible_fibers(Y) +# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" +# end # Use the first three elements of vX and map them to three elements of vY. # Then check whether the resulting transformation preserves everything. - candidates = Function[] + candidates = [] @assert length(vX) >= 3 "at least three reducible fibers are needed" length(vX) == length(vY) || return candidates # No moebius transformation is possible in this case - + kkt = base_field(EX) + t = gen(kkt) p1 = vX[1:3] for i in vY for j in vY @@ -2586,47 +2813,67 @@ function admissible_moebius_transformations( mt = find_moebius_transformation(p1, p2) any(is_zero(mt(x)[2]) for x in vX) && continue # reducible fibers over ∞ are not implemented at the moment. any(!(mt(x)[1]//mt(x)[2] in vY) for x in vX) && continue # the transformation does not preserve all admissible fibers in this case - push!(candidates, mt) + p, q = mt(t) + img_t = (p//q)::typeof(t) + EYbc = base_change(f->evaluate(f, img_t), EY) + is_isomorphic(EYbc, EX) || continue + iso_ell = isomorphism(EX, EYbc) + push!(candidates, _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model)) end end end - - result = MorphismFromRationalFunctions[] - + return candidates +end + +function _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model) + EX = generic_fiber(X) + EY = generic_fiber(Y) # Set up some variables kkt = base_field(EX) t = gen(kkt) - WX = weierstrass_chart_on_minimal_model(X) + if on_weierstrass_model + WX = weierstrass_chart(X) + WY = weierstrass_chart(Y) + else + WX = weierstrass_chart_on_minimal_model(X) + WY = weierstrass_chart_on_minimal_model(Y) + end RX = ambient_coordinate_ring(WX) FRX = fraction_field(RX) - WY = weierstrass_chart_on_minimal_model(Y) RY = ambient_coordinate_ring(WY) FRY = fraction_field(RY) - # Go through the candidates again and for those which do indeed lead to isomorphic - # surfaces, construct the isomorphism. - for mt in candidates - p, q = mt(t) - img_t = (p//q)::typeof(t) - EYbc = base_change(f->evaluate(f, img_t), EY) - is_isomorphic(EYbc, EX) || continue - # Construct the isomorphism of elliptic surfaces explicitly - iso_ell = isomorphism(EX, EYbc) - - a, b, _ = rational_maps(iso_ell) - kkTxy = parent(a) - to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) - A = to_FRX(a) - B = to_FRX(b) - P, Q = mt(FRX(RX[3])) - img_T = (P//Q)::elem_type(FRX) - img_gens = [A, B, img_T] - loc_res = morphism_from_rational_functions(X, Y, WX, WY, img_gens; check=true) - set_attribute!(loc_res, :is_isomorphism=>true) - push!(result, loc_res) - end + # Construct the isomorphism of elliptic surfaces explicitly - return result + a, b, _ = rational_maps(iso_ell) + kkTxy = parent(a) + to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) + A = to_FRX(a) + B = to_FRX(b) + P, Q = mt(FRX(RX[3])) + img_T = (P//Q)::elem_type(FRX) + img_gens = [A, B, img_T] + return img_gens +end + +function _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens) + WY = weierstrass_chart(Y) + WX = weierstrass_chart(X) + RX = ambient_coordinate_ring(WX) + FRX = fraction_field(RX) + RY = ambient_coordinate_ring(WY) + FRY = fraction_field(RY) + + return extend_domain_to_fraction_field(hom(RY, FRX, img_gens)) +end + +function _moebius_to_morphism_from_rational_functions(X, Y, img_gens) + WY = weierstrass_chart_on_minimal_model(Y) + WX = weierstrass_chart_on_minimal_model(X) + + loc_res = morphism_from_rational_functions(X, Y, WX, WY, img_gens; check=true) + set_attribute!(loc_res, :is_isomorphism=>true) + return loc_res end # An internal helper routine to verify that a given isomorphism of elliptic surfaces @@ -2951,7 +3198,7 @@ function _vertical_part(X::EllipticSurface, v::QQMatrix) phi = hom(ag, mwlAb, mwlAb.(mwl_tors_gens)) a = preimage(phi, mwlAb(t)) for i in 1:ngens(ag) - P += a[i]*get_attribute(tors[i][1],:point) + P += a[i]*rational_point(tors[i][1]) end p = candidates[i] @@ -2982,3 +3229,4 @@ function _mordell_weil_group(X) Triv = lattice(V, identity_matrix(QQ,dim(V))[1:t,:]) return torsion_quadratic_module(N, Triv;modulus=1, modulus_qf=1, check=false) end + diff --git a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/MorphismFromRationalFunctions/Methods.jl b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/MorphismFromRationalFunctions/Methods.jl index b4b10284f6f5..6567b6ec396e 100644 --- a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/MorphismFromRationalFunctions/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Morphisms/MorphismFromRationalFunctions/Methods.jl @@ -308,9 +308,9 @@ This method is cheap in the sense that it simply inverts all representatives of the denominators occurring in the `realization_preview(Phi, U, V)`. """ function cheap_realization(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - if haskey(cheap_realizations(Phi), (U, V)) - return cheap_realizations(Phi)[(U, V)] - end + #if haskey(cheap_realizations(Phi), (U, V)) + # return cheap_realizations(Phi)[(U, V)] + #end img_gens_frac = realization_preview(Phi, U, V) # Try to cancel the fractions heuristically; turns out it was too expensive in some applications due to slow divide # for (k, f) in enumerate(img_gens_frac) @@ -574,7 +574,7 @@ function pushforward(Phi::MorphismFromRationalFunctions, D::AbsAlgebraicCycle) error("not implemented") end -function pushforward(Phi::MorphismFromRationalFunctions, D::WeilDivisor) +function pushforward(Phi::MorphismFromRationalFunctions, D::AbsWeilDivisor) is_isomorphism(Phi) || error("method not implemented unless for the case of an isomorphism") #is_proper(Phi) || error("morphism must be proper") all(is_prime, components(D)) || error("divisor must be given in terms of irreducible components") @@ -839,7 +839,7 @@ function _pullback(phi::MorphismFromRationalFunctions, I::AbsIdealSheaf) error("ideal sheaf could not be pulled back") end -function pullback(phi::MorphismFromRationalFunctions, D::WeilDivisor) +function pullback(phi::MorphismFromRationalFunctions, D::AbsWeilDivisor) return WeilDivisor(pullback(phi)(underlying_cycle(D)), check=false) end @@ -872,7 +872,7 @@ end function _find_good_representative_chart(I::AbsIdealSheaf; covering::Covering=default_covering(scheme(I))) # We assume that I is prime # TODO: Make this an hassert? - @assert is_prime(I) + @hassert :IdealSheaves 2 is_prime(I) X = scheme(I) # Some heuristics to choose a reasonably "easy" chart @@ -905,6 +905,7 @@ function _prepare_pushforward_prime_divisor( domain_chart::AbsAffineScheme = _find_good_representative_chart(I), codomain_charts::Vector{<:AbsAffineScheme} = copy(patches(codomain_covering(phi))) ) + @assert !is_one(I(domain_chart)) U = domain_chart X = domain(phi) Y = codomain(phi) diff --git a/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl b/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl index 0e4af4ca0e0e..33a5ce98411e 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/WeilDivisor.jl @@ -5,12 +5,12 @@ underlying_cycle(D::AbsWeilDivisor) = underlying_cycle(underlying_divisor(D)) underlying_cycle(D::WeilDivisor) = D.C ### type getters -scheme_type(D::WeilDivisor{S, U, V}) where{S, U, V} = S -scheme_type(::Type{WeilDivisor{S, U, V}}) where{S, U, V} = S -coefficient_ring_type(D::WeilDivisor{S, U, V}) where{S, U, V} = U -coefficient_ring_type(::Type{WeilDivisor{S, U, V}}) where{S, U, V} = U -coefficient_type(D::WeilDivisor{S, U, V}) where{S, U, V} = V -coefficient_type(::Type{WeilDivisor{S, U, V}}) where{S, U, V} = V +scheme_type(D::AbsWeilDivisor{S, U}) where{S, U} = S +scheme_type(::Type{AbsWeilDivisor{S, U}}) where{S, U} = S +coefficient_ring_type(D::AbsWeilDivisor{S, U}) where{S, U} = U +coefficient_ring_type(::Type{AbsWeilDivisor{S, U}}) where{S, U} = U +coefficient_type(D::AbsWeilDivisor{S, U}) where{S, U} = elem_type(U) +coefficient_type(::Type{AbsWeilDivisor{S, U}}) where{S, U} = elem_type(U) @doc raw""" WeilDivisor(X::CoveredScheme, R::Ring) @@ -338,7 +338,7 @@ function intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; I = c1 + c2 if !has_dimension_leq_zero(I) # potentially faster for localized ideals if c1 == c2 - result = result + a1*a2*_self_intersection(c1) + result = result + a1*a2* (has_attribute(c1, :_self_intersection) ? _self_intersection(c1) : _self_intersection(c2)) else error("self intersection unknown") end @@ -352,7 +352,7 @@ function intersect(D::AbsWeilDivisor, E::AbsWeilDivisor; end -function pushforward(inc::CoveredClosedEmbedding, W::WeilDivisor) +function pushforward(inc::CoveredClosedEmbedding, W::AbsWeilDivisor) X = domain(inc) Y = codomain(inc) X === ambient_scheme(W) || error("divisor not defined on the domain") @@ -420,7 +420,7 @@ end @doc raw""" - is_in_linear_system(f::VarietyFunctionFieldElem, D::WeilDivisor; regular_on_complement::Bool=true, check::Bool=true) -> Bool + is_in_linear_system(f::VarietyFunctionFieldElem, D::AbsWeilDivisor; regular_on_complement::Bool=true, check::Bool=true) -> Bool Return whether the rational function `f` is in the linear system ``|D|``, i.e. if $(f) + D \geq 0$. diff --git a/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl b/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl index 7628da425b3b..bba517f1af49 100644 --- a/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl +++ b/src/AlgebraicGeometry/Schemes/Sheaves/IdealSheaves.jl @@ -345,6 +345,7 @@ end end @attr Bool function has_dimension_leq_zero(I::Ideal) + is_one(I) && return true return dim(I) <= 0 end @@ -353,6 +354,7 @@ end P = base_ring(R)::MPolyRing J = ideal(P, numerator.(gens(I))) has_dimension_leq_zero(J) && return true + is_one(I) && return true return dim(I) <= 0 end @@ -1691,6 +1693,7 @@ function produce_object( algorithm::Symbol=:pullback # Either :pushforward or :pullback # This determines how to extend through the gluings. ) + U2 === original_chart(F) && return F.P # Initialize some local variables X = scheme(F) OOX = OO(X) diff --git a/src/Rings/MPolyMap/MPolyRing.jl b/src/Rings/MPolyMap/MPolyRing.jl index 0b3b9b6044ea..e8cd05b41b89 100644 --- a/src/Rings/MPolyMap/MPolyRing.jl +++ b/src/Rings/MPolyMap/MPolyRing.jl @@ -152,6 +152,11 @@ function _allunique(lst::Vector{T}) where {T<:RingElem} return all(!(x in lst[i+1:end]) for (i, x) in enumerate(lst)) end +function _allunique(lst::Vector{T}) where {T<:MPolyQuoRingElem} + rep_list = lift.(lst) + return _allunique(rep_list) +end + function _build_poly(u::MPolyRingElem, indices::Vector{Int}, S::MPolyRing) kk = coefficient_ring(S) r = ngens(S) @@ -263,6 +268,7 @@ function _evaluate_help(F::MPolyAnyMap{<: MPolyRing}, g) end function (F::MPolyAnyMap{<: MPolyRing})(g) + parent(g) === domain(F) || return F(domain(F)(g)) if g isa elem_type(domain(F)) if coefficient_map(F) === nothing return _evaluate_plain(F, g) diff --git a/src/Rings/mpolyquo-localizations.jl b/src/Rings/mpolyquo-localizations.jl index e0e3ecf557d3..eba0faa6c8b2 100644 --- a/src/Rings/mpolyquo-localizations.jl +++ b/src/Rings/mpolyquo-localizations.jl @@ -2807,4 +2807,5 @@ _exponents(x::MPolyQuoLocRingElem) = AbstractAlgebra.exponent_vectors(lifted_num # overwriting the comparison method to avoid computing saturations and groebner bases. _cmp_reps(a::MPolyLocRingElem) = y->(fraction(y) == fraction(a)) _cmp_reps(a::MPolyQuoLocRingElem) = y->(fraction(y) == fraction(a)) +_cmp_reps(a::MPolyQuoRingElem) = y->(y.f == a.f) From 1de754da934a8aaa193d0ee1e7dd335a6ba79f7a Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Wed, 30 Oct 2024 10:46:51 +0100 Subject: [PATCH 30/84] [ToricVarieties] Support computation of basis of H^4(X, Q) --- .../ToricVarieties/CohomologyClasses.md | 1 + .../CohomologyClasses/special_attributes.jl | 52 +++++++++++++++++++ src/exports.jl | 1 + 3 files changed, 54 insertions(+) diff --git a/docs/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses.md b/docs/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses.md index ba5ea52a853d..6538f5e2a2ba 100644 --- a/docs/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses.md +++ b/docs/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses.md @@ -66,4 +66,5 @@ volume_form(v::NormalToricVariety) intersection_form(v::NormalToricVariety) chern_class(v::NormalToricVariety, k::Int; check::Bool = true) chern_classes(v::NormalToricVariety; check::Bool = true) +basis_of_h4(v::NormalToricVariety; check::Bool = true) ``` diff --git a/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/special_attributes.jl b/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/special_attributes.jl index 605ecfecc967..293c7d49cfd6 100644 --- a/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/special_attributes.jl +++ b/src/AlgebraicGeometry/ToricVarieties/CohomologyClasses/special_attributes.jl @@ -214,3 +214,55 @@ function chern_classes(v::NormalToricVariety; check::Bool = true) end return [chern_class(v, k; check = check) for k in 0:dim(v)] end + + +@doc raw""" + basis_of_h4(v::NormalToricVariety; check::Bool = true) + +This method computes a monomial basis of the cohomology class $H^4(X, \mathbb{Q})$ +for a toric variety $X$. The algorithm employs Theorem 12.4.1 in [CLS11](@cite), +i.e. truncates the cohomology ring to degree $2$. By virtue of this theorem, +this approach is supported only for toric varieties that are both complete and +simplicial. Since it can be computationally very demanding to verify completeness, +the optional argument `check` can be set to `false` to skip the 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> h4_basis = basis_of_h4(Y) +6-element Vector{CohomologyClass}: + Cohomology class on a normal toric variety given by yx2^2 + Cohomology class on a normal toric variety given by xx2*yx2 + Cohomology class on a normal toric variety given by xx2*yt2 + Cohomology class on a normal toric variety given by xx2^2 + Cohomology class on a normal toric variety given by xt2*yx2 + Cohomology class on a normal toric variety given by xt2*yt2 + +julia> betti_number(Y, 4) == length(h4_basis) +true +``` +""" +function basis_of_h4(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} + if check + @req is_complete(v) "Computation of basis of H4(X, Q) is currently only supported for complete toric varieties" + @req is_simplicial(v) "Computation of basis of H4(X, Q) is currently only supported for simplicial toric varieties" + end + if dim(v) < 4 + set_attribute!(v, :basis_of_h4, Vector{CohomologyClass}()) + end + if has_attribute(v, :basis_of_h4) + return get_attribute(v, :basis_of_h4) + end + R = cohomology_ring(v, check = check) + basis_of_h4 = [cohomology_class(v, R(g)) for g in monomial_basis(R, [2])] + set_attribute!(v, :basis_of_h4, basis_of_h4) + return basis_of_h4 +end diff --git a/src/exports.jl b/src/exports.jl index cce9ee01c6e0..2eec9e786eb1 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -293,6 +293,7 @@ export bases export basis_of_global_sections export basis_of_global_sections_via_homogeneous_component export basis_of_global_sections_via_rational_functions +export basis_of_h4 export bell export betti export betti_number From 0b01449a42414419cfe68ca15fcac7c2fdb0ea7a Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Thu, 31 Oct 2024 19:55:23 +0100 Subject: [PATCH 31/84] [FTheoryTools] Sort exports alphabetically --- experimental/FTheoryTools/src/exports.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/FTheoryTools/src/exports.jl b/experimental/FTheoryTools/src/exports.jl index cdcb5d4fa449..0f2529e4221e 100644 --- a/experimental/FTheoryTools/src/exports.jl +++ b/experimental/FTheoryTools/src/exports.jl @@ -34,6 +34,7 @@ export arxiv_version export associated_literature_models export base_space export blow_up +export birational_literature_models export calabi_yau_hypersurface export chern_class export chern_classes @@ -142,7 +143,6 @@ export passes_tadpole_cancellation_check export passes_verticality_checks export polytope_index export put_over_concrete_base -export birational_literature_models export resolution_generating_sections export resolution_zero_sections export resolutions From 4f19d607016b9bf7497b8dc04942646902a49179 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Fri, 1 Nov 2024 01:19:40 +0100 Subject: [PATCH 32/84] [FTheoryTools] Support computation of H^(2,2)(X, Q) --- experimental/FTheoryTools/docs/src/g4.md | 7 +- experimental/FTheoryTools/src/FTheoryTools.jl | 1 + .../src/G4Fluxes/special_attributes.jl | 159 ++++++++++++++++++ experimental/FTheoryTools/src/exports.jl | 1 + 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl diff --git a/experimental/FTheoryTools/docs/src/g4.md b/experimental/FTheoryTools/docs/src/g4.md index a6c5c8095da5..a0359fd0677b 100644 --- a/experimental/FTheoryTools/docs/src/g4.md +++ b/experimental/FTheoryTools/docs/src/g4.md @@ -32,4 +32,9 @@ passes_elementary_quantization_checks(gf::G4Flux) ``` -## Methods +## Auxiliary + +The following methods are relevant to the discussion and study of $G_4$-fluxes: +```@docs +basis_of_h22(v::NormalToricVariety; check::Bool = true) +``` diff --git a/experimental/FTheoryTools/src/FTheoryTools.jl b/experimental/FTheoryTools/src/FTheoryTools.jl index d8d4cccfa42f..f345d829f8c4 100644 --- a/experimental/FTheoryTools/src/FTheoryTools.jl +++ b/experimental/FTheoryTools/src/FTheoryTools.jl @@ -29,6 +29,7 @@ include("LiteratureModels/create_index.jl") include("G4Fluxes/constructors.jl") include("G4Fluxes/attributes.jl") include("G4Fluxes/properties.jl") +include("G4Fluxes/special_attributes.jl") include("Serialization/tate_models.jl") include("Serialization/weierstrass_models.jl") diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl new file mode 100644 index 000000000000..ce0aceb27051 --- /dev/null +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -0,0 +1,159 @@ +@doc raw""" + basis_of_h22(v::NormalToricVariety; check::Bool = true) + +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) +#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) Convert relations into matrix + remaining_relations_matrix = matrix(QQ, remaining_relations) + + # (10) Now identify those variables that we can remove with those 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) + + # (11) 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 diff --git a/experimental/FTheoryTools/src/exports.jl b/experimental/FTheoryTools/src/exports.jl index 0f2529e4221e..ff5faa5d3b32 100644 --- a/experimental/FTheoryTools/src/exports.jl +++ b/experimental/FTheoryTools/src/exports.jl @@ -33,6 +33,7 @@ export arxiv_model_section export arxiv_version export associated_literature_models export base_space +export basis_of_h22 export blow_up export birational_literature_models export calabi_yau_hypersurface From 2814275cbf32ee53ca142a0fb86ca2ed416a7f7f Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Fri, 1 Nov 2024 17:48:20 +0100 Subject: [PATCH 33/84] [FTheoryTools] Fix LaTeX super indices in documentation --- .../FTheoryTools/src/G4Fluxes/special_attributes.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index ce0aceb27051..4619ed3af0a7 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -4,13 +4,13 @@ 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 +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})$. +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 +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 From 18a592780698e5f18dec345d81ffa6f3b5e43f9b Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Fri, 1 Nov 2024 19:02:29 +0100 Subject: [PATCH 34/84] [FtheoryTools] Set return value of basis_of_h22 --- experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index 4619ed3af0a7..116748999a6f 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -41,8 +41,7 @@ julia> betti_number(Y, 4) == length(h22_basis) true ``` """ -function basis_of_h22(v::NormalToricVariety; check::Bool = true) -#function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} +function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{CohomologyClass} # (0) Some initial checks if check From f49bf9ecb9358129d1d06bcb1499c9a6da3fb968 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Sat, 2 Nov 2024 11:43:11 +0100 Subject: [PATCH 35/84] [FTheoryTools] Implement method ambient_space_models_of_g4_fluxes --- experimental/FTheoryTools/docs/src/g4.md | 39 ++++++++- .../src/G4Fluxes/special_attributes.jl | 84 ++++++++++++++++++- experimental/FTheoryTools/src/exports.jl | 1 + 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/experimental/FTheoryTools/docs/src/g4.md b/experimental/FTheoryTools/docs/src/g4.md index a0359fd0677b..47e3b0b00fe8 100644 --- a/experimental/FTheoryTools/docs/src/g4.md +++ b/experimental/FTheoryTools/docs/src/g4.md @@ -32,9 +32,44 @@ passes_elementary_quantization_checks(gf::G4Flux) ``` -## Auxiliary +## Ambient Space Models for G4-Fluxes + +Focus on 4-dimensional F-theory models $m$, such that the resolution $\widehat{Y}_4$ +of the defining singular elliptically fibered CY 4-fold $\Y_4 \twoheadrightarrow B_3$ +is defined as hypersurface in a complete and simplicial toric variety $X_\Sigma$. In +such a setup, it is convenient to focus on $G_4$-fluxes modelled from the restriction +of elements of $H^{(2,2)}( X_\Sigma, \mathbb{Q})$ to $\widehat{Y}_4$. This method +identifies a basis of $H^{(2,2)}( X_\Sigma, \mathbb{Q})$ and filters out elements, +whose restricton to $\widehat{Y}_4$ is obviously trivial. + +It is important to elaborate a bit more on the meaning of "obviously". To this end, fix a +basis element of $H^{(2,2)}( X_\Sigma, \mathbb{Q})$. Let us denote a corresponding algebraic +cycle by $A = \mathbb{V}(x_i, x_j) \subset X_\Sigma$, where $x_i$, $x_j$ are suitable +homogeneous coordinates. Furthermore, let $\widehat{Y}_4 = \mathbb{V}( p ) \subset X_\Sigma$. +Then of course, we can look at the set-theoretic intersection $\mathbb{V}( p, x_i, x_j)$. +Provided that $p(x_i = 0, x_j = 0)$ is a non-zero constant, this set-theoretic intersection +is trivial. This is exactly the check conducted by the method `ambient_space_models_of_g4_fluxes` +below. However, for reasons of simplicity, this approach is avoid a number of sutleties. + +Namely, we really have to work out the intersection in the Chow ring, that is we should consider +the rational equivalence class of the algebraic cycle $A$ and intersect this class with the +rational equivalence class of the algebraic cycle $\mathbb{V}( p )$. In particular, for +"unlucky" choices of $i, j$, the algebraic cycles $\mathbb{V}( p )$ and $\mathbb{V}(x_i, x_j)$ +may not intersect transversely. This is for instance the case if $i = j$. Such phenomena are +addressed in theory by "moving the algebraic cycles into general position", but in practice this +somewhat tricky. Instances include the following: +1. $i = j$: Then apparently, a self-intersection of $\mathbb{V}(x_i)$ is involved. +2. $p(x_i, x_j) \equiv 0$: This is unexpected for dimensional reasons, and indicates a +non-transverse intersection. + +In both instances, one makes use of the linear relations of $X_\Sigma$ to replace the cycle + $\mathbb{V}(x_i)$ (and/or $\mathbb{V}(x_j)$) with a rational combination of algebraic cycles +$R = \sum_{k = 1}^{N}{c_k \cdot A_k}$, such that $R$ is rationally equivalent to $\mathbb{V}(x_i)$. +From experience, it is then rather common that $R$ and $\mathbb{V}(x_i)$ intersect transversely. +And if not, then modify the non-transverse intersections by using the linear relations again to +replace an involved algebraic cycle. -The following methods are relevant to the discussion and study of $G_4$-fluxes: ```@docs +ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true) basis_of_h22(v::NormalToricVariety; check::Bool = true) ``` diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index 116748999a6f..3e2fc0780913 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -1,5 +1,5 @@ @doc raw""" - basis_of_h22(v::NormalToricVariety; check::Bool = true) + 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$ @@ -156,3 +156,85 @@ function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{Cohomol return basis_of_h22 end + + +@doc raw""" + ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Vector{CohomologyClass} + +Given an F-theory model $m$ defined as hypersurface in a simplicial and +complete toric base, we this method first computes a basis of +$H^(2,2)(X, \mathbb{Q})$ (by use of the method `basis_of_h22` below) and then filters +out "some" basis elements whose restriction to the hypersurface in question +is trivial. The exact meaning of "some" is explained above this method. + +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; 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" => 8)) +Hypersurface model over a concrete base + +julia> g4_amb_list = ambient_space_models_of_g4_fluxes(qsm_model, check = false); + +julia> length(g4_amb_list) == 172 +true +``` +""" +function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool = true)::Vector{CohomologyClass} + + # Entry check + @req base_space(m) isa NormalToricVariety "Base space must be a toric variety for computation of ambient space G4 candidates" + + # Prepare data of the toric ambient space + gS = gens(cox_ring(ambient_space(m))) + mnf = Oscar._minimal_nonfaces(ambient_space(m)) + sr_ideal_pos = [Vector{Int}(Polymake.row(mnf, i)) for i in 1:Polymake.nrows(mnf)] + filtered_h22_basis = basis_of_h22(ambient_space(m), check = check) + + # 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 + 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] + end + + # If all coefficient of `new_pt` sum to zero, keep this generator. + if sum(coefficients(new_pt)) == 0 + continue + end + + # Determine remaining variables, after scaling "away" others. + remaining_vars_list = Set(1:length(gS)) + for my_exps in sr_ideal_pos + len_my_exps = length(my_exps) + inter_len = count(idx -> idx in vanishing_vars_pos, 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 vanishing_vars_pos), my_exps)]) + end + end + remaining_vars_list = collect(remaining_vars_list) + + # If one monomial of `new_pt` has unset positions, then keep this generator. + delete_it = true + for exps in exponents(new_pt) + if any(x -> x != 0, exps[remaining_vars_list]) + delete_it = false + break + end + end + if delete_it + deleteat!(filtered_h22_basis, a) + end + + end + + return filtered_h22_basis +end diff --git a/experimental/FTheoryTools/src/exports.jl b/experimental/FTheoryTools/src/exports.jl index ff5faa5d3b32..6a3f2b2acd41 100644 --- a/experimental/FTheoryTools/src/exports.jl +++ b/experimental/FTheoryTools/src/exports.jl @@ -23,6 +23,7 @@ export add_weighted_resolution export add_weighted_resolution_generating_section export add_weighted_resolution_zero_section export ambient_space +export ambient_space_models_of_g4_fluxes export analyze_fibers export arxiv_doi export arxiv_id From aea00d03978c37dce333316b28d0ff170edd78aa Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Sat, 2 Nov 2024 15:25:26 +0100 Subject: [PATCH 36/84] [FTheoryTools] Fix bug in basis_of_h22 --- .../src/G4Fluxes/special_attributes.jl | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index 3e2fc0780913..75c5d04faf4e 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -133,16 +133,17 @@ function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{Cohomol end - # (9) Convert relations into matrix - remaining_relations_matrix = matrix(QQ, remaining_relations) - - # (10) Now identify those variables that we can remove with those 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) - - # (11) Return the basis elements in terms of cohomology classes + # (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 = [] @@ -152,7 +153,7 @@ function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{Cohomol 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, basis_of_h22) return basis_of_h22 end @@ -173,6 +174,22 @@ to `false` to skip these tests. # Examples ```jldoctest; setup = :(Oscar.LazyArtifacts.ensure_artifact_installed("QSMDB", Oscar.LazyArtifacts.find_artifacts_toml(Oscar.oscardir))) +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> g4_amb_list = ambient_space_models_of_g4_fluxes(t) +2-element Vector{CohomologyClass}: + Cohomology class on a normal toric variety given by z^2 + Cohomology class on a normal toric variety given by y^2 + julia> qsm_model = literature_model(arxiv_id = "1903.00009", model_parameters = Dict("k" => 8)) Hypersurface model over a concrete base From 25e774bf8dec7a77d9d5ab3e3c427de4d8c4fc99 Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Tue, 5 Nov 2024 12:21:03 +0100 Subject: [PATCH 37/84] Intersection theory: correct bug, add more examples (#4269) * Intersection theory: correct bug, add more examples * correction, reaction to review --- .../docs/src/AbstractBundles.md | 7 +- .../docs/src/AbstractVarieties.md | 2 +- experimental/IntersectionTheory/src/Main.jl | 113 +++++++++++++++++- 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/experimental/IntersectionTheory/docs/src/AbstractBundles.md b/experimental/IntersectionTheory/docs/src/AbstractBundles.md index fbe2bf454387..d3e70a15f804 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractBundles.md +++ b/experimental/IntersectionTheory/docs/src/AbstractBundles.md @@ -66,7 +66,6 @@ euler_characteristic(F::AbstractBundle) hilbert_polynomial(F::AbstractBundle) ``` - ## Operations on Abstract Bundles ```@docs @@ -88,3 +87,9 @@ exterior_power(F::AbstractBundle, k::Int) ```@docs symmetric_power(F::AbstractBundle, k::Int) ``` + +## Tests on Abstract Bundles + +```@docs +==(F::AbstractBundle, G::AbstractBundle) +``` diff --git a/experimental/IntersectionTheory/docs/src/AbstractVarieties.md b/experimental/IntersectionTheory/docs/src/AbstractVarieties.md index 5ad8d7d66434..be2d44f769b6 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractVarieties.md +++ b/experimental/IntersectionTheory/docs/src/AbstractVarieties.md @@ -156,7 +156,7 @@ product(X::AbstractVariety, Y::AbstractVariety) ## Integrate Chow Ring Elements ```@julia -integral(x::MPolyDecRingElem) +integral(x::Union{MPolyDecRingElem, MPolyQuoRingElem}) ``` Given an element `x` of the Chow ring of an abstract variety `X`, say, return the integral of `x`. diff --git a/experimental/IntersectionTheory/src/Main.jl b/experimental/IntersectionTheory/src/Main.jl index 5f181a4637a6..8091e6d56c8e 100644 --- a/experimental/IntersectionTheory/src/Main.jl +++ b/experimental/IntersectionTheory/src/Main.jl @@ -50,8 +50,28 @@ true abstract_bundle(X::AbstractVariety, ch::MPolyDecRingOrQuoElem) = AbstractBundle(X, ch) abstract_bundle(X::AbstractVariety, r::RingElement, c::MPolyDecRingOrQuoElem) = AbstractBundle(X, r, c) +####################################################### +@doc raw""" + ==(F::AbstractBundle, G::AbstractBundle) + +Return `true` if `F` is equal to `G`, and `false` otherwise. + +# Examples +```jldoctest +julia> P2 = abstract_projective_space(2) +AbstractVariety of dim 2 + +julia> 3*OO(P2, 1) - OO(P2) == tangent_bundle(P2) # Euler sequence +true + +``` +""" ==(F::AbstractBundle, G::AbstractBundle) = chern_character(F) == chern_character(G) +function Base.hash(F::AbstractBundle, h::UInt) + return hash(chern_character(F), h) +end + @doc raw""" chern_character(F::AbstractBundle) @@ -196,6 +216,28 @@ total_segre_class(F::AbstractBundle) = inv(total_chern_class(F)) segre_class(F::AbstractBundle, k::Int) Return the `k`-th Segre class of `F`. + +# Examples +```jldoctest +julia> G = abstract_grassmannian(3,5) +AbstractVariety of dim 6 + +julia> Q = tautological_bundles(G)[2] +AbstractBundle of rank 2 on AbstractVariety of dim 6 + +julia> segre_class(Q,0) +1 + +julia> segre_class(Q,1) +c[1] + +julia> segre_class(Q,2) +c[2] + +julia> segre_class(Q,3) +c[3] + +``` """ segre_class(F::AbstractBundle, k::Int) = total_segre_class(F)[k] @@ -203,6 +245,33 @@ segre_class(F::AbstractBundle, k::Int) = total_segre_class(F)[k] todd_class(F::AbstractBundle) Return the Todd class of `F`. + +# Examples +```jldoctest +julia> P = abstract_projective_space(4, symbol = "H"); # Hartshorne, p. 433 + +julia> F = exterior_power(cotangent_bundle(P), 3)*OO(P,3); + +julia> G = OO(P, 1)+4*OO(P); + +julia> Z = degeneracy_locus(F, G, 3) # rational surface in P4 +AbstractVariety of dim 2 + +julia> TZ = tangent_bundle(Z); + +julia> K = canonical_class(Z) +z - H + +julia> chern_class(TZ, 1) == -K +true + +julia> tc = todd_class(TZ) +-1//2*z + 1//8*H^2 + 1//2*H + 1 + +julia> tc == 1-1//2*K+1//12*(K^2+chern_class(TZ, 2)) +true + +``` """ todd_class(F::AbstractBundle) = _todd_class(chern_character(F)) @@ -231,6 +300,36 @@ pontryagin_class(F::AbstractBundle, k::Int) = total_pontryagin_class(F)[2k] Return the holomorphic Euler characteristic $\chi(F)$ and the Euler pairing $\chi(F,G)$, respectively. + +# Examples +```jldoctest +julia> P = abstract_projective_space(4, symbol = "H"); # Hartshorne, p. 433 + +julia> F = exterior_power(cotangent_bundle(P), 3)*OO(P,3); + +julia> G = OO(P, 1)+4*OO(P); + +julia> Z = degeneracy_locus(F, G, 3) # rational surface in P4 +AbstractVariety of dim 2 + +julia> TZ = tangent_bundle(Z); + +julia> tc = todd_class(TZ) +-1//2*z + 1//8*H^2 + 1//2*H + 1 + +julia> K = canonical_class(Z) +z - H + +julia> H = hyperplane_class(Z) +H + +julia> ec = euler_characteristic(OO(Z, H)) +4 + +julia> ec == integral(1//2*H*(H-K)+1//12*(K^2+chern_class(TZ, 2))) +true + +``` """ Oscar.euler_characteristic(F::AbstractBundle) = integral(chern_character(F) * todd_class(F.parent)) # Hirzebruch-Riemann-Roch euler_pairing(F::AbstractBundle, G::AbstractBundle) = begin @@ -757,7 +856,7 @@ structure_map(X::AbstractVariety) = X.struct_map @doc raw""" line_bundle(X::AbstractVariety, n::RingElement) - line_bundle(X::AbstractVariety, D::MPolyDecRingElem) + line_bundle(X::AbstractVariety, D::Union{MPolyDecRingElem, MPolyQuoRingElem}) Return the line bundle $\mathcal O_X(n)$ on `X` if `X` has been given a hyperplane class, or a line bundle $\mathcal O_X(D)$ with first Chern class $D$. @@ -771,13 +870,19 @@ AbstractVariety of dim 2 julia> tautological_bundles(P2)[1] == OO(P2, -1) true +julia> h = gens(P2)[1] +h + +julia> OO(P2, h) == OO(P2, 1) +true + ``` """ line_bundle(X::AbstractVariety, n::RingElement) = AbstractBundle(X, 1, 1+n*X.O1) -line_bundle(X::AbstractVariety, D::MPolyDecRingElem) = AbstractBundle(X, 1, 1+D[1]) +line_bundle(X::AbstractVariety, D::Union{MPolyDecRingElem, MPolyQuoRingElem}) = AbstractBundle(X, 1, 1+D[1]) (OO)(X::AbstractVariety, n::RingElement) = line_bundle(X, n) -OO(X::AbstractVariety, D::MPolyDecRingElem) = line_bundle(X, D) +OO(X::AbstractVariety, D::Union{MPolyDecRingElem, MPolyQuoRingElem}) = line_bundle(X, D) @doc raw""" degree(X::AbstractVariety) @@ -1451,7 +1556,7 @@ julia> basis(P2xP2) betti_numbers(X::AbstractVariety) = length.(basis(X)) @doc raw""" - integral(x::MPolyDecRingElem) + integral(x:::Union{MPolyDecRingElem, MPolyQuoRingElem}) Given an element `x` of the Chow ring of an abstract variety `X`, say, return the integral of `x`. From 19bf27c5defe71a1e73c3fcbbaf454aa10b8f9c0 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Tue, 5 Nov 2024 12:33:52 +0100 Subject: [PATCH 38/84] add `stabilizer` for G-sets w.r.t. derived actions (#4265) The stabilizer of a `Set` of points in the G-set is the setwise stabilizer. The stabilizer of a `Vector` or `Tuple` of points in the G-set is the pointwise stabilizer. --- src/Groups/gsets.jl | 45 ++++++++++++++++++++++++++++++++++++++++++-- test/Groups/gsets.jl | 20 +++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index f5ce457194a4..a1a5b9277963 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -450,17 +450,29 @@ julia> map(length, orbs) """ stabilizer(Omega::GSet{T,S}) stabilizer(Omega::GSet{T,S}, omega::S = representative(Omega); check::Bool = true) where {T,S} + stabilizer(Omega::GSet{T,S}, omega::Set{S}; check::Bool = true) where {T,S} + stabilizer(Omega::GSet{T,S}, omega::Vector{S}; check::Bool = true) where {T,S} + stabilizer(Omega::GSet{T,S}, omega::Tuple{Vararg{S}}; check::Bool = true) where {T,S} Return the subgroup of `G = acting_group(Omega)` that fixes `omega`, together with the embedding of this subgroup into `G`. + +If `omega` is a `Set` of points in `Omega` +then `stabilizer` means the setwise stabilizer of the entries in `omega`. +If `omega` is a `Vector` or a `Tuple` of points in `Omega` +then `stabilizer` means the pointwise stabilizer of the entries in `omega`. + If `check` is `false` then it is not checked whether `omega` is in `Omega`. # Examples ```jldoctest -julia> Omega = gset(symmetric_group(3)); +julia> Omega = gset(symmetric_group(4)); julia> stabilizer(Omega) -(Permutation group of degree 3 and order 2, Hom: permutation group -> Sym(3)) +(Permutation group of degree 4 and order 6, Hom: permutation group -> Sym(4)) + +julia> stabilizer(Omega, [1, 2]) +(Permutation group of degree 4 and order 2, Hom: permutation group -> Sym(4)) ``` """ @attr Tuple{sub_type(T), Map{sub_type(T), T}} function stabilizer(Omega::GSet{T,S}) where {T,S} @@ -474,6 +486,35 @@ function stabilizer(Omega::GSet{T,S}, omega::S; check::Bool = true) where {T,S} return stabilizer(G, omega, gfun) end +# support `stabilizer` under "derived" actions: +# If the given point is a set of the element type of the G-set +# then compute the setwise stabilizer. +# If the given point is a tuple or vector of the element type of the G-set +# then compute the pointwise stabilizer. + +function stabilizer(Omega::GSet{T,S}, omega::Set{S}; check::Bool = true) where {T,S} + check && @req all(in(Omega), omega) "omega must be a set of elements of Omega" + G = acting_group(Omega) + gfun = action_function(Omega) + derived_fun = function(x, g) return Set(gfun(y, g) for y in x); end + return stabilizer(G, omega, derived_fun) +end + +function stabilizer(Omega::GSet{T,S}, omega::Vector{S}; check::Bool = true) where {T,S} + check && @req all(in(Omega), omega) "omega must be a vector of elements of Omega" + G = acting_group(Omega) + gfun = action_function(Omega) + derived_fun = function(x, g) return [gfun(y, g) for y in x]; end + return stabilizer(G, omega, derived_fun) +end + +function stabilizer(Omega::GSet{T,S}, omega::Tuple{Vararg{S}}; check::Bool = true) where {T,S} + check && @req all(in(Omega), omega) "omega must be a tuple of elements of Omega" + G = acting_group(Omega) + gfun = action_function(Omega) + derived_fun = function(x, g) return Tuple([gfun(y, g) for y in x]); end + return stabilizer(G, omega, derived_fun) +end ############################################################################# ## diff --git a/test/Groups/gsets.jl b/test/Groups/gsets.jl index f8fd31d602d1..046588fb1148 100644 --- a/test/Groups/gsets.jl +++ b/test/Groups/gsets.jl @@ -12,20 +12,34 @@ @test ! is_semiregular(Omega) @test collect(Omega) == 1:6 # ordering is kept @test order(stabilizer(Omega)[1]) * length(Omega) == order(G) + @test order(stabilizer(Omega, 1)[1]) == 120 + @test order(stabilizer(Omega, Set([1, 2]))[1]) == 48 + @test order(stabilizer(Omega, [1, 2])[1]) == 24 + @test order(stabilizer(Omega, (1, 2))[1]) == 24 Omega = gset(G, [Set([1, 2])]) # action on unordered pairs @test isa(Omega, GSet) @test length(Omega) == 15 - @test order(stabilizer(Omega)[1]) * length(Omega) == order(G) @test length(orbits(Omega)) == 1 @test is_transitive(Omega) @test ! is_regular(Omega) @test ! is_semiregular(Omega) + @test order(stabilizer(Omega)[1]) * length(Omega) == order(G) + @test order(stabilizer(Omega, Set([1, 3]))[1]) == 48 + @test order(stabilizer(Omega, Set([Set([1, 2]), Set([1, 3])]))[1]) == 12 + @test order(stabilizer(Omega, [Set([1, 2]), Set([1, 3])])[1]) == 6 + @test order(stabilizer(Omega, (Set([1, 2]), Set([1, 3])))[1]) == 6 + @test_throws MethodError stabilizer(Omega, [1, 2]) Omega = gset(G, [[1, 2]]) # action on ordered pairs @test isa(Omega, GSet) @test length(Omega) == 30 @test order(stabilizer(Omega)[1]) * length(Omega) == order(G) + @test order(stabilizer(Omega, [1, 3])[1]) == 24 + @test order(stabilizer(Omega, Set([[1, 2], [1, 3]]))[1]) == 12 + @test order(stabilizer(Omega, [[1, 2], [1, 3]])[1]) == 6 + @test order(stabilizer(Omega, ([1, 2], [1, 3]))[1]) == 6 + @test_throws MethodError stabilizer(Omega, Set([1, 2])) @test length(orbits(Omega)) == 1 @test is_transitive(Omega) @test ! is_regular(Omega) @@ -46,6 +60,10 @@ @test isa(Omega, GSet) @test length(Omega) == 740 @test order(stabilizer(Omega, omega)[1]) * length(orbit(Omega, omega)) == order(G) + @test order(stabilizer(Omega, Set([omega, [1,0,0,1,0,1]]))[1]) == 8 + @test order(stabilizer(Omega, [omega, [1,0,0,1,0,1]])[1]) == 4 + @test order(stabilizer(Omega, (omega, [1,0,0,1,0,1]))[1]) == 4 + @test_throws MethodError stabilizer(Omega, Set(omega)) @test length(orbits(Omega)) == 2 @test ! is_transitive(Omega) @test ! is_regular(Omega) From 452a1a4e684b4d0c392440390d67f5134b6b3e90 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Tue, 5 Nov 2024 15:28:42 +0100 Subject: [PATCH 39/84] [ToricVarieties] Serialize cohomology classes (#4266) [ToricVarieties] Serialize cohomology classes --------- Co-authored-by: antonydellavecchia --- src/Serialization/ToricGeometry.jl | 28 +++++++++++++++++++++++++++- test/Serialization/ToricGeometry.jl | 10 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Serialization/ToricGeometry.jl b/src/Serialization/ToricGeometry.jl index c7434869be26..7c8fad660f2e 100644 --- a/src/Serialization/ToricGeometry.jl +++ b/src/Serialization/ToricGeometry.jl @@ -2,7 +2,7 @@ # Toric varieties @register_serialization_type AffineNormalToricVariety uses_id -@register_serialization_type NormalToricVariety uses_id [:cox_ring, :class_group] +@register_serialization_type NormalToricVariety uses_id [:cox_ring, :class_group, :cohomology_ring] function save_object(s::SerializerState, ntv::T) where T <: NormalToricVarietyType @@ -93,3 +93,29 @@ function load_object(s::DeserializerState, ::Type{ToricDivisorClass}, tv::Normal pmdiv = Polymake._lookup_multi(pm_object(tv), "DIVISOR", index-1) return toric_divisor_class(ToricDivisor(pmdiv, tv, coeffs)) end + +################################################################################ +# Cohomology classes on toric varieties +@register_serialization_type CohomologyClass uses_params + +function save_type_params(s::SerializerState, obj::CohomologyClass) + save_data_dict(s) do + save_object(s, encode_type(CohomologyClass), :name) + save_typed_object(s, obj.v, :params) + end +end + +function load_type_params(s::DeserializerState, ::Type{<:CohomologyClass}) + return load_typed_object(s) +end + +function save_object(s::SerializerState, cc::CohomologyClass) + save_data_dict(s) do + save_object(s, lift(polynomial(cc)), :polynomial) + end +end + +function load_object(s::DeserializerState, ::Type{CohomologyClass}, tv::NormalToricVarietyType) + poly = load_object(s, MPolyDecRingElem, base_ring(cohomology_ring(tv)), :polynomial) + return cohomology_class(tv, MPolyQuoRingElem(poly, cohomology_ring(tv))) +end diff --git a/test/Serialization/ToricGeometry.jl b/test/Serialization/ToricGeometry.jl index 9701264e9abe..b8c8d9187638 100644 --- a/test/Serialization/ToricGeometry.jl +++ b/test/Serialization/ToricGeometry.jl @@ -48,5 +48,15 @@ end end + @testset "ToricDivisorClass" begin + pp = projective_space(NormalToricVariety, 2) + cc = volume_form(pp) + cc_list = [cc] + test_save_load_roundtrip(path, cc_list) do loaded + @test cc == loaded[1] + @test toric_variety(cc) === toric_variety(loaded[1]) + end + end + end end From c6d3aac8d9a484842eaab3bf97bab83a3266cc83 Mon Sep 17 00:00:00 2001 From: Marcel Salmon <125460956+Syz-MS@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:40:47 +0100 Subject: [PATCH 40/84] monomial basis for MPolyQuoLocRing (#4235) monomial_basis for MPolyQuoLocRing --- src/Rings/mpolyquo-localizations.jl | 77 ++++++++++++++++++++++++++++ test/Rings/mpolyquo-localizations.jl | 17 ++++++ 2 files changed, 94 insertions(+) diff --git a/src/Rings/mpolyquo-localizations.jl b/src/Rings/mpolyquo-localizations.jl index eba0faa6c8b2..633652679a38 100644 --- a/src/Rings/mpolyquo-localizations.jl +++ b/src/Rings/mpolyquo-localizations.jl @@ -1850,6 +1850,83 @@ function vector_space_dimension(R::MPolyQuoLocRing{<:Field, <:Any,<:Any, <:Any, return vector_space_dimension(quo(base_ring(R),ideal(base_ring(R),gens(LI)))[1]) end +@doc raw""" + _monomial_basis(L::MPolyLocRing{<:Field, <:Any, <:Any, <:Any, <:MPolyComplementOfKPointIdeal}, I::MPolyLocalizedIdeal) + +If, say, `A = L/I`, where `L` is a localization of multivariate polynomial ring over a field +`K` at a point `p`, and `I` is an ideal of `L`, return a vector of monomials of `L` +such that the residue classes of these monomials form a basis of `A` as a `K`-vector +space. +!!! note + This is an internal method for computing a monomial basis without creating the quotient. +""" +function _monomial_basis(L::MPolyLocRing{<:Field, <:Any, <:Any, <:Any, <:MPolyComplementOfKPointIdeal}, I::MPolyLocalizedIdeal) + base_ring(I) == L || error("ideal does not belong to the correct ring") + G = numerator.(gens(I)) + shift,_ = base_ring_shifts(L) + G_0 = shift.(G) + R = base_ring(L) + LI = leading_ideal(ideal(R, G_0), ordering = negdeglex(R)) + return L.(monomial_basis(quo(R, LI)[1])) +end + +@doc raw""" + monomial_basis(A::MPolyQuoLocRing{<:Field, <:Any, <:Any, <:Any, <:MPolyComplementOfKPointIdeal}) + +If, say, `A = L/I`, where `L` is a localization of multivariate polynomial ring over a field +`K` at a point `p`, and `I` is an ideal of `L`, return a vector of monomials of `L` +such that the residue classes of these monomials form a basis of `A` as a `K`-vector +space. +!!! note + The monomials are for readabilty in the varibles of the underlying polynomial ring of `L` and not in the variables of power series ring $K[[x_1-p_1,...,x_n-p_n]]$ in which `L` is embedded. +!!! note + If `A` is not finite-dimensional as a `K`-vector space, an error is thrown. +# Examples +```jldoctest +julia> R, (x,y) = QQ["x","y"]; + +julia> f = (x^2-y^3)*(y-1); # 3 singularities, a cusp singularity and two node singularities + +julia> A = tjurina_algebra(f) +Quotient + of multivariate polynomial ring in 2 variables x, y + over rational field + by ideal (x^2*y - x^2 - y^4 + y^3, 2*x*y - 2*x, x^2 - 4*y^3 + 3*y^2) + +julia> monomial_basis(A) +4-element Vector{QQMPolyRingElem}: + y^2 + y + x + 1 + +julia> Aloc0,_ = localization(A, complement_of_point_ideal(R, [0,0])); + +julia> monomial_basis(Aloc0) +2-element Vector{Oscar.MPolyLocRingElem{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem, Oscar.MPolyComplementOfKPointIdeal{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem}}}: + y + 1 + +julia> Aloc1,_ = localization(A, complement_of_point_ideal(R, [1,1])); + +julia> monomial_basis(Aloc1) +1-element Vector{Oscar.MPolyLocRingElem{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem, Oscar.MPolyComplementOfKPointIdeal{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem}}}: + 1 + +julia> Aloc2,_ = localization(A, complement_of_point_ideal(R, [-1,1])); + +julia> monomial_basis(Aloc2) +1-element Vector{Oscar.MPolyLocRingElem{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem, Oscar.MPolyComplementOfKPointIdeal{QQField, QQFieldElem, QQMPolyRing, QQMPolyRingElem}}}: + 1 + +julia> vector_space_dimension(A) == vector_space_dimension(Aloc0) + vector_space_dimension(Aloc1) + vector_space_dimension(Aloc2) +true +``` +""" +function monomial_basis(A::MPolyQuoLocRing{<:Field, <:Any, <:Any, <:Any, <:MPolyComplementOfKPointIdeal}) + return _monomial_basis(localized_ring(A), modulus(A)) +end + function is_finite_dimensional_vector_space(R::MPolyQuoLocRing) throw(NotImplementedError(:is_finite_dimensional_vector_space, R)) end diff --git a/test/Rings/mpolyquo-localizations.jl b/test/Rings/mpolyquo-localizations.jl index d3c910c10662..ce7a37d225de 100644 --- a/test/Rings/mpolyquo-localizations.jl +++ b/test/Rings/mpolyquo-localizations.jl @@ -510,3 +510,20 @@ end @test Oscar._get_generators_string_one_line(ideal(R, x)) == "with 100 generators" @test Oscar._get_generators_string_one_line(ideal(R, [sum(x)])) == "with 1 generator" end + +@testset "monomial_basis MPolyQuoLocRing" begin + R, (x,y) = QQ["x", "y"] + L, _ = localization(R, complement_of_point_ideal(R, [0,0])) + Q1, _ = quo(L, ideal(L, L(x+1))) + @test isempty(monomial_basis(Q1)) + Q2, _ = quo(L, ideal(L, L(x^2))) + @test_throws InfiniteDimensionError() monomial_basis(Q2) + Q3, _ = quo(L, ideal(L, L.([x^2, y^3]))) + @test monomial_basis(Q3) == L.([x*y^2, y^2, x*y, y, x, 1]) + Q4,_ = quo(L, ideal(L, [x^2-x, y^2-2*y])) + @test monomial_basis(Q4) == [L(1)] #test for difference in localized and non-localized case + @test Oscar._monomial_basis(L, ideal(L, [x^3*(x-1), y*(y-1)*(y-2)])) == L.([x^2, x, 1]) + L1, _ = localization(R, complement_of_point_ideal(R, [1,2])) + @test Oscar._monomial_basis(L1, ideal(L1, [(x-1)^2, (y-2)^2])) == L1.([x*y, y, x, 1]) + @test isempty(Oscar._monomial_basis(L1, ideal(L1, L1.([x, y])))) +end From 50d2662afb8bb097cb08d5ecaaefa82dbba3d5e7 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Tue, 5 Nov 2024 14:46:12 +0100 Subject: [PATCH 41/84] [FTheoryTools] Bug fix - set attribute basis_of_h22 --- .../FTheoryTools/src/G4Fluxes/special_attributes.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl index 75c5d04faf4e..49e20bc07bcd 100644 --- a/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl +++ b/experimental/FTheoryTools/src/G4Fluxes/special_attributes.jl @@ -153,7 +153,7 @@ function basis_of_h22(v::NormalToricVariety; check::Bool = true)::Vector{Cohomol 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, basis_of_h22) return basis_of_h22 end @@ -203,12 +203,17 @@ function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool # Entry check @req base_space(m) isa NormalToricVariety "Base space must be a toric variety for computation of ambient space G4 candidates" - + if has_attribute(m, :ambient_space_models_of_g4_fluxes) + return get_attribute(m, :ambient_space_models_of_g4_fluxes) + end + + # 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) + # Prepare data of the toric ambient space gS = gens(cox_ring(ambient_space(m))) mnf = Oscar._minimal_nonfaces(ambient_space(m)) sr_ideal_pos = [Vector{Int}(Polymake.row(mnf, i)) for i in 1:Polymake.nrows(mnf)] - filtered_h22_basis = basis_of_h22(ambient_space(m), check = check) # Filter out basis elements for a in length(filtered_h22_basis):-1:1 @@ -253,5 +258,7 @@ function ambient_space_models_of_g4_fluxes(m::AbstractFTheoryModel; check::Bool end + set_attribute!(m, :ambient_space_models_of_g4_fluxes, filtered_h22_basis) return filtered_h22_basis + end From c40f14e614bed3394a7a8dd25226c8b475701bd5 Mon Sep 17 00:00:00 2001 From: JohnAAbbott <124266874+JohnAAbbott@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:44:01 +0100 Subject: [PATCH 42/84] Resolves `BoundError` in `weights(hook_lengths(...))` (#4270) * Added arg check to weights * weight seq of non-semi-standard YT gives error * Inputs for weight seq tests are now semi-standard --- .../EnumerativeCombinatorics/tableaux.jl | 12 ++++++++---- .../EnumerativeCombinatorics/tableaux.jl | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl b/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl index 522c13f0fdbb..61aa41ad3241 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl @@ -48,7 +48,7 @@ young_tableau function young_tableau(::Type{T}, v::Vector{Vector{TT}}; check::Bool = true) where {T <: IntegerUnion, TT <: IntegerUnion} if check - @req _defines_partition(map(length, v)) "The input does not define a Young tableau" + @req _defines_partition(map(length, v)) "The input does not define a Young tableau: lengths of rows must be weakly decreasing" end return YoungTableau{T}(v) end @@ -222,20 +222,24 @@ Return the shape of the tableau `tab`, i.e. the partition given by the lengths of the rows of the tableau. """ function shape(tab::YoungTableau{T}) where T - return partition(T[ length(tab[i]) for i = 1:length(tab) ], check = false) + # Line below DOES NOT CHECK that the lengths of the rows are weakly decreasing + return partition(T[ length(tab[i]) for i = 1:length(tab) ]; check = false) end @doc raw""" weight(tab::YoungTableau) -Return the weight of the tableau `tab` as an array whose `i`-th element gives -the number of times the integer `i` appears in the tableau. +Return the weight sequence of the tableau `tab` as an array whose `i`-th element +gives the number of times the integer `i` appears in the tableau. """ function weight(tab::YoungTableau) + @req is_semistandard(tab) "Tableau must be (semi-)standard" + if isempty(tab) return Int[] end + # Computation of max must be changed if we want to permit non-semi-standard YT max = 0 for i = 1:length(tab) if max < tab[i][end] diff --git a/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl b/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl index c3f539917395..16d1f844c703 100644 --- a/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl +++ b/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl @@ -6,9 +6,9 @@ @test reading_word(young_tableau(Array{Int,1}[])) == Int[] # weight - @test weight(young_tableau([[1,2,3],[1,2],[1]])) == [3,2,1] + @test weight(young_tableau([[1,2,3],[2,3],[3]])) == [1,2,3] @test weight(young_tableau([[1,2,3,4,5]])) == [1,1,1,1,1] - @test weight(young_tableau([[1],[1],[1]])) == [3] + @test_throws ArgumentError weight(young_tableau([[1],[1],[1]])) @test weight(young_tableau(Array{Int,1}[])) == Int[] # is_standard From 953ad2324b879b0c9c2aedb0048ec1d196d5c618 Mon Sep 17 00:00:00 2001 From: Erik Paemurru <143521159+paemurru@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:21:32 +0100 Subject: [PATCH 43/84] Update license (#4257) Co-authored-by: Max Horn --- LICENSE.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index d5ab55d1895a..2faf769ff70e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,11 @@ The Oscar.jl package is licensed under the GNU Public License, Version 3.0+: -> Copyright (c) 2019 The OSCAR Development Team. +> Copyright (c) 2019-2024 The OSCAR Development Team +> +> See for a list of OSCAR +> Development Team members. Note that this list may occasionally be slightly +> outdated. Additional contributors may be found in the `git log` of the main +> OSCAR repository and other related repositories. > This program is free software: you can redistribute it and/or modify > it under the terms of the GNU General Public License as published by > the Free Software Foundation, either version 3 of the License, or From 8bea0b6e53d1db5ed79e0282d43de0b3fd85ef94 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Wed, 6 Nov 2024 13:46:21 +0100 Subject: [PATCH 44/84] fix `_as_subgroups` (#4277) and extend tests accordingly, and fix `==` for group homomorphisms --- src/Groups/homomorphisms.jl | 2 +- src/Groups/sub.jl | 10 +---- test/Groups/subgroups_and_cosets.jl | 60 +++++++++++++++++------------ 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/Groups/homomorphisms.jl b/src/Groups/homomorphisms.jl index e42a1cc7d151..6b1424491325 100644 --- a/src/Groups/homomorphisms.jl +++ b/src/Groups/homomorphisms.jl @@ -9,7 +9,7 @@ function Base.show(io::IO, x::GAPGroupHomomorphism) end -function ==(f::GAPGroupHomomorphism{S,T}, g::GAPGroupHomomorphism{S,T}) where S where T +function ==(f::GAPGroupHomomorphism, g::GAPGroupHomomorphism) return GapObj(f) == GapObj(g) end diff --git a/src/Groups/sub.jl b/src/Groups/sub.jl index b107a3062912..95162c68fa3c 100644 --- a/src/Groups/sub.jl +++ b/src/Groups/sub.jl @@ -157,15 +157,7 @@ end # convert a GAP list of subgroups into a vector of Julia groups objects function _as_subgroups(G::T, subs::GapObj) where T <: GAPGroup - res = Vector{T}(undef, length(subs)) - for i = 1:length(res) - res[i] = _as_subgroup_bare(G, subs[i]::GapObj) - end - return res -end - -function _as_subgroups(G::PcGroup, subs::GapObj) - res = Vector{SubPcGroup}(undef, length(subs)) + res = Vector{sub_type(T)}(undef, length(subs)) for i = 1:length(res) res[i] = _as_subgroup_bare(G, subs[i]::GapObj) end diff --git a/test/Groups/subgroups_and_cosets.jl b/test/Groups/subgroups_and_cosets.jl index abf2fcea09b9..298fc50958c0 100644 --- a/test/Groups/subgroups_and_cosets.jl +++ b/test/Groups/subgroups_and_cosets.jl @@ -1,29 +1,41 @@ @testset "Subgroups" begin - G = symmetric_group(7) - x = cperm(G,[1,2,3,4,5,6,7]) - y = cperm(G,[1,2,3]) - z = cperm(G,[1,2]) - - H,f=sub(G,[x,y]) - K,g=sub(x,y) - @test H isa PermGroup - @test H==alternating_group(7) - @test domain(f)==H - @test codomain(f)==G - @test [f(x) for x in gens(H)]==gens(H) - @test (H,f)==(K,g) - @test is_subset(K, G) - flag, emb = is_subgroup(K, G) - @test flag - @test g == emb - @test g == embedding(K, G) - @test K === domain(emb) - @test G === codomain(emb) - @test is_normal_subgroup(H, G) - H,f=sub(G,[x,z]) - @test H==G - @test f==id_hom(G) + S = symmetric_group(7) + Sx = cperm(S, [1, 2, 3, 4, 5, 6, 7]) + Sy = cperm(S, [1, 2, 3]) + Sz = cperm(S, [1, 2]) + + for T in [PermGroup, FPGroup] + iso = isomorphism(T, S) + G = codomain(iso) + x = iso(Sx) + y = iso(Sy) + z = iso(Sz) + + H, f = sub(G, [x, y]) + K, g = sub(x, y) + @test H isa Oscar.sub_type(T) + @test domain(f) == H + @test codomain(f) == G + @test [f(x) for x in gens(H)] == gens(H) + @test (H, f) == (K, g) + @test is_subset(K, G) + flag, emb = is_subgroup(K, G) + @test flag + @test g == emb + @test g == embedding(K, G) + @test K === domain(emb) + @test G === codomain(emb) + @test is_normal_subgroup(H, G) + H, f = sub(G, [x, z]) + @test H == G + @test f == id_hom(G) + end + G = symmetric_group(7) + x = cperm(G, [1, 2, 3, 4, 5, 6, 7]) + y = cperm(G, [1, 2, 3]) + H, f = sub(G, [x, y]) + @test H == alternating_group(7) @test !is_subset(symmetric_group(8), G) @test_throws ArgumentError embedding(symmetric_group(8), G) From e91af4152646749a856e987cb640e0eac4512c21 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Wed, 6 Nov 2024 15:28:22 +0100 Subject: [PATCH 45/84] Fixes small_generating_set, mininimal_primes over a number field (#4279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: HechtiDerLachs Co-authored-by: ederc Co-authored-by: Lars Göttgens Co-authored-by: Max Horn --- src/Rings/mpoly-ideals.jl | 16 +++++++++++++--- test/Rings/mpoly.jl | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Rings/mpoly-ideals.jl b/src/Rings/mpoly-ideals.jl index 629681cc028c..6f69dd8d0068 100644 --- a/src/Rings/mpoly-ideals.jl +++ b/src/Rings/mpoly-ideals.jl @@ -1203,17 +1203,27 @@ function minimal_primes( # This will in many cases lead to an easy simplification of the problem if factor_generators - J = typeof(I)[ideal(R, elem_type(R)[])] + J = [ideal(R, gens(I))] # A copy of I as initialization for g in gens(I) K = typeof(I)[] is_zero(g) && continue for (b, k) in factor(g) + # Split the already collected components with b for j in J push!(K, j + ideal(R, b)) end end J = K end + + unique_comp = typeof(I)[] + for q in J + is_one(q) && continue + q in unique_comp && continue + push!(unique_comp, q) + end + J = unique_comp + # unique! seems to fail here. We have to do it manually. pre_result = filter!(!is_one, vcat([minimal_primes(j; algorithm, factor_generators=false) for j in J]...)) result = typeof(I)[] @@ -2110,7 +2120,8 @@ function small_generating_set( computed_gb = IdealGens(ring, sing_gb, true) if !haskey(I.gb,computed_gb.ord) # if not yet present, store gb for later use - I.gb[computed_gb.ord] = computed_gb + I.gb[computed_gb.ord] = computed_gb + I.gb[computed_gb.ord].isGB = true end # we do not have a notion of minimal generating set in this context! @@ -2328,4 +2339,3 @@ end function hash(I::Ideal, c::UInt) return hash(base_ring(I), c) end - diff --git a/test/Rings/mpoly.jl b/test/Rings/mpoly.jl index 8c81bdd1d30b..1f9c33a9dcae 100644 --- a/test/Rings/mpoly.jl +++ b/test/Rings/mpoly.jl @@ -159,7 +159,6 @@ end l = minimal_primes(i, algorithm=:charSets) @test length(l) == 2 @test l[1] == i1 && l[2] == i2 || l[1] == i2 && l[2] == i1 - R, (a, b, c, d) = polynomial_ring(ZZ, [:a, :b, :c, :d]) i = ideal(R, [R(9), (a+3)*(b+3)]) i1 = ideal(R, [R(3), a]) @@ -232,6 +231,19 @@ end R, (x, y) = polynomial_ring(QQ, [:x, :y]) I = ideal(R, [one(R)]) @test is_prime(I) == false + + J = ideal(R, [x*(x-1), y*(y-1), x*y]) + l = minimal_primes(J) + @test length(l) == 3 + + QQt, t = QQ[:t] + kk, a = extension_field(t^2 + 1) + + R, (x, y) = kk[:x, :y] + J = ideal(R, [x^2 + 1, y^2 + 1, (x - a)*(y - a)]) + l = minimal_primes(J) + @test length(l) == 3 + end @testset "Groebner" begin @@ -601,4 +613,3 @@ end I = ideal(P, elem_type(P)[]) @test !radical_membership(x, I) end - From 309dcf7bc8a0ff4e32394d91d60dfa26eb79b1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 6 Nov 2024 21:37:57 +0100 Subject: [PATCH 46/84] Replace all uses of `FlintQQ` and `FlintZZ` (#4261) --- docs/src/DeveloperDocumentation/caching.md | 6 +++--- experimental/QuadFormAndIsom/test/runtests.jl | 6 +++--- src/NumberTheory/GaloisGrp/GaloisGrp.jl | 2 +- src/NumberTheory/GaloisGrp/Qt.jl | 4 ++-- src/NumberTheory/NmbThy.jl | 16 +++++++-------- src/PolyhedralGeometry/solving_integrally.jl | 20 +++++++++---------- src/Rings/NumberField.jl | 4 ++-- src/Rings/binomial_ideals.jl | 14 ++++++------- src/Rings/hilbert.jl | 4 ++-- src/Rings/mpoly-graded.jl | 2 +- test/AlgebraicGeometry/Surfaces/K3Auto.jl | 20 +++++++++---------- test/Groups/matrixgroups.jl | 2 +- test/PolyhedralGeometry/solve_integrally.jl | 8 ++++---- test/Rings/NumberField.jl | 18 ++++++++--------- test/Rings/binomial-ideals.jl | 6 +++--- 15 files changed, 66 insertions(+), 66 deletions(-) diff --git a/docs/src/DeveloperDocumentation/caching.md b/docs/src/DeveloperDocumentation/caching.md index 00db05dcb431..220c6344e3d8 100644 --- a/docs/src/DeveloperDocumentation/caching.md +++ b/docs/src/DeveloperDocumentation/caching.md @@ -96,9 +96,9 @@ like `cyclotomic_polynomial` ``` module Globals using Hecke - const Qx, _ = polynomial_ring(FlintQQ, :x, cached = false) - const Zx, _ = polynomial_ring(FlintZZ, :x, cached = false) - const Zxy, _ = polynomial_ring(FlintZZ, [:x, :y], cached = false) + const Qx, _ = polynomial_ring(QQ, :x, cached = false) + const Zx, _ = polynomial_ring(ZZ, :x, cached = false) + const Zxy, _ = polynomial_ring(ZZ, [:x, :y], cached = false) end ``` You can use these in your own code as well, or imitate this pattern if convenient. diff --git a/experimental/QuadFormAndIsom/test/runtests.jl b/experimental/QuadFormAndIsom/test/runtests.jl index 4ac217f81c89..a72b1f114981 100644 --- a/experimental/QuadFormAndIsom/test/runtests.jl +++ b/experimental/QuadFormAndIsom/test/runtests.jl @@ -48,7 +48,7 @@ end @test evaluate(minimal_polynomial(Vf), -1) == 0 @test evaluate(characteristic_polynomial(Vf), 0) == 1 - G = matrix(FlintQQ, 6, 6 ,[3, 1, -1, 1, 0, 0, 1, 3, 1, 1, 1, 1, -1, 1, 3, 0, 0, 1, 1, 1, 0, 4, 2, 2, 0, 1, 0, 2, 4, 2, 0, 1, 1, 2, 2, 4]) + G = matrix(QQ, 6, 6 ,[3, 1, -1, 1, 0, 0, 1, 3, 1, 1, 1, 1, -1, 1, 3, 0, 0, 1, 1, 1, 0, 4, 2, 2, 0, 1, 0, 2, 4, 2, 0, 1, 1, 2, 2, 4]) V = quadratic_space(QQ, G) f = matrix(QQ, 6, 6, [1 0 0 0 0 0; 0 0 -1 0 0 0; -1 1 -1 0 0 0; 0 0 0 1 0 -1; 0 0 0 0 0 -1; 0 0 0 0 1 -1]) Vf = @inferred quadratic_space_with_isometry(V, f) @@ -386,8 +386,8 @@ end @test length(reps) == 1 ## Odd case - B = matrix(FlintQQ, 5, 5 ,[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]); - G = matrix(FlintQQ, 5, 5 ,[3, 1, 0, 0, 0, 1, 3, 1, 1, -1, 0, 1, 3, 0, 0, 0, 1, 0, 3, 0, 0, -1, 0, 0, 3]); + B = matrix(QQ, 5, 5 ,[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]); + G = matrix(QQ, 5, 5 ,[3, 1, 0, 0, 0, 1, 3, 1, 1, -1, 0, 1, 3, 0, 0, 0, 1, 0, 3, 0, 0, -1, 0, 0, 3]); L = integer_lattice(B, gram = G); k = lattice_in_same_ambient_space(L, B[2:2, :]) N = orthogonal_submodule(L, k) diff --git a/src/NumberTheory/GaloisGrp/GaloisGrp.jl b/src/NumberTheory/GaloisGrp/GaloisGrp.jl index 8e3ef37c81ac..7b5246099f7e 100644 --- a/src/NumberTheory/GaloisGrp/GaloisGrp.jl +++ b/src/NumberTheory/GaloisGrp/GaloisGrp.jl @@ -579,7 +579,7 @@ end function Nemo.roots_upper_bound(f::ZZMPolyRingElem, t::Int = 0) @assert nvars(parent(f)) == 2 - Qs, s = rational_function_field(FlintQQ, "t", cached = false) + Qs, s = rational_function_field(QQ, "t", cached = false) Qsx, x = polynomial_ring(Qs, cached = false) F = evaluate(f, [x, Qsx(s)]) dis = numerator(discriminant(F)) diff --git a/src/NumberTheory/GaloisGrp/Qt.jl b/src/NumberTheory/GaloisGrp/Qt.jl index 2b69cc4c8ef9..ad9791b87fba 100644 --- a/src/NumberTheory/GaloisGrp/Qt.jl +++ b/src/NumberTheory/GaloisGrp/Qt.jl @@ -88,7 +88,7 @@ for analysis of the denominator and the infinite valuations function _galois_init(F::Generic.FunctionField{QQFieldElem}; tStart::Int = -1) f = defining_polynomial(F) @assert is_monic(f) - Zxy, (x, y) = polynomial_ring(FlintZZ, 2, cached = false) + Zxy, (x, y) = polynomial_ring(ZZ, 2, cached = false) ff = Zxy() d = lcm(map(denominator, coefficients(f))) df = f*d @@ -101,7 +101,7 @@ function _galois_init(F::Generic.FunctionField{QQFieldElem}; tStart::Int = -1) for i=0:degree(f) c = coeff(df, i) if !iszero(c) - ff += map_coefficients(FlintZZ, numerator(c))(y)*x^i + ff += map_coefficients(ZZ, numerator(c))(y)*x^i end end _subfields(F, ff, tStart = tStart) diff --git a/src/NumberTheory/NmbThy.jl b/src/NumberTheory/NmbThy.jl index 7f8921b85823..2cffd1ff3d00 100644 --- a/src/NumberTheory/NmbThy.jl +++ b/src/NumberTheory/NmbThy.jl @@ -89,7 +89,7 @@ function norm_equation_fac_elem(R::AbsNumFieldOrder, k::ZZRingElem; abs::Bool = S = Tuple{Vector{Tuple{Hecke.ideal_type(R), Int}}, Vector{ZZMatrix}}[] for (p, k) = lp.fac P = prime_decomposition(R, p) - s = solve_non_negative(matrix(FlintZZ, 1, length(P), [degree(x[1]) for x = P]), matrix(FlintZZ, 1, 1, [k])) + s = solve_non_negative(matrix(ZZ, 1, length(P), [degree(x[1]) for x = P]), matrix(ZZ, 1, 1, [k])) push!(S, (P, ZZMatrix[view(s, i:i, 1:ncols(s)) for i=1:nrows(s)])) end sol = FacElem{AbsSimpleNumFieldElem, AbsSimpleNumField}[] @@ -144,10 +144,10 @@ function norm_equation_fac_elem(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem, q, mms = snf(q) mq = mq*inv(mms) - C = vcat([matrix(FlintZZ, 1, ngens(q), [valuation(mS(preimage(mq, q[i])), p) for i=1:ngens(q)]) for p = keys(lp)]) + C = vcat([matrix(ZZ, 1, ngens(q), [valuation(mS(preimage(mq, q[i])), p) for i=1:ngens(q)]) for p = keys(lp)]) - A = vcat([matrix(FlintZZ, 1, ngens(q), [valuation(norm(mkK, mS(preimage(mq, g))), p) for g in gens(q)]) for p = keys(la)]) - b = matrix(FlintZZ, length(la), 1, [valuation(a, p) for p = keys(la)]) + A = vcat([matrix(ZZ, 1, ngens(q), [valuation(norm(mkK, mS(preimage(mq, g))), p) for g in gens(q)]) for p = keys(la)]) + b = matrix(ZZ, length(la), 1, [valuation(a, p) for p = keys(la)]) so = solve_mixed(A, b, C) u, mu = Hecke.unit_group_fac_elem(parent(a)) @@ -189,16 +189,16 @@ function is_irreducible(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFi # if this is possible, then a is not irreducible as a # is then ms(Ax) * ms(Ay) and neither is trivial. - I = identity_matrix(FlintZZ, length(S)) + I = identity_matrix(ZZ, length(S)) A = hcat(I, I) #so A*(x|y) = x+y = sol is the 1. condition C = cat(V, V, dims=(1,2)) # C(x|y) >=0 iff Cx >=0 and Cy >=0 #Cx <> 0 iff (1,..1)*Cx >= 1 - one = matrix(FlintZZ, 1, length(S), [1 for p = S]) - zer = matrix(FlintZZ, 1, length(S), [0 for p = S]) + one = matrix(ZZ, 1, length(S), [1 for p = S]) + zer = matrix(ZZ, 1, length(S), [0 for p = S]) C = vcat(C, hcat(one, zer), hcat(zer, one)) - d = matrix(FlintZZ, 2*length(S)+2, 1, [0 for i = 1:2*length(S) + 2]) + d = matrix(ZZ, 2*length(S)+2, 1, [0 for i = 1:2*length(S) + 2]) d[end-1, 1] = 1 d[end, 1] = 1 pt = solve_mixed(A, sol, C, d) diff --git a/src/PolyhedralGeometry/solving_integrally.jl b/src/PolyhedralGeometry/solving_integrally.jl index 7818e280a53e..4659cf58a221 100644 --- a/src/PolyhedralGeometry/solving_integrally.jl +++ b/src/PolyhedralGeometry/solving_integrally.jl @@ -49,11 +49,11 @@ Note that the output can be permuted, hence we sort it. ```jldoctest julia> A = ZZMatrix([1 1]); -julia> b = zero_matrix(FlintZZ, 1,1); b[1,1]=7; +julia> b = zero_matrix(ZZ, 1,1); b[1,1]=7; julia> C = ZZMatrix([1 0; 0 1]); -julia> d = zero_matrix(FlintZZ,2,1); d[1,1]=2; d[2,1]=3; +julia> d = zero_matrix(ZZ,2,1); d[1,1]=2; d[2,1]=3; julia> sortslices(Matrix{BigInt}(solve_mixed(A, b, C, d)), dims=1) 3×2 Matrix{BigInt}: @@ -101,7 +101,7 @@ Note that the output can be permuted, hence we sort it. ```jldoctest julia> A = ZZMatrix([1 1]); -julia> b = zero_matrix(FlintZZ, 1,1); b[1,1]=3; +julia> b = zero_matrix(ZZ, 1,1); b[1,1]=3; julia> C = ZZMatrix([1 0; 0 1]); @@ -131,9 +131,9 @@ julia> for x in it """ solve_mixed( as::Type{T}, A::ZZMatrix, b::ZZMatrix, C::ZZMatrix; permit_unbounded=false -) where {T} = solve_mixed(T, A, b, C, zero_matrix(FlintZZ, nrows(C), 1); permit_unbounded) +) where {T} = solve_mixed(T, A, b, C, zero_matrix(ZZ, nrows(C), 1); permit_unbounded) solve_mixed(A::ZZMatrix, b::ZZMatrix, C::ZZMatrix; permit_unbounded=false) = - solve_mixed(ZZMatrix, A, b, C, zero_matrix(FlintZZ, nrows(C), 1); permit_unbounded) + solve_mixed(ZZMatrix, A, b, C, zero_matrix(ZZ, nrows(C), 1); permit_unbounded) @doc raw""" solve_ineq(as::Type{T}, A::ZZMatrix, b::ZZMatrix) where {T} @@ -151,7 +151,7 @@ Note that the output can be permuted, hence we sort it. ```jldoctest julia> A = ZZMatrix([1 0; 0 1; -1 0; 0 -1]); -julia> b = zero_matrix(FlintZZ, 4,1); b[1,1]=1; b[2,1]=1; b[3,1]=0; b[4,1]=0; +julia> b = zero_matrix(ZZ, 4,1); b[1,1]=1; b[2,1]=1; b[3,1]=0; b[4,1]=0; julia> sortslices(Matrix{BigInt}(solve_ineq(A, b)), dims=1) 4×2 Matrix{BigInt}: @@ -173,8 +173,8 @@ SubObjectIterator{PointVector{ZZRingElem}} solve_ineq(as::Type{T}, A::ZZMatrix, b::ZZMatrix; permit_unbounded=false) where {T} = solve_mixed( T, - zero_matrix(FlintZZ, 0, ncols(A)), - zero_matrix(FlintZZ, 0, 1), + zero_matrix(ZZ, 0, ncols(A)), + zero_matrix(ZZ, 0, 1), -A, -b; permit_unbounded, @@ -198,7 +198,7 @@ Note that the output can be permuted, hence we sort it. ```jldoctest julia> A = ZZMatrix([1 1]); -julia> b = zero_matrix(FlintZZ, 1,1); b[1,1]=3; +julia> b = zero_matrix(ZZ, 1,1); b[1,1]=3; julia> sortslices(Matrix{BigInt}(solve_non_negative(A, b)), dims=1) 4×2 Matrix{BigInt}: @@ -219,6 +219,6 @@ SubObjectIterator{PointVector{ZZRingElem}} """ solve_non_negative( as::Type{T}, A::ZZMatrix, b::ZZMatrix; permit_unbounded=false -) where {T} = solve_mixed(T, A, b, identity_matrix(FlintZZ, ncols(A)); permit_unbounded) +) where {T} = solve_mixed(T, A, b, identity_matrix(ZZ, ncols(A)); permit_unbounded) solve_non_negative(A::ZZMatrix, b::ZZMatrix; permit_unbounded=false) = solve_non_negative(ZZMatrix, A, b; permit_unbounded) diff --git a/src/Rings/NumberField.jl b/src/Rings/NumberField.jl index 4f3e651d85ef..85a3e0c8549f 100644 --- a/src/Rings/NumberField.jl +++ b/src/Rings/NumberField.jl @@ -138,7 +138,7 @@ parent(a::NfNSGenElem) = a.parent Hecke.data(a::NfNSGenElem) = a.f -base_field(K::NfNSGen{QQFieldElem, QQMPolyRingElem}) = FlintQQ +base_field(K::NfNSGen{QQFieldElem, QQMPolyRingElem}) = QQ base_field(K::NfNSGen) = base_ring(polynomial_ring(K)) @@ -616,7 +616,7 @@ end function basis_matrix(v::Vector{NfNSGenElem{QQFieldElem, QQMPolyRingElem}}, ::Type{Hecke.FakeFmpqMat}) d = degree(parent(v[1])) - z = zero_matrix(FlintQQ, length(v), d) + z = zero_matrix(QQ, length(v), d) for i in 1:length(v) elem_to_mat_row!(z, i, v[i]) end diff --git a/src/Rings/binomial_ideals.jl b/src/Rings/binomial_ideals.jl index c9dd600ef8eb..1cfa6e0d0bfb 100644 --- a/src/Rings/binomial_ideals.jl +++ b/src/Rings/binomial_ideals.jl @@ -393,7 +393,7 @@ function (Chi::PartialCharacter)(b::ZZMatrix) end function (Chi::PartialCharacter)(b::Vector{ZZRingElem}) - return Chi(matrix(FlintZZ, 1, length(b), b)) + return Chi(matrix(ZZ, 1, length(b), b)) end function have_same_domain(P::PartialCharacter, Q::PartialCharacter) @@ -482,7 +482,7 @@ function ideal_from_character(P::PartialCharacter, R::MPolyRing) @assert ncols(P.A) == nvars(R) #test if the domain of the partial character is the zero lattice - if isone(nrows(P.A)) && have_same_span(P.A, zero_matrix(FlintZZ, 1, ncols(P.A))) + if isone(nrows(P.A)) && have_same_span(P.A, zero_matrix(ZZ, 1, ncols(P.A))) return ideal(R, zero(R)) end @@ -582,7 +582,7 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) Delta = cell[2] #cell variables if isempty(Delta) - return partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAb)], Set{Int64}()) + return partial_character(zero_matrix(ZZ, 1, nvars(R)), [one(QQAb)], Set{Int64}()) end #now consider the case where Delta is not empty @@ -598,12 +598,12 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) end QQAbcl, = abelian_closure(QQ) if iszero(J) - return partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAbcl)], Set{Int64}()) + return partial_character(zero_matrix(ZZ, 1, nvars(R)), [one(QQAbcl)], Set{Int64}()) end #now case if J \neq 0 #let ts be a list of minimal binomial generators for J gb = groebner_basis(J, complete_reduction = true) - vs = zero_matrix(FlintZZ, 0, nvars(R)) + vs = zero_matrix(ZZ, 0, nvars(R)) images = QQAbFieldElem{AbsSimpleNumFieldElem}[] for t in gb #TODO: Once tail will be available, use it. @@ -612,7 +612,7 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) u = exponent_vector(lm, 1) v = exponent_vector(tl, 1) #now test if we need the vector uv - uv = matrix(FlintZZ, 1, nvars(R), Int[u[j]-v[j] for j =1:length(u)]) #this is the vector of u-v + uv = matrix(ZZ, 1, nvars(R), Int[u[j]-v[j] for j =1:length(u)]) #this is the vector of u-v #TODO: It can be done better by saving the hnf... if !can_solve(vs, uv, side = :left)[1] push!(images, -QQAbcl(AbstractAlgebra.leading_coefficient(tl))) @@ -1162,7 +1162,7 @@ function birth_death_ideal(m::Int, n::Int) R = Matrix{QQMPolyRingElem}(undef, m, n+1) D = Matrix{QQMPolyRingElem}(undef, m+1, m) L = Matrix{QQMPolyRingElem}(undef, m+1, n+1) - Qxy, gQxy = polynomial_ring(FlintQQ, length(U)+length(R)+length(D)+length(L); cached = false) + Qxy, gQxy = polynomial_ring(QQ, length(U)+length(R)+length(D)+length(L); cached = false) pols = Vector{elem_type(Qxy)}(undef, 4*n*m) ind = 1 for i = 1:m+1 diff --git a/src/Rings/hilbert.jl b/src/Rings/hilbert.jl index 26acdd148b69..cce1b152d364 100644 --- a/src/Rings/hilbert.jl +++ b/src/Rings/hilbert.jl @@ -934,9 +934,9 @@ end # # Transpose while converting: # ncols = length(W); # nrows = length(W[1]); -# A = zero_matrix(FlintZZ, nrows,ncols); +# A = zero_matrix(ZZ, nrows,ncols); # for i in 1:nrows for j in 1:ncols A[i,j] = W[j][i]; end; end; -# b = zero_matrix(FlintZZ, nrows,1); +# b = zero_matrix(ZZ, nrows,1); # try # solve_non_negative(A, b); # any non-zero soln gives rise to infinitely many, which triggers an exception # catch e diff --git a/src/Rings/mpoly-graded.jl b/src/Rings/mpoly-graded.jl index a6a2ffcd5cad..c6ed7d739a1a 100644 --- a/src/Rings/mpoly-graded.jl +++ b/src/Rings/mpoly-graded.jl @@ -1239,7 +1239,7 @@ function monomial_basis(W::MPolyDecRing, d::FinGenAbGroupElem) k, im = kernel(h) #need the positive elements in there... #Ax = b, Cx >= 0 - C = identity_matrix(FlintZZ, ngens(W)) + C = identity_matrix(ZZ, ngens(W)) A = reduce(vcat, [x.coeff for x = W.d]) k = solve_mixed(transpose(A), transpose(d.coeff), C) for ee = 1:nrows(k) diff --git a/test/AlgebraicGeometry/Surfaces/K3Auto.jl b/test/AlgebraicGeometry/Surfaces/K3Auto.jl index 0e637c52ff02..035c3f2ecee2 100644 --- a/test/AlgebraicGeometry/Surfaces/K3Auto.jl +++ b/test/AlgebraicGeometry/Surfaces/K3Auto.jl @@ -1,6 +1,6 @@ @testset "elliptic fibrations" begin - B = matrix(FlintQQ, 16, 16 ,[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3//2, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 3//2, 3//2, 1//2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1//2, 3//2, 0, 1//2, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1//2, 1//2, 1//2, 0, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 1, 1//2, 0, 1//2, 0, 0, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 0, 1//2, 0, 3//5, 1//10]); - G = matrix(FlintQQ, 16, 16 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -170]); + B = matrix(QQ, 16, 16 ,[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3//2, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 3//2, 3//2, 1//2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1//2, 3//2, 0, 1//2, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1//2, 1//2, 1//2, 0, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 1, 1//2, 0, 1//2, 0, 0, 1//2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//2, 0, 1//2, 0, 3//5, 1//10]); + G = matrix(QQ, 16, 16 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -170]); NS = integer_lattice(B, gram = G); V = ambient_space(NS) f = QQFieldElem[2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] @@ -23,12 +23,12 @@ end @testset "walls of chamber" begin S = integer_lattice(gram=QQ[-2 1 0 0; 1 -2 1 1; 0 1 -2 1; 0 1 1 -2]) # fix an embedding - B = matrix(FlintQQ, 10, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//3, 2//3, 1//3, 2//3, 2//3, 2//3, 1//3, 1//3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - G = matrix(FlintQQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); + B = matrix(QQ, 10, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1//3, 2//3, 1//3, 2//3, 2//3, 2//3, 1//3, 1//3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + G = matrix(QQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); L = integer_lattice(B, gram = G); - B = matrix(FlintQQ, 4, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]); - G = matrix(FlintQQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); + B = matrix(QQ, 4, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]); + G = matrix(QQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); S = integer_lattice(B, gram = G); weyl = QQ[31 61 52 71 5 -6 5 -2 -7 8] @@ -171,11 +171,11 @@ end end @testset "weyl_vector_non_degenerate" begin - B = matrix(FlintQQ, 10, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1//3, 2//3, 1//3, 2//3, 2//3, 2//3, 1//3, 1//3]); - G = matrix(FlintQQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); + B = matrix(QQ, 10, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1//3, 2//3, 1//3, 2//3, 2//3, 2//3, 1//3, 1//3]); + G = matrix(QQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); L = integer_lattice(B, gram = G); - B = matrix(FlintQQ, 4, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]); - G = matrix(FlintQQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); + B = matrix(QQ, 4, 10 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]); + G = matrix(QQ, 10, 10 ,[-2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, -2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 1, -1, -1, -1, 0, 0, 0, 0, -1, -2, 1, -1, 0, -1, 0, 0, 0, 0, 1, 1, -2, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, -2, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, 1, -1, -1, -2]); S = integer_lattice(B, gram = G); u = QQ[90 157 218//3 346//3 -7//3 13//3 16//3 -11//3 -1//3 32//3] weyl = QQ[90 157 218//3 346//3 -7//3 13//3 16//3 -11//3 -1//3 32//3] diff --git a/test/Groups/matrixgroups.jl b/test/Groups/matrixgroups.jl index 5cc046652da0..61ca115ce1c7 100644 --- a/test/Groups/matrixgroups.jl +++ b/test/Groups/matrixgroups.jl @@ -752,7 +752,7 @@ end # L = lattice(q, QQ[0 0; 0 0], isbasis=false) # @test order(isometry_group(L)) == 1 - Qx, x = polynomial_ring(FlintQQ, :x, cached = false) + Qx, x = polynomial_ring(QQ, :x, cached = false) f = x^2-2; K, a = number_field(f) D = matrix(K, 3, 3, [2, 0, 0, 0, 1, 0, 0, 0, 7436]); diff --git a/test/PolyhedralGeometry/solve_integrally.jl b/test/PolyhedralGeometry/solve_integrally.jl index b4acc42344e2..a592f39ae6e4 100644 --- a/test/PolyhedralGeometry/solve_integrally.jl +++ b/test/PolyhedralGeometry/solve_integrally.jl @@ -1,10 +1,10 @@ @testset "solve_integrally" begin @testset "solve_mixed" begin A = ZZMatrix([1 1]) - b = zero_matrix(FlintZZ, 1, 1) + b = zero_matrix(ZZ, 1, 1) b[1, 1] = 7 C = ZZMatrix([1 0; 0 1]) - d = zero_matrix(FlintZZ, 2, 1) + d = zero_matrix(ZZ, 2, 1) d[1, 1] = 2 d[2, 1] = 3 S0 = solve_mixed(A, b, C, d) @@ -30,7 +30,7 @@ @testset "solve_ineq" begin A = ZZMatrix([1 0; 0 1; -1 0; 0 -1]) - b = zero_matrix(FlintZZ, 4, 1) + b = zero_matrix(ZZ, 4, 1) b[1, 1] = 1 b[2, 1] = 1 b[3, 1] = 0 @@ -48,7 +48,7 @@ @testset "solve_non_negative" begin A = ZZMatrix([1 1]) - b = zero_matrix(FlintZZ, 1, 1) + b = zero_matrix(ZZ, 1, 1) b[1, 1] = 3 S0 = solve_non_negative(A, b) S1 = solve_non_negative(ZZMatrix, A, b) diff --git a/test/Rings/NumberField.jl b/test/Rings/NumberField.jl index 6776c019aa79..9beb6682e3c2 100644 --- a/test/Rings/NumberField.jl +++ b/test/Rings/NumberField.jl @@ -1,14 +1,14 @@ @testset "Number field" begin - Qx, x = FlintQQ[:x] + Qx, x = QQ[:x] k, _ = number_field(x^2 + 1) ku, u = k[:u1, :u2] Ik = ideal(ku, [u[1]^3 + u[2]^3 - 3, u[1]^5 + u[2]^5 - 5]) - Qy, y = FlintQQ[:y1, :y2] + Qy, y = QQ[:y1, :y2] IQ = ideal(Qy, [y[1]^3 + y[2]^3 - 3, y[1]^5 + y[2]^5 - 5]) - for (Bk, Pk, I) in [(k, ku, Ik), (FlintQQ, Qy, IQ)] + for (Bk, Pk, I) in [(k, ku, Ik), (QQ, Qy, IQ)] gg = gens(Pk) @test_throws ErrorException number_field(ideal([gg[1]])) K, = @inferred number_field(I, [:a1, :a2]) @@ -180,7 +180,7 @@ end # denominator - if Bk === FlintQQ + if Bk === QQ b = rand(K, -2:2) d = @inferred denominator(b) @test d isa ZZRingElem @@ -203,7 +203,7 @@ end # basis matrix - if Bk == FlintQQ + if Bk == QQ for i in 1:10 BB = [rand(K, -2:2) for j in 1:rand(1:10)] M = @inferred basis_matrix(BB, Hecke.FakeFmpqMat) @@ -226,7 +226,7 @@ @test b * B[n] == sum(M[n, m] * B[m] for m in 1:length(B)) end - if Bk == FlintQQ + if Bk == QQ M, d = @inferred representation_matrix_q(b) @test nrows(M) == degree(K) @test ncols(M) == degree(K) @@ -243,7 +243,7 @@ c = rand(-10:10) end b = b//c - MM = zero_matrix(FlintZZ, nrows(M), ncols(M)) + MM = zero_matrix(ZZ, nrows(M), ncols(M)) dd = ZZRingElem() j = rand(1:nrows(MM)) Oscar.Hecke.elem_to_mat_row!(MM, j, dd, b) @@ -318,7 +318,7 @@ end # simple extension - if Bk == FlintQQ + if Bk == QQ Ks, KstoK = simple_extension(K, simplify = true) else Ks, KstoK = simple_extension(K) @@ -332,7 +332,7 @@ @test KstoK(b + c) == KstoK(b) + KstoK(c) @test KstoK(b * c) == KstoK(b) * KstoK(c) - if Bk == FlintQQ + if Bk == QQ @test KstoK\(KstoK(b)) == b b = rand(K, -10:10) c = rand(K, -10:10) diff --git a/test/Rings/binomial-ideals.jl b/test/Rings/binomial-ideals.jl index 12390df54231..15aeca65fc6f 100644 --- a/test/Rings/binomial-ideals.jl +++ b/test/Rings/binomial-ideals.jl @@ -6,7 +6,7 @@ @test is_binomial(f) J = ideal(R, [x^2-y^3, z^2]) @test is_binomial(J) - Qxy, (x, y, z, t) = polynomial_ring(FlintQQ, 4) + Qxy, (x, y, z, t) = polynomial_ring(QQ, 4) I = ideal(elem_type(Qxy)[x*y, z*t^2-t^3, z^2-y^2]) @test Oscar.is_binomial(I) @test Oscar.is_unital(I) @@ -24,7 +24,7 @@ @test is_cellular(I)[1] I = ideal(R, [x[1]*x[4]^2-x[2]*x[5]^2, x[1]^3*x[3]^3-x[2]^4*x[4]^2, x[2]*x[4]^8-x[3]^3*x[5]^6]) @test !is_cellular(I)[1] - Qxy, (x, y, z, t) = polynomial_ring(FlintQQ, 4) + Qxy, (x, y, z, t) = polynomial_ring(QQ, 4) I = ideal(elem_type(Qxy)[x*y, z*t^2-t^3, z^2-y^2]) @test !Oscar.is_cellular(I)[1] lI = Oscar.cellular_decomposition(I) @@ -44,7 +44,7 @@ end @testset "Binomial primary decomposition" begin - Qxy, (x, y, z, t) = polynomial_ring(FlintQQ, 4) + Qxy, (x, y, z, t) = polynomial_ring(QQ, 4) I = ideal(elem_type(Qxy)[x*y, z*t^2-t^3, z^2-y^2]) lP = Oscar.binomial_primary_decomposition(I) From 24711eeecbe977de2bd7ba4a0bba8d7decc86464 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Wed, 6 Nov 2024 21:42:30 +0100 Subject: [PATCH 47/84] rename `minimal_generating_set` for groups to `minimal_size_generating_set` (#4278) --- docs/src/Groups/basics.md | 1 + src/Groups/GAPGroups.jl | 12 ++++++------ src/deprecations.jl | 4 ++++ src/exports.jl | 3 ++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/src/Groups/basics.md b/docs/src/Groups/basics.md index a8d9ff001ece..120c883a4157 100644 --- a/docs/src/Groups/basics.md +++ b/docs/src/Groups/basics.md @@ -21,6 +21,7 @@ has_gens(::GAPGroup) number_of_generators(G::GAPGroup) gen(::GAPGroup, i::Int) small_generating_set(G::GAPGroup) +minimal_size_generating_set(G::GAPGroup) Base.rand(G::GAPGroup) rand_pseudo(G::GAPGroup) ``` diff --git a/src/Groups/GAPGroups.jl b/src/Groups/GAPGroups.jl index 89321335532e..cdb45b98b49b 100644 --- a/src/Groups/GAPGroups.jl +++ b/src/Groups/GAPGroups.jl @@ -673,25 +673,25 @@ julia> length(small_generating_set(abelian_group(PermGroup, [2,3,4]))) end """ - minimal_generating_set(G::GAPGroup) + minimal_size_generating_set(G::GAPGroup) Return a vector of minimal length of elements in `G` that generate `G`. # Examples ```jldoctest -julia> length(minimal_generating_set(abelian_group(SubPcGroup, [2,3,4]))) +julia> length(minimal_size_generating_set(abelian_group(SubPcGroup, [2,3,4]))) 2 -julia> length(minimal_generating_set(abelian_group(PermGroup, [2,3,4]))) +julia> length(minimal_size_generating_set(abelian_group(PermGroup, [2,3,4]))) 2 -julia> minimal_generating_set(symmetric_group(5)) +julia> minimal_size_generating_set(symmetric_group(5)) 2-element Vector{PermGroupElem}: (1,2,3,4,5) (1,2) ``` """ -@gapattribute function minimal_generating_set(G::GAPGroup) +@gapattribute function minimal_size_generating_set(G::GAPGroup) L = GAP.Globals.MinimalGeneratingSet(GapObj(G))::GapObj res = Vector{elem_type(G)}(undef, length(L)) for i = 1:length(res) @@ -1852,7 +1852,7 @@ end # if is_free(G) || (has_is_finite(G) && is_finite(G) && is_pgroup(G)) # return GAP.Globals.Rank(GapObj(G))::Int # end -# has_is_finite(G) && is_finite(G) && return length(minimal_generating_set(G)) +# has_is_finite(G) && is_finite(G) && return length(minimal_size_generating_set(G)) # error("not yet supported") #end diff --git a/src/deprecations.jl b/src/deprecations.jl index e48a6cc42ecd..64e8188595e0 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -143,3 +143,7 @@ Base.@deprecate_binding in_linear_system is_in_linear_system @deprecate scheme(W::AbsAlgebraicCycle) ambient_scheme(W) @deprecate scheme(W::CartierDivisor) ambient_scheme(W) @deprecate scheme(W::EffectiveCartierDivisor) ambient_scheme(W) + +@deprecate minimal_generating_set(G::GAPGroup) minimal_size_generating_set(G) +@deprecate has_minimal_generating_set(G::GAPGroup) has_minimal_size_generating_set(G) +@deprecate set_minimal_generating_set(G::GAPGroup, v) set_minimal_size_generating_set(G, v) diff --git a/src/exports.jl b/src/exports.jl index 2eec9e786eb1..ff4e43fb6eef 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -1054,7 +1054,8 @@ export min_weights export minimal_betti_table export minimal_block_reps export minimal_faces -export minimal_generating_set, has_minimal_generating_set, set_minimal_generating_set +export minimal_generating_set +export minimal_size_generating_set, has_minimal_size_generating_set, set_minimal_size_generating_set export minimal_nonfaces export minimal_normal_subgroups, has_minimal_normal_subgroups, set_minimal_normal_subgroups export minimal_primes From 0879f92ef7c4d97c910dd47623f05b88e05c2a08 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Thu, 7 Nov 2024 15:14:12 +0100 Subject: [PATCH 48/84] make the `GapObj` in left/right cosets optional (#4274) Compute the GAP coset object only if one asks for it. In particular, do not use it for comparing left cosets with `==`. --- src/Groups/cosets.jl | 47 ++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index 9fc72c240dc3..86d1bcb36592 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -11,20 +11,39 @@ struct GroupCoset{T<: GAPGroup, S <: GAPGroupElem} H::GAPGroup # subgroup (may have a different type) repr::S # element side::Symbol # says if the coset is left or right - X::GapObj # GapObj(H*repr) + X::Ref{GapObj} # GapObj(H*repr) + + function GroupCoset(G::T, H::GAPGroup, representative::S, side::Symbol) where {T<: GAPGroup, S<:GAPGroupElem} + return new{T, S}(G, H, representative, side, Ref{GapObj}()) + end end -GAP.@install GapObj(obj::GroupCoset) = obj.X +GAP.@install function GapObj(obj::GroupCoset) + if !isassigned(obj.X) + g = GapObj(representative(obj)) + if is_right(obj) + obj.X[] = GAPWrap.RightCoset(GapObj(obj.H), g) + else + obj.X[] = GAPWrap.RightCoset(GAPWrap.ConjugateSubgroup(GapObj(obj.H), GAPWrap.Inverse(g)), g) + end + end + return obj.X[] +end Base.hash(x::GroupCoset, h::UInt) = h # FIXME Base.eltype(::Type{GroupCoset{T,S}}) where {T,S} = S -function _group_coset(G::GAPGroup, H::GAPGroup, repr::GAPGroupElem, side::Symbol, X::GapObj) - return GroupCoset{typeof(G), typeof(repr)}(G, H, repr, side, X) -end - -function ==(x::GroupCoset, y::GroupCoset) - return GapObj(x) == GapObj(y) && x.side == y.side +function ==(C1::GroupCoset, C2::GroupCoset) + H = C1.H + right = is_right(C1) + (right == is_right(C2) && C1.G == C2.G && H == C2.H ) || return false + if right + # Hx == Hy if x/y in H + return representative(C1) / representative(C2) in H + else + # xH == yH if x\y in H + return representative(C1) \ representative(C2) in H + end end function Base.show(io::IO, ::MIME"text/plain", x::GroupCoset) @@ -73,8 +92,9 @@ Right coset of Sym(3) ``` """ function right_coset(H::GAPGroup, g::GAPGroupElem) - @req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(H)) "H is not a subgroup of parent(g)" - return _group_coset(parent(g), H, g, :right, GAPWrap.RightCoset(GapObj(H), GapObj(g))) + G = parent(g) + @req GAPWrap.IsSubset(GapObj(G), GapObj(H)) "H is not a subgroup of parent(g)" + return GroupCoset(G, H, g, :right) end """ @@ -84,7 +104,7 @@ end Return the coset `gH`. !!! note Since GAP supports right cosets only, the underlying GAP object of - `left_coset(H,g)` is the right coset `H^(g^-1) * g`. + `left_coset(H,g)`, if assigned, is the right coset `H^(g^-1) * g`. # Examples ```jldoctest @@ -101,8 +121,9 @@ Left coset of Sym(3) ``` """ function left_coset(H::GAPGroup, g::GAPGroupElem) - @req GAPWrap.IsSubset(GapObj(parent(g)), GapObj(H)) "H is not a subgroup of parent(g)" - return _group_coset(parent(g), H, g, :left, GAPWrap.RightCoset(GAPWrap.ConjugateSubgroup(GapObj(H), GAPWrap.Inverse(GapObj(g))), GapObj(g))) + G = parent(g) + @req GAPWrap.IsSubset(GapObj(G), GapObj(H)) "H is not a subgroup of parent(g)" + return GroupCoset(G, H, g, :left) end From 266fc702fd656fb143378b30a1632803686968c8 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Thu, 7 Nov 2024 15:27:23 +0100 Subject: [PATCH 49/84] improve certain `stabilizer` methods (#4281) * improve certain `stabilizer` methods change the code such that the special GAP methods are used for the natural action of permutation groups, and the actions on sets, tuples, and vectors of pos. integers * make Aqua.jl happy --- src/GAP/wrappers.jl | 1 + src/Groups/action.jl | 20 +++++++++++++++++--- src/Groups/gsets.jl | 19 +++++++++++++++++-- test/Groups/action.jl | 8 ++++++++ test/Groups/gsets.jl | 11 +++++++++++ 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index 5cdfbc00cd2e..d3013a214bae 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -346,6 +346,7 @@ GAP.@wrap SizesCentralizers(x::GapObj)::GapObj GAP.@wrap SizesConjugacyClasses(x::GapObj)::GapObj GAP.@wrap Source(x::GapObj)::GapObj GAP.@wrap Sqrt(x::Int64)::GAP.Obj +GAP.@wrap Stabilizer(x::GapObj, y::Any, z::GapObj)::GapObj GAP.@wrap Stabilizer(v::GapObj, w::Any, x::GapObj, y::GapObj, z::GapObj)::GapObj GAP.@wrap StringViewObj(x::Any)::GapObj GAP.@wrap StructureConstantsTable(x::GapObj)::GapObj diff --git a/src/Groups/action.jl b/src/Groups/action.jl index 8c7624163e9d..4499d8ce5dbc 100644 --- a/src/Groups/action.jl +++ b/src/Groups/action.jl @@ -493,11 +493,25 @@ function stabilizer(G::GAPGroup, pnt::Any, actfun::Function) end # natural stabilizers in permutation groups -stabilizer(G::PermGroup, pnt::T) where T <: Oscar.IntegerUnion = stabilizer(G, pnt, ^) +# Construct the arguments on the GAP side such that GAP's method selection +# can choose the special method. +function stabilizer(G::PermGroup, pnt::T) where T <: Oscar.IntegerUnion + return Oscar._as_subgroup(G, GAPWrap.Stabilizer(GapObj(G), + GapObj(pnt), + GAP.Globals.OnPoints)) # Do not use GAPWrap.OnPoints! +end -stabilizer(G::PermGroup, pnt::Vector{T}) where T <: Oscar.IntegerUnion = stabilizer(G, pnt, on_tuples) +function stabilizer(G::PermGroup, pnt::Union{Vector{T}, Tuple{T, Vararg{T}}}) where T <: Oscar.IntegerUnion + return Oscar._as_subgroup(G, GAPWrap.Stabilizer(GapObj(G), + GapObj(pnt, recursive = true), + GAP.Globals.OnTuples)) # Do not use GAPWrap.OnTuples! +end -stabilizer(G::PermGroup, pnt::AbstractSet{T}) where T <: Oscar.IntegerUnion = stabilizer(G, pnt, on_sets) +function stabilizer(G::PermGroup, pnt::AbstractSet{T}) where T <: Oscar.IntegerUnion + return Oscar._as_subgroup(G, GAPWrap.Stabilizer(GapObj(G), + GapObj(pnt, recursive = true), + GAP.Globals.OnSets)) # Do not use GAPWrap.OnSets! +end # natural stabilizers in matrix groups stabilizer(G::MatrixGroup{ET,MT}, pnt::AbstractAlgebra.Generic.FreeModuleElem{ET}) where {ET,MT} = stabilizer(G, pnt, *) diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index a1a5b9277963..a22df1cd98de 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -452,7 +452,7 @@ julia> map(length, orbs) stabilizer(Omega::GSet{T,S}, omega::S = representative(Omega); check::Bool = true) where {T,S} stabilizer(Omega::GSet{T,S}, omega::Set{S}; check::Bool = true) where {T,S} stabilizer(Omega::GSet{T,S}, omega::Vector{S}; check::Bool = true) where {T,S} - stabilizer(Omega::GSet{T,S}, omega::Tuple{Vararg{S}}; check::Bool = true) where {T,S} + stabilizer(Omega::GSet{T,S}, omega::Tuple{S,Vararg{S}}; check::Bool = true) where {T,S} Return the subgroup of `G = acting_group(Omega)` that fixes `omega`, together with the embedding of this subgroup into `G`. @@ -486,6 +486,13 @@ function stabilizer(Omega::GSet{T,S}, omega::S; check::Bool = true) where {T,S} return stabilizer(G, omega, gfun) end +# Construct the arguments on the GAP side such that GAP's method selection +# can choose the special method. +function stabilizer(Omega::GSet{PermGroup,S}, omega::S; check::Bool = true) where S <: Oscar.IntegerUnion + check && @req omega in Omega "omega must be an element of Omega" + return stabilizer(acting_group(Omega), omega) +end + # support `stabilizer` under "derived" actions: # If the given point is a set of the element type of the G-set # then compute the setwise stabilizer. @@ -508,7 +515,7 @@ function stabilizer(Omega::GSet{T,S}, omega::Vector{S}; check::Bool = true) wher return stabilizer(G, omega, derived_fun) end -function stabilizer(Omega::GSet{T,S}, omega::Tuple{Vararg{S}}; check::Bool = true) where {T,S} +function stabilizer(Omega::GSet{T,S}, omega::Tuple{S,Vararg{S}}; check::Bool = true) where {T,S} check && @req all(in(Omega), omega) "omega must be a tuple of elements of Omega" G = acting_group(Omega) gfun = action_function(Omega) @@ -516,6 +523,14 @@ function stabilizer(Omega::GSet{T,S}, omega::Tuple{Vararg{S}}; check::Bool = tru return stabilizer(G, omega, derived_fun) end +# Construct the arguments on the GAP side such that GAP's method selection +# can choose the special method. +function stabilizer(Omega::GSet{PermGroup,S}, omega::Union{Set{S}, Tuple{S,Vararg{S}}, Vector{S}}; check::Bool = true) where S <: Oscar.IntegerUnion + check && @req all(in(Omega), omega) "omega must be a set of elements of Omega" + return stabilizer(acting_group(Omega), omega) +end + + ############################################################################# ## ## `:elements` a vector of points; diff --git a/test/Groups/action.jl b/test/Groups/action.jl index 2dd697fb61f3..30a97cd832b4 100644 --- a/test/Groups/action.jl +++ b/test/Groups/action.jl @@ -29,6 +29,14 @@ @test order(H) == 645120 @test K == stabilizer(H, 1)[1] + # larger examples + G = symmetric_group(100) + S1, _ = stabilizer(G, [1, 2, 3, 4, 5]) + @test order(S1) == factorial(big(95)) + S2, _ = stabilizer(G, (1, 2, 3, 4, 5)) + @test S2 == S1 + S3, _ = stabilizer(G, Set([1, 2, 3, 4, 5])) + @test order(S3) == order(S1) * factorial(5) end @testset "natural stabilizers in matrix groups" begin diff --git a/test/Groups/gsets.jl b/test/Groups/gsets.jl index 046588fb1148..a252160ae5c6 100644 --- a/test/Groups/gsets.jl +++ b/test/Groups/gsets.jl @@ -54,7 +54,18 @@ @test ! is_regular(Omega) @test ! is_semiregular(Omega) + # larger examples + G = symmetric_group(100) + Omega = gset(G) + S1, _ = stabilizer(Omega, [1, 2, 3, 4, 5]) + @test order(S1) == factorial(big(95)) + S2, _ = stabilizer(Omega, (1, 2, 3, 4, 5)) + @test S2 == S1 + S3, _ = stabilizer(Omega, Set([1, 2, 3, 4, 5])) + @test order(S3) == order(S1) * factorial(big(5)) + # constructions by explicit action functions + G = symmetric_group(6) omega = [0,1,0,1,0,1] Omega = gset(G, permuted, [omega, [1,2,3,4,5,6]]) @test isa(Omega, GSet) From f2329a8bd52204e0e072ab0fffb20277074acf48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 8 Nov 2024 12:38:13 +0100 Subject: [PATCH 50/84] Some progress around root systems and Weyl groups (#4271) * Change `WeightLatticeElem` to have coeff vector as row * Add right action of `WeylGroupElem`s on roots and weights * some test simplifications * Determine root system type on demand if skipped in constructor * Add some more examples to docstrings to show changes to printing in the following commits * Adapt some printing * Fix doctests * Remove two constructors --- .../LieAlgebras/src/AbstractLieAlgebra.jl | 48 +++- experimental/LieAlgebras/src/LieAlgebra.jl | 27 ++ .../LieAlgebras/src/LieAlgebraModule.jl | 6 +- .../LieAlgebras/src/LinearLieAlgebra.jl | 153 +++++++++-- experimental/LieAlgebras/src/RootSystem.jl | 251 ++++++++++++++---- experimental/LieAlgebras/src/Types.jl | 8 +- experimental/LieAlgebras/src/WeylGroup.jl | 43 ++- experimental/LieAlgebras/src/serialization.jl | 2 +- .../LieAlgebras/test/RootSystem-test.jl | 75 +----- .../LieAlgebras/test/WeylGroup-test.jl | 38 ++- experimental/LieAlgebras/test/setup_tests.jl | 8 + 11 files changed, 486 insertions(+), 173 deletions(-) diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index 1052aa7c43ff..24a854b438fa 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -28,9 +28,19 @@ function Base.show(io::IO, mime::MIME"text/plain", L::AbstractLieAlgebra) @show_special(io, mime, L) io = pretty(io) println(io, "Abstract Lie algebra") - println(io, Indent(), "of dimension $(dim(L))", Dedent()) - print(io, "over ") - print(io, Lowercase(), coefficient_ring(L)) + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, Indent(), "of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + println(io, Dedent()) + end + end + println(io, Indent(), "of dimension ", dim(L), Dedent()) + print(io, "over ", Lowercase(), coefficient_ring(L)) end function Base.show(io::IO, L::AbstractLieAlgebra) @@ -40,8 +50,18 @@ function Base.show(io::IO, L::AbstractLieAlgebra) print(io, "Abstract Lie algebra") else io = pretty(io) - print(io, "Abstract Lie algebra over ", Lowercase()) - print(terse(io), coefficient_ring(L)) + print(io, "Abstract Lie algebra") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + end + end + print(terse(io), " over ", Lowercase(), coefficient_ring(L)) end end @@ -256,6 +276,15 @@ via the kwarg `extraspecial_pair_signs::Vector{Bool}` to specify the concrete Li If $(\alpha,\beta)$ is the extraspecial pair for the non-simple root `root(rs, i)`, then $\varepsilon_{\alpha,\beta} = 1$ iff `extraspecial_pair_signs[i - n_simple_roots(rs)] = true`. For the used notation and the definition of extraspecial pairs, see [CMT04](@cite). + +# Examples +```jldoctest +julia> L = lie_algebra(QQ, root_system(:B, 4)) +Abstract Lie algebra + of type B4 + of dimension 36 +over rational field +``` """ function lie_algebra( R::Field, @@ -426,6 +455,15 @@ end Construct a simple Lie algebra over the field `R` with Dynkin type given by `fam` and `rk`. See `cartan_matrix(fam::Symbol, rk::Int)` for allowed combinations. The internally used basis of this Lie algebra is the Chevalley basis. + +# Examples +```jldoctest +julia> L = lie_algebra(QQ, :C, 4) +Abstract Lie algebra + of type C4 + of dimension 36 +over rational field +``` """ function lie_algebra(R::Field, S::Symbol, n::Int) rs = root_system(S, n) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 011aadbceb8f..a38a308d2ee2 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -832,6 +832,33 @@ end # ############################################################################### +@doc raw""" + abelian_lie_algebra(R::Field, n::Int) -> LinearLieAlgebra{elem_type(R)} + abelian_lie_algebra(::Type{LinearLieAlgebra}, R::Field, n::Int) -> LinearLieAlgebra{elem_type(R)} + abelian_lie_algebra(::Type{AbstractLieAlgebra}, R::Field, n::Int) -> AbstractLieAlgebra{elem_type(R)} + +Return the abelian Lie algebra of dimension `n` over the field `R`. +The first argument can be optionally provided to specify the type of the returned +Lie algebra. + +# Example +```jldoctest +julia> abelian_lie_algebra(LinearLieAlgebra, QQ, 3) +Linear Lie algebra with 3x3 matrices + of dimension 3 +over rational field + +julia> abelian_lie_algebra(AbstractLieAlgebra, QQ, 3) +Abstract Lie algebra + of dimension 3 +over rational field +``` +""" +function abelian_lie_algebra(R::Field, n::Int) + @req n >= 0 "Dimension must be non-negative." + return abelian_lie_algebra(LinearLieAlgebra, R, n) +end + @doc raw""" lie_algebra(gapL::GapObj, s::Vector{<:VarName}) -> LieAlgebra{elem_type(R)} diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 69a8f19200a0..f50773d2fe67 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -1445,8 +1445,8 @@ julia> dominant_weights(L, [1, 0, 3]) 7-element Vector{Vector{Int64}}: [1, 0, 3] [1, 1, 1] - [2, 0, 1] [0, 0, 3] + [2, 0, 1] [0, 1, 1] [1, 0, 1] [0, 0, 1] @@ -1509,10 +1509,10 @@ Dict{Vector{Int64}, Int64} with 10 entries: [-1, 1, -1] => 1 [-2, 2, 0] => 1 [1, -1, 1] => 1 - [-1, 0, 1] => 1 [1, 0, -1] => 1 - [2, 0, 0] => 1 + [-1, 0, 1] => 1 [0, -1, 0] => 1 + [2, 0, 0] => 1 ``` """ function character(L::LieAlgebra, hw::Vector{<:IntegerUnion}) diff --git a/experimental/LieAlgebras/src/LinearLieAlgebra.jl b/experimental/LieAlgebras/src/LinearLieAlgebra.jl index 19ae7921d7f3..80cb4c0cba33 100644 --- a/experimental/LieAlgebras/src/LinearLieAlgebra.jl +++ b/experimental/LieAlgebras/src/LinearLieAlgebra.jl @@ -44,28 +44,105 @@ function Base.show(io::IO, mime::MIME"text/plain", L::LinearLieAlgebra) @show_name(io, L) @show_special(io, mime, L) io = pretty(io) - println(io, _lie_algebra_type_to_string(get_attribute(L, :type, :unknown), L.n)) - println(io, Indent(), "of dimension $(dim(L))", Dedent()) + type_string = _lie_algebra_type_to_string(get_attribute(L, :type, :unknown), L.n) + if !isnothing(type_string) + println(io, type_string) + else + println(io, "Linear Lie algebra with $(L.n)x$(L.n) matrices") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, Indent(), "of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + println(io, Dedent()) + end + end + end + println(io, Indent(), "of dimension ", dim(L), Dedent()) + print(io, "over ", Lowercase(), coefficient_ring(L)) +end + +function Base.show(io::IO, L::LinearLieAlgebra) + @show_name(io, L) + @show_special(io, L) + if is_terse(io) + type_string_compact = _lie_algebra_type_to_compact_string( + get_attribute(L, :type, :unknown), L.n + ) + if !isnothing(type_string_compact) + print(io, type_string_compact) + else + print(io, "Linear Lie algebra") + end + else + io = pretty(io) + type_string = _lie_algebra_type_to_string(get_attribute(L, :type, :unknown), L.n) + if !isnothing(type_string) + print(io, type_string) + else + print(io, "Linear Lie algebra with $(L.n)x$(L.n) matrices") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + end + end + end + print(terse(io), " over ", Lowercase(), coefficient_ring(L)) + end +end + +#= +function Base.show(io::IO, mime::MIME"text/plain", L::AbstractLieAlgebra) + @show_name(io, L) + @show_special(io, mime, L) + io = pretty(io) + println(io, "Abstract Lie algebra") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, Indent(), "of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + println(io, Dedent()) + end + end + println(io, Indent(), "of dimension ", dim(L), Dedent()) print(io, "over ") print(io, Lowercase(), coefficient_ring(L)) end -function Base.show(io::IO, L::LinearLieAlgebra) +function Base.show(io::IO, L::AbstractLieAlgebra) @show_name(io, L) @show_special(io, L) if is_terse(io) - print(io, _lie_algebra_type_to_compact_string(get_attribute(L, :type, :unknown), L.n)) + print(io, "Abstract Lie algebra") else io = pretty(io) - print( - io, - _lie_algebra_type_to_string(get_attribute(L, :type, :unknown), L.n), - " over ", - Lowercase(), - ) - print(terse(io), coefficient_ring(L)) + print(io, "Abstract Lie algebra") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + end + end + print(terse(io), " over ", Lowercase(), coefficient_ring(L)) end end +=# function _lie_algebra_type_to_string(type::Symbol, n::Int) if type == :general_linear @@ -76,9 +153,8 @@ function _lie_algebra_type_to_string(type::Symbol, n::Int) return "Special orthogonal Lie algebra of degree $n" elseif type == :symplectic return "Symplectic Lie algebra of degree $n" - else - return "Linear Lie algebra with $(n)x$(n) matrices" end + return nothing end function _lie_algebra_type_to_compact_string(type::Symbol, n::Int) @@ -88,9 +164,8 @@ function _lie_algebra_type_to_compact_string(type::Symbol, n::Int) return "sl_$n" elseif type == :special_orthogonal return "so_$n" - else - return "Linear Lie algebra" end + return nothing end function symbols(L::LinearLieAlgebra) @@ -188,6 +263,40 @@ given by `s`. The basis elements must be square matrices of size `n`. We require `basis` to be linearly independent, and to contain the Lie bracket of any two basis elements in its span (this is currently not checked). Setting `check=false` disables these checks (once they are in place). + +# Examples +```jldoctest +julia> e = matrix(QQ, [0 0 1; 0 0 0; 0 0 0]); + +julia> f = matrix(QQ, [0 0 0; 0 0 0; 1 0 0]); + +julia> h = matrix(QQ, [1 0 0; 0 0 0; 0 0 -1]); + +julia> L = lie_algebra(QQ, 3, [e, f, h], [:e, :f, :h]) +Linear Lie algebra with 3x3 matrices + of dimension 3 +over rational field + +julia> root_system(L); + +julia> L +Linear Lie algebra with 3x3 matrices + of type A1 + of dimension 3 +over rational field + +julia> basis(L) +3-element Vector{LinearLieAlgebraElem{QQFieldElem}}: + e + f + h + +julia> matrix_repr_basis(L) +3-element Vector{QQMatrix}: + [0 0 1; 0 0 0; 0 0 0] + [0 0 0; 0 0 0; 1 0 0] + [1 0 0; 0 0 0; 0 0 -1] +``` """ function lie_algebra( R::Field, @@ -215,20 +324,6 @@ function lie_algebra( return lie_algebra(R, L.n, matrix_repr.(basis), s; check) end -@doc raw""" - abelian_lie_algebra(R::Field, n::Int) -> LinearLieAlgebra{elem_type(R)} - abelian_lie_algebra(::Type{LinearLieAlgebra}, R::Field, n::Int) -> LinearLieAlgebra{elem_type(R)} - abelian_lie_algebra(::Type{AbstractLieAlgebra}, R::Field, n::Int) -> AbstractLieAlgebra{elem_type(R)} - -Return the abelian Lie algebra of dimension `n` over the field `R`. -The first argument can be optionally provided to specify the type of the returned -Lie algebra. -""" -function abelian_lie_algebra(R::Field, n::Int) - @req n >= 0 "Dimension must be non-negative." - return abelian_lie_algebra(LinearLieAlgebra, R, n) -end - function abelian_lie_algebra(::Type{T}, R::Field, n::Int) where {T<:LinearLieAlgebra} @req n >= 0 "Dimension must be non-negative." basis = [(b = zero_matrix(R, n, n); b[i, i] = 1; b) for i in 1:n] diff --git a/experimental/LieAlgebras/src/RootSystem.jl b/experimental/LieAlgebras/src/RootSystem.jl index c6e07e744ce6..f67ce0f90eb4 100644 --- a/experimental/LieAlgebras/src/RootSystem.jl +++ b/experimental/LieAlgebras/src/RootSystem.jl @@ -11,6 +11,21 @@ Construct the root system defined by the Cartan matrix. If `check` is `true`, checks that `cartan_matrix` is a generalized Cartan matrix. Passing `detect_type=false` will skip the detection of the root system type. + +# Examples +```jldoctest +julia> root_system([2 -1; -1 2]) +Root system of rank 2 + of type A2 + +julia> root_system(matrix(ZZ, 2, 2, [2, -1, -1, 2]); detect_type=false) +Root system of rank 2 + of unknown type + +julia> root_system(matrix(ZZ, [2 -1 -2; -1 2 0; -1 0 2])) +Root system of rank 3 + of type C3 (with non-canonical ordering of simple roots) +``` """ function root_system(cartan_matrix::ZZMatrix; check::Bool=true, detect_type::Bool=true) return RootSystem(cartan_matrix; check, detect_type) @@ -28,9 +43,8 @@ Construct the root system of the given type. See `cartan_matrix(fam::Symbol, rk: # Examples ```jldoctest julia> root_system(:A, 2) -Root system defined by Cartan matrix - [ 2 -1] - [-1 2] +Root system of rank 2 + of type A2 ``` """ function root_system(fam::Symbol, rk::Int) @@ -40,6 +54,22 @@ function root_system(fam::Symbol, rk::Int) return R end +@doc raw""" + root_system(type::Vector{Tuple{Symbol,Int}}) -> RootSystem + +Construct the root system of the given type. See `cartan_matrix(fam::Symbol, rk::Int)` for allowed combinations of tuples. + +# Examples +```jldoctest +julia> root_system([(:A, 2), (:F, 4)]) +Root system of rank 6 + of type A2 x F4 + +julia> root_system(Tuple{Symbol,Int}[]) +Root system of rank 0 + of type [] +``` +""" function root_system(type::Vector{Tuple{Symbol,Int}}) cartan = cartan_matrix(type) R = root_system(cartan; check=false, detect_type=false) @@ -55,9 +85,18 @@ function Base.show(io::IO, mime::MIME"text/plain", R::RootSystem) @show_name(io, R) @show_special(io, mime, R) io = pretty(io) - println(io, "Root system defined by Cartan matrix") - print(io, Indent()) - show(io, mime, cartan_matrix(R)) + print(io, "Root system") + print(io, " of rank ", rank(R)) + println(io, Indent()) + if has_root_system_type(R) + type, ord = root_system_type_with_ordering(R) + print(io, "of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (with non-canonical ordering of simple roots)") + end + else + print(io, "of unknown type") + end print(io, Dedent()) end @@ -67,10 +106,27 @@ function Base.show(io::IO, R::RootSystem) if is_terse(io) print(io, "Root system") else - print(io, "Root system defined by Cartan matrix $(cartan_matrix(R))") + print(io, "Root system") + if has_root_system_type(R) && + ((type, ord) = root_system_type_with_ordering(R); !isempty(type)) + type, ord = root_system_type_with_ordering(R) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + else + print(io, " of rank ", rank(R)) + end end end +function _root_system_type_string(type::Vector{Tuple{Symbol,Int}}) + isempty(type) && return "[]" + return join( + [string(t[1]) * string(t[2]) for t in type], is_unicode_allowed() ? " × " : " x " + ) +end + @attr ZZMatrix function bilinear_form(R::RootSystem) return cartan_bilinear_form(cartan_matrix(R); check=false) end @@ -148,13 +204,21 @@ end function fundamental_weight(R::RootSystem, i::Int) @req 1 <= i <= rank(R) "invalid index" - return WeightLatticeElem(R, matrix(ZZ, rank(R), 1, i .== 1:rank(R))) + return WeightLatticeElem(R, matrix(ZZ, 1, rank(R), i .== 1:rank(R))) end @doc raw""" fundamental_weights(R::RootSystem) -> Vector{WeightLatticeElem} Return the fundamental weights corresponding to the `simple_roots` of `R`. + +# Examples +```jldoctest +julia> fundamental_weights(root_system(:A, 2)) +2-element Vector{WeightLatticeElem}: + w_1 + w_2 +``` """ function fundamental_weights(R::RootSystem) return [fundamental_weight(R, i) for i in 1:rank(R)] @@ -203,6 +267,15 @@ Also see: `negative_root`. This function does not return a copy of the asked for object, but the internal field of the root system. Mutating the returned object will lead to undefined behavior. + +# Examples +```jldoctest +julia> negative_roots(root_system(:A, 2)) +3-element Vector{RootSpaceElem}: + -a_1 + -a_2 + -a_1 - a_2 +``` """ function negative_roots(R::RootSystem) return [-r for r in positive_roots(R)] @@ -228,14 +301,23 @@ end @doc raw""" negative_coroot(R::RootSystem, i::Int) -> RootSpaceElem -Returns the coroots corresponding to the negative roots of `R` +Returns the negative coroots of `R`. The $i$-th element of the returned vector is the negative coroot corresponding to the $i$-th positive coroot. -Also see: `negative_coroots`. +Also see: `negative_coroot`. !!! note This function does not return a copy of the asked for object, but the internal field of the root system. Mutating the returned object will lead to undefined behavior. + +# Examples +```jldoctest +julia> negative_coroots(root_system(:A, 2)) +3-element Vector{DualRootSpaceElem}: + -a^v_1 + -a^v_2 + -a^v_1 - a^v_2 +``` """ function negative_coroots(R::RootSystem) return [-r for r in positive_coroots(R)] @@ -303,6 +385,15 @@ Also see: `positive_root`, `number_of_positive_roots`. This function does not return a copy of the asked for object, but the internal field of the root system. Mutating the returned object will lead to undefined behavior. + +# Examples +```jldoctest +julia> positive_roots(root_system(:A, 2)) +3-element Vector{RootSpaceElem}: + a_1 + a_2 + a_1 + a_2 +``` """ function positive_roots(R::RootSystem) return R.positive_roots::Vector{RootSpaceElem} @@ -336,6 +427,15 @@ Also see: `positive_coroots`. This function does not return a copy of the asked for object, but the internal field of the root system. Mutating the returned object will lead to undefined behavior. + +# Examples +```jldoctest +julia> positive_coroots(root_system(:A, 2)) +3-element Vector{DualRootSpaceElem}: + a^v_1 + a^v_2 + a^v_1 + a^v_2 +``` """ function positive_coroots(R::RootSystem) return R.positive_coroots::Vector{DualRootSpaceElem} @@ -351,11 +451,12 @@ function rank(R::RootSystem) end function root_system_type(R::RootSystem) - has_root_system_type(R) || error("Root system type not known and cannot be determined") + assure_root_system_type(R) return R.type end function root_system_type_with_ordering(R::RootSystem) + assure_root_system_type(R) return R.type, R.type_ordering end @@ -363,6 +464,12 @@ function has_root_system_type(R::RootSystem) return isdefined(R, :type) && isdefined(R, :type_ordering) end +function assure_root_system_type(R::RootSystem) + has_root_system_type(R) && return nothing + @req is_finite(weyl_group(R)) "Root system type cannot be determined for infinite Weyl groups" + set_root_system_type!(R, cartan_type_with_ordering(cartan_matrix(R))...) +end + function set_root_system_type!(R::RootSystem, type::Vector{Tuple{Symbol,Int}}) return set_root_system_type!(R, type, 1:sum(t[2] for t in type; init=0)) end @@ -375,10 +482,6 @@ function set_root_system_type!( return nothing end -function root_system_type_string(R::RootSystem) - return join([string(t[1]) * string(t[2]) for t in root_system_type(R)], " x ") -end - @doc raw""" root(R::RootSystem, i::Int) -> RootSpaceElem @@ -489,6 +592,24 @@ end weyl_group(R::RootSystem) -> WeylGroup Return the Weyl group of `R`. + +# Examples +```jldoctest +julia> weyl_group(root_system([2 -1; -1 2])) +Weyl group + of root system of rank 2 + of type A2 + +julia> weyl_group(root_system(matrix(ZZ, 2, 2, [2, -1, -1, 2]); detect_type=false)) +Weyl group + of root system of rank 2 + of unknown type + +julia> weyl_group(root_system(matrix(ZZ, [2 -1 -2; -1 2 0; -1 0 2]))) +Weyl group + of root system of rank 3 + of type C3 (with non-canonical ordering of simple roots) +``` """ function weyl_group(R::RootSystem) return R.weyl_group::WeylGroup @@ -501,7 +622,7 @@ Return the Weyl vector $\rho$ of `R`, which is the sum of all fundamental weight or half the sum of all positive roots. """ function weyl_vector(R::RootSystem) - return WeightLatticeElem(R, matrix(ZZ, rank(R), 1, fill(1, rank(R)))) + return WeightLatticeElem(R, matrix(ZZ, 1, rank(R), fill(1, rank(R)))) end ############################################################################### @@ -514,14 +635,10 @@ function RootSpaceElem(root_system::RootSystem, vec::Vector{<:RationalUnion}) return RootSpaceElem(root_system, matrix(QQ, 1, length(vec), vec)) end -function RootSpaceElem(R::RootSystem, w::WeightLatticeElem) - @req root_system(w) === R "Root system mismatch" - coeffs = transpose!(cartan_matrix_inv(R) * coefficients(w)) - return RootSpaceElem(R, matrix(QQ, coeffs)) -end - function RootSpaceElem(w::WeightLatticeElem) - return RootSpaceElem(root_system(w), w) + R = root_system(w) + coeffs = coefficients(w) * cartan_matrix_inv_tr(R) + return RootSpaceElem(R, matrix(QQ, coeffs)) end function zero(::Type{RootSpaceElem}, R::RootSystem) @@ -622,6 +739,23 @@ function dot(r1::RootSpaceElem, r2::RootSpaceElem) ) end +function expressify(r::RootSpaceElem; context=nothing) + if is_unicode_allowed() + return expressify(r, :α; context) + else + return expressify(r, :a; context) + end +end + +function expressify(r::RootSpaceElem, s; context=nothing) + sum = Expr(:call, :+) + for i in 1:length(r.vec) + push!(sum.args, Expr(:call, :*, expressify(r.vec[i]; context), "$(s)_$(i)")) + end + return sum +end +@enable_all_show_via_expressify RootSpaceElem + @doc raw""" height(r::RootSpaceElem) -> QQFieldElem @@ -696,9 +830,9 @@ function reflect(r::RootSpaceElem, s::Int) end function reflect!(r::RootSpaceElem, s::Int) - r.vec -= - dot(view(cartan_matrix(root_system(r)), s, :), r.vec) * - simple_root(root_system(r), s).vec + sub!( + Nemo.mat_entry_ptr(r.vec, 1, s), dot(view(cartan_matrix(root_system(r)), s, :), r.vec) + ) return r end @@ -805,6 +939,23 @@ function coeff(r::DualRootSpaceElem, i::Int) return r.vec[i] end +function expressify(r::DualRootSpaceElem; context=nothing) + if is_unicode_allowed() + return expressify(r, :α̌; context) + else + return expressify(r, Symbol("a^v"); context) + end +end + +function expressify(r::DualRootSpaceElem, s; context=nothing) + sum = Expr(:call, :+) + for i in 1:length(r.vec) + push!(sum.args, Expr(:call, :*, expressify(r.vec[i]; context), "$(s)_$(i)")) + end + return sum +end +@enable_all_show_via_expressify DualRootSpaceElem + @doc raw""" height(r::DualRootSpaceElem) -> QQFieldElem @@ -890,22 +1041,18 @@ end Return the weight defined by the coefficients `v` of the fundamental weights with respect to the root system `R`. """ function WeightLatticeElem(R::RootSystem, v::Vector{<:IntegerUnion}) - return WeightLatticeElem(R, matrix(ZZ, rank(R), 1, v)) + return WeightLatticeElem(R, matrix(ZZ, 1, rank(R), v)) end -function WeightLatticeElem(R::RootSystem, r::RootSpaceElem) - @req root_system(r) === R "Root system mismatch" - coeffs = transpose!(coefficients(r) * cartan_matrix_tr(R)) +function WeightLatticeElem(r::RootSpaceElem) + R = root_system(r) + coeffs = coefficients(r) * cartan_matrix_tr(R) @req all(is_integer, coeffs) "RootSpaceElem does not correspond to a weight" return WeightLatticeElem(R, matrix(ZZ, coeffs)) end -function WeightLatticeElem(r::RootSpaceElem) - return WeightLatticeElem(root_system(r), r) -end - function zero(::Type{WeightLatticeElem}, R::RootSystem) - return WeightLatticeElem(R, zero_matrix(ZZ, rank(R), 1)) + return WeightLatticeElem(R, zero_matrix(ZZ, 1, rank(R))) end function zero(r::WeightLatticeElem) @@ -1065,14 +1212,22 @@ function dot(w1::WeightLatticeElem, w2::WeightLatticeElem) return dot( coefficients(w1), - cartan_matrix_inv_tr(R) * (_cartan_symmetrizer_mat(R) * coefficients(w2)), + (coefficients(w2) * _cartan_symmetrizer_mat(R)) * cartan_matrix_inv(R), ) end -function expressify(w::WeightLatticeElem, s=:w; context=nothing) +function expressify(w::WeightLatticeElem; context=nothing) + if is_unicode_allowed() + return expressify(w, :ω; context) + else + return expressify(w, :w; context) + end +end + +function expressify(w::WeightLatticeElem, s; context=nothing) sum = Expr(:call, :+) for i in 1:length(w.vec) - push!(sum.args, Expr(:call, :*, expressify(w.vec[i]; context), "$s$i")) + push!(sum.args, Expr(:call, :*, expressify(w.vec[i]; context), "$(s)_$(i)")) end return sum end @@ -1097,7 +1252,7 @@ end Reflects the `w` at the `s`-th simple root in place and returns `w`. """ function reflect!(w::WeightLatticeElem, s::Int) - addmul!(w.vec, view(cartan_matrix(root_system(w)), :, s:s), -w.vec[s]) + w.vec = addmul!(w.vec, view(cartan_matrix_tr(root_system(w)), s:s, :), -w.vec[s]) # change to submul! once available return w end @@ -1112,7 +1267,7 @@ function dot(r::RootSpaceElem, w::WeightLatticeElem) @req root_system(r) === root_system(w) "parent root system mismatch" R = root_system(r) - return dot(coefficients(r), _cartan_symmetrizer_mat(R), coefficients(w)) + return dot(coefficients(r) * _cartan_symmetrizer_mat(R), coefficients(w)) end function dot(w::WeightLatticeElem, r::RootSpaceElem) @@ -1193,8 +1348,8 @@ julia> dominant_weights(Vector{Int}, R, [3, 0, 1]) 7-element Vector{Vector{Int64}}: [3, 0, 1] [1, 1, 1] - [2, 0, 1] [0, 0, 3] + [2, 0, 1] [0, 1, 1] [1, 0, 1] [0, 0, 1] @@ -1248,10 +1403,8 @@ function _action_matrices_on_weights(W::WeylGroup) R = root_system(W) return map(1:rank(R)) do i x = gen(W, i) - transpose!( - matrix( - ZZ, reduce(hcat, coefficients(x * fundamental_weight(R, j)) for j in 1:rank(R)) - ), + matrix( + ZZ, reduce(vcat, coefficients(x * fundamental_weight(R, j)) for j in 1:rank(R)) ) end end @@ -1290,7 +1443,7 @@ function dominant_character(R::RootSystem, hw::WeightLatticeElem) pos_roots = positive_roots(R) pos_roots_w = WeightLatticeElem.(positive_roots(R)) - pos_roots_w_coeffs = transpose.(coefficients.(pos_roots_w)) + pos_roots_w_coeffs = coefficients.(pos_roots_w) char = Dict(hw => T(1)) @@ -1309,7 +1462,7 @@ function dominant_character(R::RootSystem, hw::WeightLatticeElem) ( WeightLatticeElem( R, - transpose(first(intersect(elements(o), pos_roots_w_coeffs))), + first(intersect(elements(o), pos_roots_w_coeffs)), ), length(o), ) for o in O @@ -1366,9 +1519,9 @@ Dict{Vector{Int64}, Int64} with 8 entries: [0, 0, 1] => 1 [1, -1, 1] => 1 [-1, 0, 1] => 1 - [0, -1, 1] => 1 - [0, 0, -1] => 1 [1, 0, -1] => 1 + [0, 0, -1] => 1 + [0, -1, 1] => 1 ``` """ function character(R::RootSystem, hw::WeightLatticeElem) diff --git a/experimental/LieAlgebras/src/Types.jl b/experimental/LieAlgebras/src/Types.jl index 7f7546c46bab..0b5f7f8acf7a 100644 --- a/experimental/LieAlgebras/src/Types.jl +++ b/experimental/LieAlgebras/src/Types.jl @@ -35,9 +35,7 @@ ) R.weyl_group = WeylGroup(finite, refl, R) - detect_type && - is_finite(weyl_group(R)) && - set_root_system_type!(R, cartan_type_with_ordering(mat)...) + detect_type && is_finite(weyl_group(R)) && assure_root_system_type(R) return R end end @@ -64,10 +62,10 @@ end mutable struct WeightLatticeElem root_system::RootSystem - vec::ZZMatrix # the coordinate (column) vector with respect to the fundamental weights + vec::ZZMatrix # the coordinate (row) vector with respect to the fundamental weights function WeightLatticeElem(root_system::RootSystem, vec::ZZMatrix) - @req size(vec) == (rank(root_system), 1) "Invalid dimension" + @req size(vec) == (1, rank(root_system)) "Invalid dimension" return new(root_system, vec) end end diff --git a/experimental/LieAlgebras/src/WeylGroup.jl b/experimental/LieAlgebras/src/WeylGroup.jl index e4d0fd98e021..fff0ca142754 100644 --- a/experimental/LieAlgebras/src/WeylGroup.jl +++ b/experimental/LieAlgebras/src/WeylGroup.jl @@ -24,7 +24,9 @@ Returns the Weyl group of the given type. See `cartan_matrix(fam::Symbol, rk::In # Examples ```jldoctest julia> weyl_group(:A, 2) -Weyl group for root system defined by Cartan matrix [2 -1; -1 2] +Weyl group + of root system of rank 2 + of type A2 ``` """ function weyl_group(fam::Symbol, rk::Int) @@ -92,10 +94,25 @@ function Base.one(W::WeylGroup) return W(UInt8[]; normalize=false) end +function Base.show(io::IO, mime::MIME"text/plain", W::WeylGroup) + @show_name(io, W) + @show_special(io, mime, W) + io = pretty(io) + println(io, LowercaseOff(), "Weyl group") + print(io, Indent(), "of ", Lowercase()) + show(io, mime, root_system(W)) + print(io, Dedent()) +end + function Base.show(io::IO, W::WeylGroup) @show_name(io, W) @show_special(io, W) - print(pretty(io), LowercaseOff(), "Weyl group for ", Lowercase(), W.root_system) + io = pretty(io) + if is_terse(io) + print(io, LowercaseOff(), "Weyl group") + else + print(io, LowercaseOff(), "Weyl group of ", Lowercase(), root_system(W)) + end end function coxeter_matrix(W::WeylGroup) @@ -204,26 +221,26 @@ function Base.:(*)(x::WeylGroupElem, y::WeylGroupElem) return p end -function Base.:(*)(x::WeylGroupElem, r::RootSpaceElem) - @req root_system(parent(x)) === root_system(r) "Incompatible root systems" +function Base.:(*)(x::WeylGroupElem, rw::Union{RootSpaceElem,WeightLatticeElem}) + @req root_system(parent(x)) === root_system(rw) "Incompatible root systems" - r2 = deepcopy(r) + rw2 = deepcopy(rw) for s in Iterators.reverse(word(x)) - reflect!(r2, Int(s)) + reflect!(rw2, Int(s)) end - return r2 + return rw2 end -function Base.:(*)(x::WeylGroupElem, w::WeightLatticeElem) - @req root_system(parent(x)) === root_system(w) "Incompatible root systems" +function Base.:(*)(rw::Union{RootSpaceElem,WeightLatticeElem}, x::WeylGroupElem) + @req root_system(parent(x)) === root_system(rw) "Incompatible root systems" - w2 = deepcopy(w) - for s in Iterators.reverse(word(x)) - reflect!(w2, Int(s)) + rw2 = deepcopy(rw) + for s in word(x) + reflect!(rw2, Int(s)) end - return w2 + return rw2 end # to be removed once GroupCore is supported diff --git a/experimental/LieAlgebras/src/serialization.jl b/experimental/LieAlgebras/src/serialization.jl index 52b76c7dc997..e3f8f8cea085 100644 --- a/experimental/LieAlgebras/src/serialization.jl +++ b/experimental/LieAlgebras/src/serialization.jl @@ -255,7 +255,7 @@ function save_object(s::SerializerState, R::RootSystem) if has_root_system_type(R) type, type_ordering = root_system_type_with_ordering(R) save_object(s, type, :type) - if type_ordering != 1:length(type_ordering) # don't save if it's the default + if !issorted(type_ordering) # don't save if it's the default save_object(s, type_ordering, :type_ordering) end end diff --git a/experimental/LieAlgebras/test/RootSystem-test.jl b/experimental/LieAlgebras/test/RootSystem-test.jl index 71cd8001372f..58263e4c2c12 100644 --- a/experimental/LieAlgebras/test/RootSystem-test.jl +++ b/experimental/LieAlgebras/test/RootSystem-test.jl @@ -30,6 +30,10 @@ end @testset "property tests" begin + Main.equality(a::RootSpaceElem, b::RootSpaceElem) = a == b + Main.equality(a::DualRootSpaceElem, b::DualRootSpaceElem) = a == b + Main.equality(a::WeightLatticeElem, b::WeightLatticeElem) = a == b + function root_system_property_tests(R::RootSystem, rk::Int, npositive_roots::Int) W = weyl_group(R) @@ -136,72 +140,17 @@ RootSpaceElem, DualRootSpaceElem, WeightLatticeElem ) rk = rank(R) + for _ in 1:10 + a = T(R, rand(-10:10, rk)) + b = T(R, rand(-10:10, rk)) + c = T(R, rand(-10:10, rk)) - x1 = T(R, rand(-10:10, rk)) - x2 = T(R, rand(-10:10, rk)) - x3 = T(R, rand(-10:10, rk)) - x1c = deepcopy(x1) - x2c = deepcopy(x2) - x3c = deepcopy(x3) - - for (f, f!) in ((zero, zero!),) - x1 = f!(x1) - @test x1 == f(x1c) - x1 = deepcopy(x1c) - end - - for (f, f!) in ((-, neg!),) - x1 = f!(x1, x2) - @test x1 == f(x2c) - @test x2 == x2c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) + test_mutating_op_like_zero(zero, zero!, a) - x1 = f!(x1) - @test x1 == f(x1c) - x1 = deepcopy(x1c) - end + test_mutating_op_like_neg(-, neg!, a) - for (f, f!) in ((+, add!), (-, sub!)) - x1 = f!(x1, x2, x3) - @test x1 == f(x2c, x3c) - @test x2 == x2c - @test x3 == x3c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) - x3 = deepcopy(x3c) - - x1 = f!(x1, x1, x2) - @test x1 == f(x1c, x2c) - @test x2 == x2c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) - - x1 = f!(x1, x2, x1) - @test x1 == f(x2c, x1c) - @test x2 == x2c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) - - x1 = f!(x1, x2, x2) - @test x1 == f(x2c, x2c) - @test x2 == x2c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) - - x1 = f!(x1, x1, x1) - @test x1 == f(x1c, x1c) - x1 = deepcopy(x1c) - - x1 = f!(x1, x2) - @test x1 == f(x1c, x2c) - @test x2 == x2c - x1 = deepcopy(x1c) - x2 = deepcopy(x2c) - - x1 = f!(x1, x1) - @test x1 == f(x1c, x1c) - x1 = deepcopy(x1c) + test_mutating_op_like_add(+, add!, a, b) + test_mutating_op_like_add(-, sub!, a, b) end end diff --git a/experimental/LieAlgebras/test/WeylGroup-test.jl b/experimental/LieAlgebras/test/WeylGroup-test.jl index e78ad3d73ad5..18c39e2766d2 100644 --- a/experimental/LieAlgebras/test/WeylGroup-test.jl +++ b/experimental/LieAlgebras/test/WeylGroup-test.jl @@ -288,9 +288,9 @@ include( @test ngens(weyl_group(:F, 4)) == 4 @test ngens(weyl_group(:G, 2)) == 2 - @test ngens(weyl_group((:A, 2), (:B, 4))) == 6 - @test ngens(weyl_group((:C, 3), (:E, 7))) == 10 - @test ngens(weyl_group((:F, 4), (:G, 2))) == 6 + @test ngens(weyl_group((:A, 2), (:B, 4))) == 2 + 4 + @test ngens(weyl_group((:C, 3), (:E, 7))) == 3 + 7 + @test ngens(weyl_group((:F, 4), (:G, 2))) == 4 + 2 end @testset "Base.:(*)(x::WeylGroupElem, y::WeylGroupElem)" begin @@ -347,20 +347,30 @@ include( @test w^-4 == inv(w) * inv(w) * inv(w) * inv(w) end - @testset "Base.:(*)(x::WeylGroupElem, w::RootSpaceElem)" begin + @testset "action on RootSpaceElem" begin let R = root_system(:A, 2) W = weyl_group(R) a = positive_root(R, n_positive_roots(R)) # highest root @test one(W) * a == a + @test a * one(W) == a @test W([1]) * a == simple_root(R, 2) + @test a * W([1]) == simple_root(R, 2) @test W([2]) * a == simple_root(R, 1) + @test a * W([2]) == simple_root(R, 1) @test longest_element(W) * a == -a + @test a * longest_element(W) == -a + @test W([1, 2]) * a == -simple_root(R, 1) + @test a * W([1, 2]) == -simple_root(R, 2) + @test W([1, 2]) * a != a * W([1, 2]) a_copy = deepcopy(a) b = W([1]) * a @test a != b @test a == a_copy + b = a * W([1]) + @test a != b + @test a == a_copy b = reflect(a, 1) @test a != b @test a == a_copy @@ -371,24 +381,42 @@ include( a = positive_root(R, n_positive_roots(R)) # highest (long) root @test one(W) * a == a + @test a * one(W) == a @test W([1]) * a == a + @test a * W([1]) == a @test W([2]) * a == simple_root(R, 1) + @test a * W([2]) == simple_root(R, 1) @test longest_element(W) * a == -a + @test a * longest_element(W) == -a + @test W([1, 2]) * a == -simple_root(R, 1) + @test a * W([1, 2]) == simple_root(R, 1) + @test W([1, 2]) * a != a * W([1, 2]) a = simple_root(R, 1) @test one(W) * a == a + @test a * one(W) == a @test W([1]) * a == -a + @test a * W([1]) == -a @test W([2]) * a == positive_root(R, n_positive_roots(R)) + @test a * W([2]) == positive_root(R, n_positive_roots(R)) @test longest_element(W) * a == -a + @test a * longest_element(W) == -a + @test W([1, 2]) * a == positive_root(R, n_positive_roots(R)) + @test a * W([1, 2]) == -positive_root(R, n_positive_roots(R)) + @test W([1, 2]) * a != a * W([1, 2]) end end - @testset "Base.:(*)(x::WeylGroupElem, w::WeightLatticeElem)" begin + @testset "action on WeightLatticeElem" begin R = root_system(:A, 2) W = weyl_group(R) rho = weyl_vector(R) @test longest_element(W) * rho == -rho + @test rho * longest_element(W) == -rho + + x = W([1, 2]) + @test x * rho != rho * x end @testset "parent(::WeylGroupElem)" begin diff --git a/experimental/LieAlgebras/test/setup_tests.jl b/experimental/LieAlgebras/test/setup_tests.jl index 80dc1590266c..8c13d241e7cc 100644 --- a/experimental/LieAlgebras/test/setup_tests.jl +++ b/experimental/LieAlgebras/test/setup_tests.jl @@ -4,6 +4,14 @@ if !isdefined(Main, :GAPWrap) import Oscar: GAPWrap end +if !isdefined(Main, :test_mutating_op_like_zero) + include( + joinpath( + pathof(Oscar.Nemo.AbstractAlgebra), "..", "..", "test", "Rings-conformance-tests.jl" + ), + ) +end + if !isdefined(Main, :lie_algebra_conformance_test) || isinteractive() function lie_algebra_conformance_test( L::LieAlgebra{C}, parentT::DataType, elemT::DataType; num_random_tests::Int=10 From f3db8fe8d9b39cb8bf0914fc702b5e6c8dcfe45f Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Fri, 8 Nov 2024 16:42:58 +0100 Subject: [PATCH 51/84] remove an `intersect` method (#4285) The method is applicable to a vector whose type can be constructed only by explicitly prescribing it. Having an `intersect` method for this case looks strange. (And logically, `intersect` for one vector contradicts Julia's default behaviour, which just returns this vector.) --- src/Groups/cosets.jl | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index 86d1bcb36592..61cfc96b8477 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -673,26 +673,3 @@ function Base.iterate(G::GroupDoubleCoset, state) i = GAPWrap.NextIterator(state)::GapObj return group_element(G.G, i), state end - -""" - intersect(V::AbstractVector{Union{<: GAPGroup, GroupCoset, GroupDoubleCoset}}) - -Return a vector containing all elements belonging to all groups and cosets -in `V`. -""" -function Base.intersect(V::AbstractVector{Union{<: GAPGroup, GroupCoset, GroupDoubleCoset}}) - if V[1] isa GAPGroup - G = V[1] - else - G = V[1].G - end - l = GAP.Obj(V; recursive = true) - ints = GAPWrap.Intersection(l) - L = Vector{eltype(G)}(undef, length(ints)) - for i in 1:length(ints) - L[i] = group_element(G,ints[i]) - end - - return L -end -#TODO: Can this method get called at all? From 194d82b312439e52c49b8b957ad68cb333bd39b7 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 8 Nov 2024 16:54:42 +0100 Subject: [PATCH 52/84] Tweak Betti table printing (#4256) This makes it look a bit nicer (which is subjective) but also ensure that the Betti tables in jldoctests are formatted such that "doctest=:fix" works correctly (it didn't before because the first printed line had too many leading spaces, confusing Documenter's code for detecting were an input line ends). --- src/Modules/ModulesGraded.jl | 132 ++++++++++-------- .../UngradedModules/FreeResolutions.jl | 48 +++---- src/Rings/mpoly-graded.jl | 10 +- test/Modules/ModulesGraded.jl | 125 ++++++++++++----- .../alexander-surface.jlcon | 20 +-- .../algebraic-geometry/canonicalimage.jlcon | 32 ++--- .../algebraic-geometry/char3-surface-1.jlcon | 14 +- .../algebraic-geometry/char3-surface-2.jlcon | 36 ++--- .../algebraic-geometry/exres2.jlcon | 28 ++-- .../algebraic-geometry/param.jlcon | 10 +- 10 files changed, 261 insertions(+), 194 deletions(-) diff --git a/src/Modules/ModulesGraded.jl b/src/Modules/ModulesGraded.jl index 2aebb616c870..d7144b1ef6d3 100644 --- a/src/Modules/ModulesGraded.jl +++ b/src/Modules/ModulesGraded.jl @@ -1426,15 +1426,18 @@ of `F` in form of a Betti table. Alternatively, use `betti`. # Examples -```julia +```jldoctest julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, [:w, :x, :y, :z]); julia> I = ideal(R, [x*z, y*z, x*w^2, y*w^2]) -ideal(x*z, y*z, w^2*x, w^2*y) +Ideal generated by + x*z + y*z + w^2*x + w^2*y julia> A, _= quo(R, I) -(Quotient of multivariate polynomial ring by ideal with 4 generators, Map from -R to A defined by a julia-function with inverse) +(Quotient of multivariate polynomial ring by ideal (x*z, y*z, w^2*x, w^2*y), Map: R -> A) julia> FA = free_resolution(A) Free resolution of A @@ -1442,13 +1445,13 @@ R^1 <---- R^4 <---- R^4 <---- R^1 <---- 0 0 1 2 3 4 julia> betti_table(FA) - 0 1 2 3 +degree: 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 2 1 - -2 : - 2 3 1 + 0: 1 - - - + 1: - 2 1 - + 2: - 2 3 1 ------------------ -total: 1 4 4 1 + total: 1 4 4 1 julia> R, (x, y) = graded_polynomial_ring(QQ, [:x, :y]); @@ -1459,12 +1462,11 @@ julia> M = quotient_ring_as_module(I); julia> FM = free_resolution(M, algorithm = :nres); julia> betti_table(FM) - 0 1 2 +degree: 0 1 2 --------------- --1 : - - 1 -0 : 1 3 1 + 0: 1 2 1 --------------- -total: 1 3 2 + total: 1 2 1 ``` """ function betti_table(F::FreeResolution; project::Union{FinGenAbGroupElem, Nothing} = nothing, reverse_direction::Bool=false) @@ -1531,27 +1533,33 @@ function Base.show(io::IO, b::BettiTable) if b.project === nothing for i in 1:ngens(parent(x[1][2])) ngens(parent(x[1][2])) > 1 && println(io, "Betti Table for component ", i) + + # figure out width of first column L = sort(unique(collect(x[k][2][i] for k in 1:length(x)))) mi = minimum(L) mx = maximum(L) - initial_padding = max(ndigits(mi) + mi < 0 ? 0 : 1, 7)-2 - print(io, " "^initial_padding) - total_space_count = initial_padding + # 6 = length(degree); we take length of mi into account in case it is negative + first_column_width = max(6, ndigits(mi), ndigits(mx)) + + # header row + print(io, lpad("degree", first_column_width), ":") + total_space_count = first_column_width for j in min:step:maxv adjustment = j < 0 ? 1 : 0 + if j == min + adjustment += 1 + end space_count = max(0, column_widths[j] - ndigits(j) - adjustment) print(io, " "^(space_count)) print(io, j) - total_space_count = total_space_count + space_count + ndigits(j) + adjustment + total_space_count += space_count + ndigits(j) + adjustment end - total_space_count = total_space_count - print(io, "\n") - print(io, "-"^total_space_count) print(io, "\n") + # separator row + println(io, "-"^total_space_count) + # print bulk of table for j in mi:mx - adjustment = j < 0 ? 1 : 0 - print(io, j, " "^(5 - ndigits(j) - adjustment)) - print(io, ":") + print(io, lpad(j, first_column_width), ":") for h in min:step:maxv sum_current = sum([getindex(T, x[k]) for k in 1:length(x) if x[k][1] == h && x[k][2][i] == j]) @assert column_widths[h] - ndigits(sum_current) >= 2 @@ -1563,8 +1571,10 @@ function Base.show(io::IO, b::BettiTable) end print(io,"\n") end - print(io, "-" ^ total_space_count) - print(io, "\n", "total:") + # separator row + println(io, "-"^total_space_count) + # footer row + print(io, lpad("total", first_column_width), ":") for i_total in min:step:maxv sum_row = sum(getindex(T, x[j]) for j in 1:length(x) if x[j][1] == i_total) print(io, " ", sum_row) @@ -2499,23 +2509,22 @@ multivariate polynomial ring with coefficients in a field, return the Betti Tabl of the minimal free resolution of `M`. Similarly for `A` and `I`. # Examples -```julia +```jldoctest julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, [:w, :x, :y, :z]); julia> I = ideal(R, [w^2-x*z, w*x-y*z, x^2-w*y, x*y-z^2, y^2-w*z]); julia> A, _ = quo(R, I) -(Quotient of multivariate polynomial ring by ideal with 5 generators, Map from -R to A defined by a julia-function with inverse) +(Quotient of multivariate polynomial ring by ideal (w^2 - x*z, w*x - y*z, -w*y + x^2, x*y - z^2, -w*z + y^2), Map: R -> A) julia> minimal_betti_table(A) - 0 1 2 3 +degree: 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 5 5 - -2 : - - - 1 + 0: 1 - - - + 1: - 5 5 - + 2: - - - 1 ------------------ -total: 1 5 5 1 + total: 1 5 5 1 ``` """ function minimal_betti_table(M::ModuleFP{T}; check::Bool=true) where {T<:MPolyDecRingElem} @@ -2551,14 +2560,13 @@ Betti table of the minimal free resolution arising from `F`. The algorithm proceeds without actually minimizing the resolution. # Examples -```julia +```jldoctest julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, [:w, :x, :y, :z]); julia> I = ideal(R, [w^2-x*z, w*x-y*z, x^2-w*y, x*y-z^2, y^2-w*z]); julia> A, _ = quo(R, I) -(Quotient of multivariate polynomial ring by ideal with 5 generators, Map from -R to A defined by a julia-function with inverse) +(Quotient of multivariate polynomial ring by ideal (w^2 - x*z, w*x - y*z, -w*y + x^2, x*y - z^2, -w*z + y^2), Map: R -> A) julia> FA = free_resolution(A) Free resolution of A @@ -2566,22 +2574,22 @@ R^1 <---- R^5 <---- R^6 <---- R^2 <---- 0 0 1 2 3 4 julia> betti_table(FA) - 0 1 2 3 +degree: 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 5 5 1 -2 : - - 1 1 + 0: 1 - - - + 1: - 5 5 1 + 2: - - 1 1 ------------------ -total: 1 5 6 2 + total: 1 5 6 2 julia> minimal_betti_table(FA) - 0 1 2 3 +degree: 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 5 5 - -2 : - - - 1 + 0: 1 - - - + 1: - 5 5 - + 2: - - - 1 ------------------ -total: 1 5 5 1 + total: 1 5 5 1 ``` """ function minimal_betti_table(res::FreeResolution{T}; check::Bool=true) where {T<:ModuleFP} @@ -2806,7 +2814,7 @@ multivariate polynomial ring with coefficients in a field, return the Castelnuovo-Mumford regularity of `I`. # Examples -```julia +```jldoctest julia> R, (x, y, z) = graded_polynomial_ring(QQ, [:x, :y, :z]); julia> F = graded_free_module(R, 1); @@ -2823,16 +2831,16 @@ julia> cm_regularity(M) 3 julia> minimal_betti_table(M) - 0 1 2 3 --------------- -0 : 1 - - - -1 : - 3 - - -2 : - - 3 - -3 : - - - 1 --------------- -total: 1 3 3 1 +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 3 - - + 2: - - 3 - + 3: - - - 1 +------------------ + total: 1 3 3 1 ``` -```julia +```jldoctest julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, [:w, :x, :y, :z]); julia> I = ideal(R, [-x*z+y^2, x*y-w*z, x^2-w*y]); @@ -2843,12 +2851,12 @@ julia> cm_regularity(I) julia> A, _ = quo(R, I); julia> minimal_betti_table(A) - 0 1 2 ------------- -0 : 1 - - -1 : - 3 2 ------------- -total: 1 3 2 +degree: 0 1 2 +--------------- + 0: 1 - - + 1: - 3 2 +--------------- + total: 1 3 2 ``` """ function cm_regularity(M::ModuleFP; check::Bool=true) diff --git a/src/Modules/UngradedModules/FreeResolutions.jl b/src/Modules/UngradedModules/FreeResolutions.jl index e74778908f60..f9d075c36bf1 100644 --- a/src/Modules/UngradedModules/FreeResolutions.jl +++ b/src/Modules/UngradedModules/FreeResolutions.jl @@ -305,7 +305,7 @@ julia> is_complete(fr) true ``` -``` +```jldoctest julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, [:w, :x, :y, :z]); julia> Z = R(0) @@ -334,14 +334,14 @@ R^2 <---- R^7 <---- R^8 <---- R^3 <---- 0 0 1 2 3 4 julia> betti_table(FM1) - 0 1 2 3 ------------------ --1 : - 1 - - -0 : 2 - - - -1 : - 3 3 1 -2 : - 3 5 2 ------------------ -total: 2 7 8 3 +degree: 0 1 2 3 +------------------ + -1: - 1 - - + 0: 2 - - - + 1: - 3 3 1 + 2: - 3 5 2 +------------------ + total: 2 7 8 3 julia> matrix(map(FM1, 1)) [1 0] @@ -358,14 +358,14 @@ R^2 <---- R^4 <---- R^4 <---- R^2 <---- 0 0 1 2 3 4 julia> betti_table(FM2) - 0 1 2 3 ------------------ --1 : - 1 - - -0 : 2 - - - -1 : - 3 - - -2 : - - 4 2 ------------------ -total: 2 4 4 2 +degree: 0 1 2 3 +------------------ + -1: - 1 - - + 0: 2 - - - + 1: - 3 - - + 2: - - 4 2 +------------------ + total: 2 4 4 2 julia> matrix(map(FM2, 1)) [1 0] @@ -379,13 +379,13 @@ R^1 <---- R^3 <---- R^4 <---- R^2 <---- 0 0 1 2 3 4 julia> betti_table(FM3) - 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 3 - - -2 : - - 4 2 ------------------ -total: 1 3 4 2 +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 3 - - + 2: - - 4 2 +------------------ + total: 1 3 4 2 julia> matrix(map(FM3, 1)) [-x*z + y^2] diff --git a/src/Rings/mpoly-graded.jl b/src/Rings/mpoly-graded.jl index c6ed7d739a1a..40394f5d9b62 100644 --- a/src/Rings/mpoly-graded.jl +++ b/src/Rings/mpoly-graded.jl @@ -2383,7 +2383,15 @@ julia> I = ideal(R, [y^2*z − x^2*w, z^4 − x*w^3]); julia> cm_regularity(I) 6 -julia> minimal_betti_table(I); +julia> minimal_betti_table(I) +degree: 0 1 +------------ + 3: 1 - + 4: 1 - + 5: - - + 6: - 1 +------------ + total: 2 1 ``` """ function cm_regularity(I::MPolyIdeal) diff --git a/test/Modules/ModulesGraded.jl b/test/Modules/ModulesGraded.jl index 577da99baee1..b352bf66472d 100644 --- a/test/Modules/ModulesGraded.jl +++ b/test/Modules/ModulesGraded.jl @@ -1046,37 +1046,94 @@ end @test degree(phi) == degree(element_to_homomorphism(phi)(v)) - degree(v) end -@testset "minimal Betti tables" begin - # The Veronese surface in IP^4 due to Wolfram - F = GF(31991) - R, (x,y,z,u,v) = graded_polynomial_ring(F, [:x, :y, :z, :u, :v]) - I = ideal([F(45)/16*x^3-8565*x^2*y-7937*x*y^2+14060*x^2*z+F(53)/113*x*y*z-F(125)/17*x*z^2-15712*x^2*u-5990*x*y*u-F(71)/88*x*z*u-6141*x*u^2+F(49)/104*x^2*v-194*x*y*v+F(63)/103*y^2*v+F(74)/103*x*z*v+11618*y*z*v+F(41)/111*z^2*v+14387*x*u*v-F(64)/9*y*u*v+13097*z*u*v+F(84)/101*u^2*v+12211*x*v^2+F(85)/63*y*v^2-F(119)/100*z*v^2-6247*u*v^2-F(74)/51*v^3,-F(45)/16*x^2*y+8565*x*y^2+7937*y^3-14060*x*y*z-F(53)/113*y^2*z+F(125)/17*y*z^2+15712*x*y*u+5990*y^2*u+F(71)/88*y*z*u+6141*y*u^2-x^2*v+3340*x*y*v+F(111)/40*y^2*v+F(22)/37*x*z*v-F(110)/29*y*z*v-F(71)/25*z^2*v-F(71)/87*x*u*v+F(112)/55*y*u*v+F(103)/80*z*u*v-F(100)/7*u^2*v-5013*x*v^2+15010*y*v^2+F(52)/51*z*v^2+F(126)/5*u*v^2-6580*v^3,-F(45)/16*x^2*z+8565*x*y*z+7937*y^2*z-14060*x*z^2-F(53)/113*y*z^2+F(125)/17*z^3+15712*x*z*u+5990*y*z*u+F(71)/88*z^2*u+6141*z*u^2+F(41)/79*x^2*v+F(121)/48*x*y*v+9621*y^2*v+F(105)/83*x*z*v+F(27)/26*y*z*v-649*z^2*v-12278*x*u*v+14655*y*u*v+10594*z*u*v+10462*u^2*v+F(76)/13*x*v^2+F(97)/50*y*v^2-7761*z*v^2+F(52)/31*u*v^2-6636*v^3,-F(45)/16*x^2*u+8565*x*y*u+7937*y^2*u-14060*x*z*u-F(53)/113*y*z*u+F(125)/17*z^2*u+15712*x*u^2+5990*y*u^2+F(71)/88*z*u^2+6141*u^3-F(40)/31*x^2*v+13297*x*y*v+F(126)/113*y^2*v-F(51)/100*x*z*v-F(97)/38*y*z*v+10372*z^2*v-F(4)/31*x*u*v+F(32)/9*y*u*v-F(1)/7*z*u*v+15473*u^2*v+F(101)/36*x*v^2+F(97)/109*y*v^2-14100*z*v^2+F(109)/32*u*v^2-F(5)/111*v^3,-F(40)/31*x^3+13297*x^2*y+F(126)/113*x*y^2-F(51)/100*x^2*z-F(97)/38*x*y*z+10372*x*z^2+F(26)/105*x^2*u-3745*x*y*u+F(63)/103*y^2*u-F(98)/61*x*z*u+11618*y*z*u+F(41)/111*z^2*u+F(26)/15*x*u^2-F(64)/9*y*u^2+13097*z*u^2+F(84)/101*u^3+F(101)/36*x^2*v+F(97)/109*x*y*v-14100*x*z*v-14778*x*u*v+F(85)/63*y*u*v-F(119)/100*z*u*v-6247*u^2*v-F(5)/111*x*v^2-F(74)/51*u*v^2,F(40)/31*x^2*y-13297*x*y^2-F(126)/113*y^3+F(51)/100*x*y*z+F(97)/38*y^2*z-10372*y*z^2-x^2*u+F(103)/30*x*y*u+2754*y^2*u+F(22)/37*x*z*u+F(126)/95*y*z*u-F(71)/25*z^2*u-F(71)/87*x*u^2-F(109)/7*y*u^2+F(103)/80*z*u^2-F(100)/7*u^3-F(101)/36*x*y*v-F(97)/109*y^2*v+14100*y*z*v-5013*x*u*v+10008*y*u*v+F(52)/51*z*u*v+F(126)/5*u^2*v+F(5)/111*y*v^2-6580*u*v^2,F(40)/31*x^2*z-13297*x*y*z-F(126)/113*y^2*z+F(51)/100*x*z^2+F(97)/38*y*z^2-10372*z^3+F(41)/79*x^2*u+F(121)/48*x*y*u+9621*y^2*u+5671*x*z*u-F(42)/71*y*z*u-5219*z^2*u-12278*x*u^2+14655*y*u^2+F(58)/59*z*u^2+10462*u^3-F(101)/36*x*z*v-F(97)/109*y*z*v+14100*z^2*v+F(76)/13*x*u*v+F(97)/50*y*u*v-12763*z*u*v+F(52)/31*u^2*v+F(5)/111*z*v^2-6636*u*v^2,F(41)/79*x^3+F(121)/48*x^2*y+9621*x*y^2-F(22)/109*x^2*z+F(25)/19*x*y*z+F(63)/103*y^2*z+F(23)/45*x*z^2+11618*y*z^2+F(41)/111*z^3-12278*x^2*u+14655*x*y*u+F(126)/73*x*z*u-F(64)/9*y*z*u+13097*z^2*u+10462*x*u^2+F(84)/101*z*u^2+F(76)/13*x^2*v+F(97)/50*x*y*v-F(106)/115*x*z*v+F(85)/63*y*z*v-F(119)/100*z^2*v+F(52)/31*x*u*v-6247*z*u*v-6636*x*v^2-F(74)/51*z*v^2,-F(41)/79*x^2*y-F(121)/48*x*y^2-9621*y^3-x^2*z-F(22)/89*x*y*z-F(32)/17*y^2*z+F(22)/37*x*z^2-F(86)/111*y*z^2-F(71)/25*z^3+12278*x*y*u-14655*y^2*u-F(71)/87*x*z*u+F(74)/47*y*z*u+F(103)/80*z^2*u-10462*y*u^2-F(100)/7*z*u^2-F(76)/13*x*y*v-F(97)/50*y^2*v-5013*x*z*v-9220*y*z*v+F(52)/51*z^2*v-F(52)/31*y*u*v+F(126)/5*z*u*v+6636*y*v^2-6580*z*v^2,x^3+11117*x^2*y+991*x*y^2-F(63)/103*y^3-F(22)/37*x^2*z-F(35)/13*x*y*z-11618*y^2*z+F(71)/25*x*z^2-F(41)/111*y*z^2+F(71)/87*x^2*u+F(68)/115*x*y*u+F(64)/9*y^2*u-F(103)/80*x*z*u-13097*y*z*u+F(100)/7*x*u^2-F(84)/101*y*u^2+5013*x^2*v-F(67)/114*x*y*v-F(85)/63*y^2*v-F(52)/51*x*z*v+F(119)/100*y*z*v-F(126)/5*x*u*v+6247*y*u*v+6580*x*v^2+F(74)/51*y*v^2]) - I = ideal(R, minimal_generating_set(I)) - F = graded_free_module(R, 1) - sub_F, inc = sub(F, [g*F[1] for g in gens(I)]) - M = cokernel(inc) - A, _ = quo(R, I) - # To reproduce the string on the right hand side, evaluate - # `"$(Oscar.minimal_betti_table(M))"` - # and insert the result here; after verification of the result! - @test "$(Oscar.minimal_betti_table(A))" == " 0 1 2 3 4\n---------------------\n0 : 1 - - - -\n1 : - - - - -\n2 : - 7 10 5 1\n---------------------\ntotal: 1 7 10 5 1" - - @test "$(Oscar.minimal_betti_table(M))" == " 0 1 2 3 4\n---------------------\n0 : 1 - - - -\n1 : - - - - -\n2 : - 7 10 5 1\n---------------------\ntotal: 1 7 10 5 1" - - @test "$(Oscar.minimal_betti_table(I))" == "$(Oscar.minimal_betti_table(sub_F))" - - # small example due to Janko - R, x = graded_polynomial_ring(QQ, :x => 1:7) - I = ideal(R, [x[1]*x[2]*x[5], x[1]*x[2]*x[6], x[3]*x[4]*x[6], x[3]*x[4]*x[7], x[5]*x[7]]) - A, _ = quo(R, I) - @test "$(Oscar.minimal_betti_table(A))" == " 0 1 2 3\n-----------------\n0 : 1 - - -\n1 : - 1 - -\n2 : - 4 4 -\n3 : - - 1 -\n4 : - - - 1\n-----------------\ntotal: 1 5 5 1" - - # another example due to Wolfram - R, (x, y, z, w) = graded_polynomial_ring(QQ, [:x, :y, :z, :w]) - I = ideal(R, [w^2 - x*z, w*x - y*z, x^2 - w*y, x*y - z^2, y^2 - w*z]) - A, _ = quo(R, I) - @test "$(Oscar.minimal_betti_table(free_resolution(A)))" == " 0 1 2 3\n-----------------\n0 : 1 - - -\n1 : - 5 5 -\n2 : - - - 1\n-----------------\ntotal: 1 5 5 1" -end +Oscar.@_AuxDocTest "minimal Betti tables", (fix = false), +raw""" +# The Veronese surface in IP^4 due to Wolfram + +```jldoctest; setup = :(using Oscar) +julia> F = GF(31991) +Prime field of characteristic 31991 + +julia> R, (x,y,z,u,v) = graded_polynomial_ring(F, [:x, :y, :z, :u, :v]); + +julia> I = ideal([F(45)/16*x^3-8565*x^2*y-7937*x*y^2+14060*x^2*z+F(53)/113*x*y*z-F(125)/17*x*z^2-15712*x^2*u-5990*x*y*u-F(71)/88*x*z*u-6141*x*u^2+F(49)/104*x^2*v-194*x*y*v+F(63)/103*y^2*v+F(74)/103*x*z*v+11618*y*z*v+F(41)/111*z^2*v+14387*x*u*v-F(64)/9*y*u*v+13097*z*u*v+F(84)/101*u^2*v+12211*x*v^2+F(85)/63*y*v^2-F(119)/100*z*v^2-6247*u*v^2-F(74)/51*v^3,-F(45)/16*x^2*y+8565*x*y^2+7937*y^3-14060*x*y*z-F(53)/113*y^2*z+F(125)/17*y*z^2+15712*x*y*u+5990*y^2*u+F(71)/88*y*z*u+6141*y*u^2-x^2*v+3340*x*y*v+F(111)/40*y^2*v+F(22)/37*x*z*v-F(110)/29*y*z*v-F(71)/25*z^2*v-F(71)/87*x*u*v+F(112)/55*y*u*v+F(103)/80*z*u*v-F(100)/7*u^2*v-5013*x*v^2+15010*y*v^2+F(52)/51*z*v^2+F(126)/5*u*v^2-6580*v^3,-F(45)/16*x^2*z+8565*x*y*z+7937*y^2*z-14060*x*z^2-F(53)/113*y*z^2+F(125)/17*z^3+15712*x*z*u+5990*y*z*u+F(71)/88*z^2*u+6141*z*u^2+F(41)/79*x^2*v+F(121)/48*x*y*v+9621*y^2*v+F(105)/83*x*z*v+F(27)/26*y*z*v-649*z^2*v-12278*x*u*v+14655*y*u*v+10594*z*u*v+10462*u^2*v+F(76)/13*x*v^2+F(97)/50*y*v^2-7761*z*v^2+F(52)/31*u*v^2-6636*v^3,-F(45)/16*x^2*u+8565*x*y*u+7937*y^2*u-14060*x*z*u-F(53)/113*y*z*u+F(125)/17*z^2*u+15712*x*u^2+5990*y*u^2+F(71)/88*z*u^2+6141*u^3-F(40)/31*x^2*v+13297*x*y*v+F(126)/113*y^2*v-F(51)/100*x*z*v-F(97)/38*y*z*v+10372*z^2*v-F(4)/31*x*u*v+F(32)/9*y*u*v-F(1)/7*z*u*v+15473*u^2*v+F(101)/36*x*v^2+F(97)/109*y*v^2-14100*z*v^2+F(109)/32*u*v^2-F(5)/111*v^3,-F(40)/31*x^3+13297*x^2*y+F(126)/113*x*y^2-F(51)/100*x^2*z-F(97)/38*x*y*z+10372*x*z^2+F(26)/105*x^2*u-3745*x*y*u+F(63)/103*y^2*u-F(98)/61*x*z*u+11618*y*z*u+F(41)/111*z^2*u+F(26)/15*x*u^2-F(64)/9*y*u^2+13097*z*u^2+F(84)/101*u^3+F(101)/36*x^2*v+F(97)/109*x*y*v-14100*x*z*v-14778*x*u*v+F(85)/63*y*u*v-F(119)/100*z*u*v-6247*u^2*v-F(5)/111*x*v^2-F(74)/51*u*v^2,F(40)/31*x^2*y-13297*x*y^2-F(126)/113*y^3+F(51)/100*x*y*z+F(97)/38*y^2*z-10372*y*z^2-x^2*u+F(103)/30*x*y*u+2754*y^2*u+F(22)/37*x*z*u+F(126)/95*y*z*u-F(71)/25*z^2*u-F(71)/87*x*u^2-F(109)/7*y*u^2+F(103)/80*z*u^2-F(100)/7*u^3-F(101)/36*x*y*v-F(97)/109*y^2*v+14100*y*z*v-5013*x*u*v+10008*y*u*v+F(52)/51*z*u*v+F(126)/5*u^2*v+F(5)/111*y*v^2-6580*u*v^2,F(40)/31*x^2*z-13297*x*y*z-F(126)/113*y^2*z+F(51)/100*x*z^2+F(97)/38*y*z^2-10372*z^3+F(41)/79*x^2*u+F(121)/48*x*y*u+9621*y^2*u+5671*x*z*u-F(42)/71*y*z*u-5219*z^2*u-12278*x*u^2+14655*y*u^2+F(58)/59*z*u^2+10462*u^3-F(101)/36*x*z*v-F(97)/109*y*z*v+14100*z^2*v+F(76)/13*x*u*v+F(97)/50*y*u*v-12763*z*u*v+F(52)/31*u^2*v+F(5)/111*z*v^2-6636*u*v^2,F(41)/79*x^3+F(121)/48*x^2*y+9621*x*y^2-F(22)/109*x^2*z+F(25)/19*x*y*z+F(63)/103*y^2*z+F(23)/45*x*z^2+11618*y*z^2+F(41)/111*z^3-12278*x^2*u+14655*x*y*u+F(126)/73*x*z*u-F(64)/9*y*z*u+13097*z^2*u+10462*x*u^2+F(84)/101*z*u^2+F(76)/13*x^2*v+F(97)/50*x*y*v-F(106)/115*x*z*v+F(85)/63*y*z*v-F(119)/100*z^2*v+F(52)/31*x*u*v-6247*z*u*v-6636*x*v^2-F(74)/51*z*v^2,-F(41)/79*x^2*y-F(121)/48*x*y^2-9621*y^3-x^2*z-F(22)/89*x*y*z-F(32)/17*y^2*z+F(22)/37*x*z^2-F(86)/111*y*z^2-F(71)/25*z^3+12278*x*y*u-14655*y^2*u-F(71)/87*x*z*u+F(74)/47*y*z*u+F(103)/80*z^2*u-10462*y*u^2-F(100)/7*z*u^2-F(76)/13*x*y*v-F(97)/50*y^2*v-5013*x*z*v-9220*y*z*v+F(52)/51*z^2*v-F(52)/31*y*u*v+F(126)/5*z*u*v+6636*y*v^2-6580*z*v^2,x^3+11117*x^2*y+991*x*y^2-F(63)/103*y^3-F(22)/37*x^2*z-F(35)/13*x*y*z-11618*y^2*z+F(71)/25*x*z^2-F(41)/111*y*z^2+F(71)/87*x^2*u+F(68)/115*x*y*u+F(64)/9*y^2*u-F(103)/80*x*z*u-13097*y*z*u+F(100)/7*x*u^2-F(84)/101*y*u^2+5013*x^2*v-F(67)/114*x*y*v-F(85)/63*y^2*v-F(52)/51*x*z*v+F(119)/100*y*z*v-F(126)/5*x*u*v+6247*y*u*v+6580*x*v^2+F(74)/51*y*v^2]); + +julia> I = ideal(R, minimal_generating_set(I)); + +julia> F = graded_free_module(R, 1); + +julia> sub_F, inc = sub(F, [g*F[1] for g in gens(I)]); + +julia> M = cokernel(inc); + +julia> A, _ = quo(R, I); + +julia> minimal_betti_table(A) +degree: 0 1 2 3 4 +---------------------- + 0: 1 - - - - + 1: - - - - - + 2: - 7 10 5 1 +---------------------- + total: 1 7 10 5 1 + +julia> minimal_betti_table(M) +degree: 0 1 2 3 4 +---------------------- + 0: 1 - - - - + 1: - - - - - + 2: - 7 10 5 1 +---------------------- + total: 1 7 10 5 1 + +julia> minimal_betti_table(I) +degree: 0 1 2 3 +------------------- + 3: 7 10 5 1 +------------------- + total: 7 10 5 1 +``` + +# small example due to Janko + +```jldoctest; setup = :(using Oscar) +julia> R, x = graded_polynomial_ring(QQ, :x => 1:7); + +julia> I = ideal(R, [x[1]*x[2]*x[5], x[1]*x[2]*x[6], x[3]*x[4]*x[6], x[3]*x[4]*x[7], x[5]*x[7]]); + +julia> A, _ = quo(R, I); + +julia> minimal_betti_table(A) +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 1 - - + 2: - 4 4 - + 3: - - 1 - + 4: - - - 1 +------------------ + total: 1 5 5 1 +``` + +# another example due to Wolfram + +```jldoctest; setup = :(using Oscar) +julia> R, (x, y, z, w) = graded_polynomial_ring(QQ, [:x, :y, :z, :w]); + +julia> I = ideal(R, [w^2 - x*z, w*x - y*z, x^2 - w*y, x*y - z^2, y^2 - w*z]); + +julia> A, _ = quo(R, I); + +julia> minimal_betti_table(free_resolution(A)) +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 5 5 - + 2: - - - 1 +------------------ + total: 1 5 5 1 +``` +""" @testset "sheaf cohomology" begin S, _ = graded_polynomial_ring(QQ, :x => 1:4) @@ -1264,9 +1321,3 @@ end ideal_as_module(J) quotient_ring_as_module(J) end - - - - - - diff --git a/test/book/cornerstones/algebraic-geometry/alexander-surface.jlcon b/test/book/cornerstones/algebraic-geometry/alexander-surface.jlcon index e11ef69384e1..c770d059e81a 100644 --- a/test/book/cornerstones/algebraic-geometry/alexander-surface.jlcon +++ b/test/book/cornerstones/algebraic-geometry/alexander-surface.jlcon @@ -31,16 +31,16 @@ julia> A = homogeneous_coordinate_ring(X); julia> FA = free_resolution(A); julia> minimal_betti_table(FA) - 0 1 2 3 4 ------------------------ -0 : 1 - - - - -1 : - - - - - -2 : - - - - - -3 : - - - - - -4 : - 15 26 15 3 -5 : - 1 3 3 1 ------------------------ -total: 1 16 29 18 4 +degree: 0 1 2 3 4 +------------------------ + 0: 1 - - - - + 1: - - - - - + 2: - - - - - + 3: - - - - - + 4: - 15 26 15 3 + 5: - 1 3 3 1 +------------------------ + total: 1 16 29 18 4 julia> I = defining_ideal(X); diff --git a/test/book/cornerstones/algebraic-geometry/canonicalimage.jlcon b/test/book/cornerstones/algebraic-geometry/canonicalimage.jlcon index 535e21eb70cd..14f72936235a 100644 --- a/test/book/cornerstones/algebraic-geometry/canonicalimage.jlcon +++ b/test/book/cornerstones/algebraic-geometry/canonicalimage.jlcon @@ -48,14 +48,14 @@ julia> Q , _ = quo(P5, JJ); julia> re = free_resolution(Q); julia> minimal_betti_table(re) - 0 1 2 3 4 ---------------------- -0 : 1 - - - - -1 : - 6 8 3 - -2 : - 3 8 6 - -3 : - - - - 1 ---------------------- -total: 1 9 16 9 1 +degree: 0 1 2 3 4 +---------------------- + 0: 1 - - - - + 1: - 6 8 3 - + 2: - 3 8 6 - + 3: - - - - 1 +---------------------- + total: 1 9 16 9 1 julia> phi1 = hom(Rt,R,[x,y,R(1//10)]); @@ -94,11 +94,11 @@ julia> Q , _ = quo(P5, JJ); julia> re = free_resolution(Q); julia> minimal_betti_table(re) - 0 1 2 3 4 ---------------------- -0 : 1 - - - - -1 : - 6 5 - - -2 : - - 5 6 - -3 : - - - - 1 ---------------------- -total: 1 6 10 6 1 +degree: 0 1 2 3 4 +---------------------- + 0: 1 - - - - + 1: - 6 5 - - + 2: - - 5 6 - + 3: - - - - 1 +---------------------- + total: 1 6 10 6 1 diff --git a/test/book/cornerstones/algebraic-geometry/char3-surface-1.jlcon b/test/book/cornerstones/algebraic-geometry/char3-surface-1.jlcon index fdb0d668a212..3cb51c9331f4 100644 --- a/test/book/cornerstones/algebraic-geometry/char3-surface-1.jlcon +++ b/test/book/cornerstones/algebraic-geometry/char3-surface-1.jlcon @@ -7,10 +7,10 @@ julia> Qm, _ = quo(S, m); julia> FQm = free_resolution(Qm, algorithm = :mres); julia> betti_table(FQm) - 0 1 2 3 4 5 ---------------------------- -0 : 1 - - - - - -1 : - 10 15 2 - - -2 : - - 7 26 20 5 ---------------------------- -total: 1 10 22 28 20 5 +degree: 0 1 2 3 4 5 +---------------------------- + 0: 1 - - - - - + 1: - 10 15 2 - - + 2: - - 7 26 20 5 +---------------------------- + total: 1 10 22 28 20 5 diff --git a/test/book/cornerstones/algebraic-geometry/char3-surface-2.jlcon b/test/book/cornerstones/algebraic-geometry/char3-surface-2.jlcon index 171ccbc7a550..3acfb66800b9 100644 --- a/test/book/cornerstones/algebraic-geometry/char3-surface-2.jlcon +++ b/test/book/cornerstones/algebraic-geometry/char3-surface-2.jlcon @@ -19,14 +19,14 @@ julia> D = graded_cokernel(transpose(MM*NN)); julia> FD = free_resolution(D, algorithm = :mres); julia> betti_table(FD) - 0 1 2 ----------------- -0 : 10 10 - -1 : - - 1 -2 : - - - -3 : - - 1 ----------------- -total: 10 10 2 +degree: 0 1 2 +----------------- + 0: 10 10 - + 1: - - 1 + 2: - - - + 3: - - 1 +----------------- + total: 10 10 2 julia> P = cokernel(transpose(matrix(map(FD, 2)))); @@ -37,16 +37,16 @@ julia> QI, _ = quo(S, I); julia> FQI = free_resolution(QI, algorithm = :mres); julia> betti_table(FQI) - 0 1 2 3 4 ------------------------ -0 : 1 - - - - -1 : - - - - - -2 : - - - - - -3 : - - - - - -4 : - 5 - - - -5 : - 7 26 20 5 ------------------------ -total: 1 12 26 20 5 +degree: 0 1 2 3 4 +------------------------ + 0: 1 - - - - + 1: - - - - - + 2: - - - - - + 3: - - - - - + 4: - 5 - - - + 5: - 7 26 20 5 +------------------------ + total: 1 12 26 20 5 julia> dim(I) 3 diff --git a/test/book/cornerstones/algebraic-geometry/exres2.jlcon b/test/book/cornerstones/algebraic-geometry/exres2.jlcon index 3181f5c529ed..a1fc8c1d7e17 100644 --- a/test/book/cornerstones/algebraic-geometry/exres2.jlcon +++ b/test/book/cornerstones/algebraic-geometry/exres2.jlcon @@ -1,17 +1,17 @@ julia> betti_table(FA) - 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 5 5 1 -2 : - - 1 1 ------------------ -total: 1 5 6 2 +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 5 5 1 + 2: - - 1 1 +------------------ + total: 1 5 6 2 julia> minimal_betti_table(FA) - 0 1 2 3 ------------------ -0 : 1 - - - -1 : - 5 5 - -2 : - - - 1 ------------------ -total: 1 5 5 1 +degree: 0 1 2 3 +------------------ + 0: 1 - - - + 1: - 5 5 - + 2: - - - 1 +------------------ + total: 1 5 5 1 diff --git a/test/book/cornerstones/algebraic-geometry/param.jlcon b/test/book/cornerstones/algebraic-geometry/param.jlcon index b8e9dbe6e490..13a57fc54342 100644 --- a/test/book/cornerstones/algebraic-geometry/param.jlcon +++ b/test/book/cornerstones/algebraic-geometry/param.jlcon @@ -33,11 +33,11 @@ Projective curve defined by ideal with 3 generators julia> betti(free_resolution(defining_ideal(D))) - 0 1 ------------ -2 : 3 2 ------------ -total: 3 2 +degree: 0 1 +------------ + 2: 3 2 +------------ + total: 3 2 julia> Oscar.rat_normal_curve_anticanonical_map(D) 2-element Vector{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}}: From 0eed667315737c7802a50e98540098939c40e0e2 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Mon, 11 Nov 2024 11:21:48 +0100 Subject: [PATCH 53/84] avoid calling `conductor` (#4247) * avoid calling `conductor` Instead, call `Hecke.force_coerce_cyclo(Kg, data(x), Val(false))`, which finds out whether the conversion works. * try to reduce some confusion changed a `check = false` keyword argument to `throw_error = true` in an internal function, `check` was a misleading name; the keyword argument is used only by a `has_preimage_with_preimage` method --- src/Rings/AbelianClosure.jl | 56 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/Rings/AbelianClosure.jl b/src/Rings/AbelianClosure.jl index dd60211a93b8..76d38f6b6e4d 100644 --- a/src/Rings/AbelianClosure.jl +++ b/src/Rings/AbelianClosure.jl @@ -933,25 +933,22 @@ end # Construct the map from `F` to an abelian closure `K` such that `gen(F)` # is mapped to `x`. -# If `F` is a cyclotomic field with conductor `N` then assume that `gen(F)` -# is mapped to `QQAbFieldElem(gen(F), N)`. +# If `F` has conductor `N` then assume that `x.c == N` holds. +# If `F` is a cyclotomic field with conductor `N` then assume that +# `x == QQAbFieldElem(gen(F), N)`. # (Use that the powers of this element form a basis of the field.) function _embedding(F::QQField, K::QQAbField{AbsSimpleNumField}, x::QQAbFieldElem{AbsSimpleNumFieldElem}) - C1, z = cyclotomic_field(1) + C1, _ = cyclotomic_field(1) f = function(x::QQFieldElem) return QQAbFieldElem(C1(x), 1) end - finv = function(x::QQAbFieldElem; check::Bool = false) - if conductor(x) == 1 - return Hecke.force_coerce_cyclo(C1, data(x)) - elseif check - return - else - error("element has no preimage") - end + finv = function(x::QQAbFieldElem; throw_error::Bool = true) + res = Hecke.force_coerce_cyclo(C1, data(x), Val(false)) + throw_error && res === nothing && error("element has no preimage") + return res end return MapFromFunc(F, K, f, finv) @@ -966,18 +963,14 @@ function _embedding(F::AbsSimpleNumField, K::QQAbField{AbsSimpleNumField}, return QQAbFieldElem(x, n) end - finv = function(x::QQAbFieldElem; check::Bool = false) - if n % conductor(x) == 0 - return Hecke.force_coerce_cyclo(F, data(x)) - elseif check - return - else - error("element has no preimage") - end + finv = function(x::QQAbFieldElem; throw_error::Bool = true) + res = Hecke.force_coerce_cyclo(F, data(x), Val(false)) + throw_error && res === nothing && error("element has no preimage") + return res end else # `F` is expected to be a proper subfield of a cyclotomic field. - n = conductor(x) + n = x.c x = data(x) Kn, = AbelianClosure.cyclotomic_field(K, n) powers = [Hecke.coefficients(Hecke.force_coerce_cyclo(Kn, x^i)) @@ -989,25 +982,26 @@ function _embedding(F::AbsSimpleNumField, K::QQAbField{AbsSimpleNumField}, return QQAbFieldElem(evaluate(R(z), x), n) end - finv = function(x::QQAbFieldElem; check::Bool = false) - n % conductor(x) == 0 || return false, zero(F) + finv = function(x::QQAbFieldElem; throw_error::Bool = true) # Write `x` w.r.t. the n-th cyclotomic field ... g = gcd(x.c, n) Kg, = AbelianClosure.cyclotomic_field(K, g) - x = Hecke.force_coerce_cyclo(Kg, data(x)) + x = Hecke.force_coerce_cyclo(Kg, data(x), Val(false)) + if x === nothing + throw_error && error("element has no preimage") + return + end x = Hecke.force_coerce_cyclo(Kn, x) # ... and then w.r.t. `F` a = Hecke.coefficients(x) fl, sol = can_solve_with_solution(c, matrix(QQ, length(a), 1, a); side = :right) - if fl - b = transpose(sol) - b = [b[i] for i in 1:length(b)] - return F(b) - elseif check + if !fl + throw_error && error("element has no preimage") return - else - error("element has no preimage") end + b = transpose(sol) + b = [b[i] for i in 1:length(b)] + return F(b) end end return MapFromFunc(F, K, f, finv) @@ -1016,7 +1010,7 @@ end # The following works only if `mp.g` admits a second argument, # which is the case if `mp` has been constructed by `_embedding` above. function has_preimage_with_preimage(mp::MapFromFunc{AbsSimpleNumField, QQAbField{AbsSimpleNumField}}, x::QQAbFieldElem{AbsSimpleNumFieldElem}) - pre = mp.g(x, check = true) + pre = mp.g(x, throw_error = false) if isnothing(pre) return false, zero(domain(mp)) else From b3ab222d48dcfdd96b5e06922f881a87d16a44bd Mon Sep 17 00:00:00 2001 From: Marcel Wack <63490664+Sequenzer@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:38:02 +0100 Subject: [PATCH 54/84] changes to the check argument in matroid_from_matrix_columns (#4287) --- src/Combinatorics/Matroids/matroids.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Combinatorics/Matroids/matroids.jl b/src/Combinatorics/Matroids/matroids.jl index 4e028825fd1f..af2a530d4556 100644 --- a/src/Combinatorics/Matroids/matroids.jl +++ b/src/Combinatorics/Matroids/matroids.jl @@ -278,9 +278,9 @@ function matroid_from_hyperplanes(hyperplanes::Union{AbstractVector{T},AbstractS end @doc raw""" - matroid_from_matrix_columns(A::MatrixElem) + matroid_from_matrix_columns(A::MatrixElem; check::Bool=true) -A matroid represented by the column vectors of a matrix `A`. +A matroid represented by the column vectors of a matrix `A`. The value of `check` is currently ignored. See Section 1.1 of [Oxl11](@cite). @@ -310,13 +310,13 @@ function matroid_from_matrix_columns(A::MatrixElem; check::Bool=true) end end - return matroid_from_bases(bases, ncols(A); check=check) + return matroid_from_bases(bases, ncols(A); check=false) end @doc raw""" - matroid_from_matrix_columns(A::MatrixElem) + matroid_from_matrix_rows(A::MatrixElem, check::Bool=true) -A matroid represented by the row vectors of a matrix. +A matroid represented by the row vectors of a matrix. The value of `check` is currently ignored. See Section 1.1 of [Oxl11](@cite). From 2d7a2b06d176a883f3bd9fcc873937f5d872823a Mon Sep 17 00:00:00 2001 From: Marcel Wack <63490664+Sequenzer@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:03:15 +0100 Subject: [PATCH 55/84] fixed the leading zero bug in matroid_hex (#4299) --- src/Combinatorics/Matroids/properties.jl | 6 ++---- test/Combinatorics/Matroids/Matroids.jl | 7 +++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Combinatorics/Matroids/properties.jl b/src/Combinatorics/Matroids/properties.jl index 56bf952a350f..addae6cad2e0 100644 --- a/src/Combinatorics/Matroids/properties.jl +++ b/src/Combinatorics/Matroids/properties.jl @@ -1059,11 +1059,9 @@ julia> matroid_hex(fano_matroid()) function matroid_hex(M::Matroid) rvlx = min_revlex_basis_encoding(M) r,n = rank(M), length(M) + v = zeros(Int, 4*ceil(Int, length(rvlx)/4)) + v[length(v)-length(rvlx)+1:end] = _revlex_basis_to_vector(rvlx) - v = _revlex_basis_to_vector(rvlx) - for _ in 1:(4-length(v)%4) - pushfirst!(v,0) - end v = reshape(v,4,:) v = [string(parse(Int, join(v[:, j]), base=2), base=16) for j in 1:size(v)[2]] diff --git a/test/Combinatorics/Matroids/Matroids.jl b/test/Combinatorics/Matroids/Matroids.jl index 94474e845106..5cd0f76521dc 100644 --- a/test/Combinatorics/Matroids/Matroids.jl +++ b/test/Combinatorics/Matroids/Matroids.jl @@ -422,10 +422,17 @@ @testset "matroid_hex" begin M = fano_matroid() N = uniform_matroid(2, 4) + NN = uniform_matroid(1, 4) M1 = matroid_from_matroid_hex(matroid_hex(M)) N1 = matroid_from_matroid_hex(matroid_hex(N)) + NN1 = matroid_from_matroid_hex(matroid_hex(NN)) + @test is_isomorphic(M, M1) @test is_isomorphic(N, N1) + + @test matroid_hex(NN) == "r1n4_f" + @test is_isomorphic(NN, NN1) + end end From a0d60ee9c62e18f12802a7a6ae597f8a0a8fece7 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 13 Nov 2024 08:20:35 +0100 Subject: [PATCH 56/84] Test and fix `norm_equation` for `RelNumFieldOrder` (#4282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tommy Hofmann Co-authored-by: Lars Göttgens --- src/NumberTheory/NmbThy.jl | 22 +++++++++++----------- test/NumberTheory/nmbthy.jl | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/NumberTheory/NmbThy.jl b/src/NumberTheory/NmbThy.jl index 2cffd1ff3d00..140c6597161b 100644 --- a/src/NumberTheory/NmbThy.jl +++ b/src/NumberTheory/NmbThy.jl @@ -126,10 +126,10 @@ end norm_equation(R::AbsNumFieldOrder, k::Base.Integer; abs::Bool = false) = norm_equation(R, ZZRingElem(k), abs = abs) -function norm_equation_fac_elem(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem,Hecke.AbsSimpleNumFieldOrderFractionalIdeal}, a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}) +function norm_equation_fac_elem(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem,Hecke.AbsSimpleNumFieldOrderFractionalIdeal}, a::AbsSimpleNumFieldOrderElem) @assert Hecke.is_maximal(R) - Ka, mKa, mkK = absolute_field(Hecke.nf(R)) + Ka, mKa, mkK = collapse_top_layer(Hecke.nf(R)) Ra = maximal_order(Ka) class_group(Ra) k = Hecke.nf(parent(a)) @@ -144,9 +144,9 @@ function norm_equation_fac_elem(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem, q, mms = snf(q) mq = mq*inv(mms) - C = vcat([matrix(ZZ, 1, ngens(q), [valuation(mS(preimage(mq, q[i])), p) for i=1:ngens(q)]) for p = keys(lp)]) + C = reduce(vcat, (matrix(ZZ, 1, ngens(q), [valuation(mS(preimage(mq, q[i])), p) for i=1:ngens(q)]) for p = keys(lp))) - A = vcat([matrix(ZZ, 1, ngens(q), [valuation(norm(mkK, mS(preimage(mq, g))), p) for g in gens(q)]) for p = keys(la)]) + A = reduce(vcat, (matrix(ZZ, 1, ngens(q), [valuation(norm(mkK, mS(preimage(mq, g))), p) for g in gens(q)]) for p = keys(la))) b = matrix(ZZ, length(la), 1, [valuation(a, p) for p = keys(la)]) so = solve_mixed(A, b, C) @@ -168,9 +168,9 @@ function norm_equation_fac_elem(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem, return sol end -norm_equation(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem,Hecke.AbsSimpleNumFieldOrderFractionalIdeal}, a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}) = map(x -> R(evaluate(x)), norm_equation_fac_elem(R, a)) +norm_equation(R::Hecke.RelNumFieldOrder{AbsSimpleNumFieldElem,Hecke.AbsSimpleNumFieldOrderFractionalIdeal}, a::AbsSimpleNumFieldOrderElem) = map(x -> R(evaluate(x)), norm_equation_fac_elem(R, a)) -function is_irreducible(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}) +function is_irreducible(a::AbsSimpleNumFieldOrderElem) if iszero(a) return false end @@ -206,11 +206,11 @@ function is_irreducible(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFi end @doc raw""" - irreducibles(S::Vector{AbsNumFieldOrderIdeal{AbsSimpleNumField,AbsSimpleNumFieldElem}}) -> Vector{AbsNumFieldOrderElem} + irreducibles(S::Vector{AbsSimpleNumFieldOrderIdeal}) -> Vector{AbsNumFieldOrderElem} Return all irreducibles whose support is contained in $S$. """ -function irreducibles(S::Vector{AbsNumFieldOrderIdeal{AbsSimpleNumField,AbsSimpleNumFieldElem}}) +function irreducibles(S::Vector{AbsSimpleNumFieldOrderIdeal}) if length(S) == 0 return [] end @@ -268,11 +268,11 @@ end =# @doc raw""" - factorizations(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}) -> Vector{Fac{OrdElem}} + factorizations(a::AbsSimpleNumFieldOrderElem) -> Vector{Fac{OrdElem}} Return all factorizations of $a$ into irreducibles. """ -function factorizations(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}) +function factorizations(a::AbsSimpleNumFieldOrderElem) O = parent(a) S = collect(keys(factor(a*O))) if length(S) == 0 @@ -302,7 +302,7 @@ function factorizations(a::AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFi end end sol = solve_non_negative(A, b) - res = Fac{AbsNumFieldOrderElem{AbsSimpleNumField,AbsSimpleNumFieldElem}}[] + res = Fac{AbsSimpleNumFieldOrderElem}[] for j=1:nrows(sol) x = Dict{typeof(a), Int}() y = a diff --git a/test/NumberTheory/nmbthy.jl b/test/NumberTheory/nmbthy.jl index f4e628df5ccc..bc6790ac17a3 100644 --- a/test/NumberTheory/nmbthy.jl +++ b/test/NumberTheory/nmbthy.jl @@ -4,7 +4,8 @@ using Test function evalu(x::Fac) return x.unit * prod(p*k for (p,k) = x.fac) end -@testset "Polymake.factorizations" begin + +@testset "factorizations" begin k, a = quadratic_field(-5) zk = maximal_order(k) f = factorizations(zk(6)) @@ -12,7 +13,7 @@ end @test all(x -> evalu(x) == 6, f) end -@testset "Polymake.norm_equation" begin +@testset "norm_equation.absolute" begin k, a = wildanger_field(3, 13) zk = maximal_order(k) na = norm(rand(zk, 1:10)) @@ -20,6 +21,18 @@ end @test all(x->norm(x) == na, s) end +@testset "norm_equation.relative" begin + L, _ = quadratic_field(-1) + _, x = L[:x] + f = x^3 - 2; + k, _ = number_field(f) + + zk = maximal_order(k) + na = norm(rand(zk, 10)) + s = norm_equation(zk, na) + @test all(x->norm(x) == na, s) +end + @testset "DiscreteLog" begin F = GF(3,4); From f7079a77dd57a3373a21b0ff6d1c559566f8095c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 13 Nov 2024 08:47:23 +0100 Subject: [PATCH 57/84] docs: uniform DocTestSetup in more .md files (#4301) --- .../AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md | 5 +---- .../AlgebraicSets/ProjectiveAlgebraicSet.md | 5 +---- .../AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md | 5 +---- .../AlgebraicVarieties/ProjectiveVariety.md | 5 +---- docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md | 5 +---- .../AlgebraicGeometry/Schemes/RationalPointsProjective.md | 5 +---- experimental/AlgebraicStatistics/docs/src/phylogenetics.md | 1 + .../docs/src/advice_for_the_programmer.md | 5 +++++ .../DoubleAndHyperComplexes/docs/src/user_interface.md | 2 ++ experimental/FTheoryTools/docs/src/g4.md | 1 + experimental/FTheoryTools/docs/src/generalities.md | 1 + experimental/FTheoryTools/docs/src/hypersurface.md | 1 + experimental/FTheoryTools/docs/src/introduction.md | 1 + experimental/FTheoryTools/docs/src/literature.md | 1 + experimental/FTheoryTools/docs/src/tate.md | 1 + experimental/FTheoryTools/docs/src/weierstrass.md | 1 + experimental/GroebnerWalk/docs/src/introduction.md | 1 + experimental/GroebnerWalk/docs/src/special-ideals.md | 1 + experimental/IntersectionTheory/docs/src/AbstractBundles.md | 1 + .../IntersectionTheory/docs/src/AbstractVarietyMaps.md | 1 + experimental/IntersectionTheory/docs/src/BlowUps.md | 1 + experimental/IntersectionTheory/docs/src/BottFormulas.md | 1 + experimental/IntersectionTheory/docs/src/intro.md | 1 + experimental/IntersectionTheory/docs/src/schubert.md | 1 + experimental/LieAlgebras/docs/src/cartan_matrix.md | 6 ++---- experimental/LinearQuotients/docs/src/cox_rings.md | 1 + experimental/LinearQuotients/docs/src/introduction.md | 1 + experimental/LinearQuotients/docs/src/linear_quotients.md | 1 + .../OrthogonalDiscriminants/docs/src/introduction.md | 1 + .../PartitionedPermutations/docs/src/introduction.md | 1 + experimental/QuadFormAndIsom/docs/src/enumeration.md | 1 + experimental/QuadFormAndIsom/docs/src/introduction.md | 5 +++++ experimental/QuadFormAndIsom/docs/src/latwithisom.md | 1 + experimental/QuadFormAndIsom/docs/src/primembed.md | 1 + experimental/QuadFormAndIsom/docs/src/spacewithisom.md | 1 + 35 files changed, 45 insertions(+), 28 deletions(-) diff --git a/docs/src/AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md b/docs/src/AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md index e32762182099..0b9e522514dc 100644 --- a/docs/src/AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md +++ b/docs/src/AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Affine Algebraic Sets diff --git a/docs/src/AlgebraicGeometry/AlgebraicSets/ProjectiveAlgebraicSet.md b/docs/src/AlgebraicGeometry/AlgebraicSets/ProjectiveAlgebraicSet.md index f127036cb3df..f7b0b60bb1aa 100644 --- a/docs/src/AlgebraicGeometry/AlgebraicSets/ProjectiveAlgebraicSet.md +++ b/docs/src/AlgebraicGeometry/AlgebraicSets/ProjectiveAlgebraicSet.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Projective Algebraic Sets diff --git a/docs/src/AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md b/docs/src/AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md index c79b8d363b4c..9c4a2b29f919 100644 --- a/docs/src/AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md +++ b/docs/src/AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Affine Varieties diff --git a/docs/src/AlgebraicGeometry/AlgebraicVarieties/ProjectiveVariety.md b/docs/src/AlgebraicGeometry/AlgebraicVarieties/ProjectiveVariety.md index c754a8878ce2..720f2fff00a9 100644 --- a/docs/src/AlgebraicGeometry/AlgebraicVarieties/ProjectiveVariety.md +++ b/docs/src/AlgebraicGeometry/AlgebraicVarieties/ProjectiveVariety.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Projective Varieties diff --git a/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md b/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md index 428864035015..de0b6480c16f 100644 --- a/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md +++ b/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Rational Points on Affine Schemes diff --git a/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md b/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md index 0c672102d644..8960a3ea0930 100644 --- a/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md +++ b/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md @@ -1,9 +1,6 @@ ```@meta CurrentModule = Oscar -``` - -```@setup oscar -using Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Rational Points on Projective Schemes diff --git a/experimental/AlgebraicStatistics/docs/src/phylogenetics.md b/experimental/AlgebraicStatistics/docs/src/phylogenetics.md index 77d8d9c36ff1..d27b4f269c64 100644 --- a/experimental/AlgebraicStatistics/docs/src/phylogenetics.md +++ b/experimental/AlgebraicStatistics/docs/src/phylogenetics.md @@ -2,6 +2,7 @@ CurrentModule = Oscar DocTestSetup = Oscar.doctestsetup() ``` + # Algebraic Phylogenetics The Algebraic Phylogenetics part of OSCAR provides functionality for diff --git a/experimental/DoubleAndHyperComplexes/docs/src/advice_for_the_programmer.md b/experimental/DoubleAndHyperComplexes/docs/src/advice_for_the_programmer.md index fa240099606b..709b13d0f9b1 100644 --- a/experimental/DoubleAndHyperComplexes/docs/src/advice_for_the_programmer.md +++ b/experimental/DoubleAndHyperComplexes/docs/src/advice_for_the_programmer.md @@ -1,3 +1,8 @@ +```@meta +CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() +``` + # Advice for the programmer ## How to implement my custom double complex? diff --git a/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md b/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md index c96bf85fb017..691c34d6020a 100644 --- a/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md +++ b/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md @@ -1,6 +1,8 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` + # Double complexes -- the user's interface We briefly review the mathematical notion of a double complex. Let ``\mathcal A`` be an Abelian category. A double complex diff --git a/experimental/FTheoryTools/docs/src/g4.md b/experimental/FTheoryTools/docs/src/g4.md index 47e3b0b00fe8..ce69a5483f7a 100644 --- a/experimental/FTheoryTools/docs/src/g4.md +++ b/experimental/FTheoryTools/docs/src/g4.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # G4-Fluxes diff --git a/experimental/FTheoryTools/docs/src/generalities.md b/experimental/FTheoryTools/docs/src/generalities.md index 007287ac2f46..579577a57a5f 100644 --- a/experimental/FTheoryTools/docs/src/generalities.md +++ b/experimental/FTheoryTools/docs/src/generalities.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Functionality for all F-theory models diff --git a/experimental/FTheoryTools/docs/src/hypersurface.md b/experimental/FTheoryTools/docs/src/hypersurface.md index 905a868e24ed..329b67a6d9d5 100644 --- a/experimental/FTheoryTools/docs/src/hypersurface.md +++ b/experimental/FTheoryTools/docs/src/hypersurface.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Hypersurface models diff --git a/experimental/FTheoryTools/docs/src/introduction.md b/experimental/FTheoryTools/docs/src/introduction.md index 2a0102c31170..c30d217dea4f 100644 --- a/experimental/FTheoryTools/docs/src/introduction.md +++ b/experimental/FTheoryTools/docs/src/introduction.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Welcome to FTheoryTools diff --git a/experimental/FTheoryTools/docs/src/literature.md b/experimental/FTheoryTools/docs/src/literature.md index 3450523bdf87..ddc1ee0f58a1 100644 --- a/experimental/FTheoryTools/docs/src/literature.md +++ b/experimental/FTheoryTools/docs/src/literature.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Literature constructions diff --git a/experimental/FTheoryTools/docs/src/tate.md b/experimental/FTheoryTools/docs/src/tate.md index b48f59b4f072..946084fc10f3 100644 --- a/experimental/FTheoryTools/docs/src/tate.md +++ b/experimental/FTheoryTools/docs/src/tate.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Global Tate models diff --git a/experimental/FTheoryTools/docs/src/weierstrass.md b/experimental/FTheoryTools/docs/src/weierstrass.md index 008aa1e50b6c..9b423261ea9c 100644 --- a/experimental/FTheoryTools/docs/src/weierstrass.md +++ b/experimental/FTheoryTools/docs/src/weierstrass.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Weierstrass models diff --git a/experimental/GroebnerWalk/docs/src/introduction.md b/experimental/GroebnerWalk/docs/src/introduction.md index 2583cb9056cd..de159085f9d3 100644 --- a/experimental/GroebnerWalk/docs/src/introduction.md +++ b/experimental/GroebnerWalk/docs/src/introduction.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Usage diff --git a/experimental/GroebnerWalk/docs/src/special-ideals.md b/experimental/GroebnerWalk/docs/src/special-ideals.md index 6c58ef1fe75a..9ca4066b54e1 100644 --- a/experimental/GroebnerWalk/docs/src/special-ideals.md +++ b/experimental/GroebnerWalk/docs/src/special-ideals.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Special ideals used for benchmarking diff --git a/experimental/IntersectionTheory/docs/src/AbstractBundles.md b/experimental/IntersectionTheory/docs/src/AbstractBundles.md index d3e70a15f804..0e54dc3c17ed 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractBundles.md +++ b/experimental/IntersectionTheory/docs/src/AbstractBundles.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Abstract Bundles diff --git a/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md b/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md index 0612ae170fa0..830fdebe618d 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md +++ b/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Abstract Variety Maps diff --git a/experimental/IntersectionTheory/docs/src/BlowUps.md b/experimental/IntersectionTheory/docs/src/BlowUps.md index 0fa72b3be318..8032f3f39c77 100644 --- a/experimental/IntersectionTheory/docs/src/BlowUps.md +++ b/experimental/IntersectionTheory/docs/src/BlowUps.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Blowups diff --git a/experimental/IntersectionTheory/docs/src/BottFormulas.md b/experimental/IntersectionTheory/docs/src/BottFormulas.md index 941ab97232bf..501f6e677d27 100644 --- a/experimental/IntersectionTheory/docs/src/BottFormulas.md +++ b/experimental/IntersectionTheory/docs/src/BottFormulas.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Bott Formulas diff --git a/experimental/IntersectionTheory/docs/src/intro.md b/experimental/IntersectionTheory/docs/src/intro.md index 7b546bf82902..7ab02028622a 100644 --- a/experimental/IntersectionTheory/docs/src/intro.md +++ b/experimental/IntersectionTheory/docs/src/intro.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Introduction diff --git a/experimental/IntersectionTheory/docs/src/schubert.md b/experimental/IntersectionTheory/docs/src/schubert.md index ffd1f54102f1..47944cdbbc89 100644 --- a/experimental/IntersectionTheory/docs/src/schubert.md +++ b/experimental/IntersectionTheory/docs/src/schubert.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Schubert Calculus diff --git a/experimental/LieAlgebras/docs/src/cartan_matrix.md b/experimental/LieAlgebras/docs/src/cartan_matrix.md index 2ca1b72d53b8..4f15e3cf32c9 100644 --- a/experimental/LieAlgebras/docs/src/cartan_matrix.md +++ b/experimental/LieAlgebras/docs/src/cartan_matrix.md @@ -1,8 +1,6 @@ ```@meta CurrentModule = Oscar -DocTestSetup = quote -using Oscar -end +DocTestSetup = Oscar.doctestsetup() ``` # Cartan Matrices @@ -15,4 +13,4 @@ cartan_symmetrizer(::ZZMatrix; check::Bool) cartan_bilinear_form(::ZZMatrix; check::Bool) cartan_type(::ZZMatrix; check::Bool) cartan_type_with_ordering(::ZZMatrix; check::Bool) -``` \ No newline at end of file +``` diff --git a/experimental/LinearQuotients/docs/src/cox_rings.md b/experimental/LinearQuotients/docs/src/cox_rings.md index d1e7d0123214..561f9353339c 100644 --- a/experimental/LinearQuotients/docs/src/cox_rings.md +++ b/experimental/LinearQuotients/docs/src/cox_rings.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Cox rings diff --git a/experimental/LinearQuotients/docs/src/introduction.md b/experimental/LinearQuotients/docs/src/introduction.md index 36199c781961..6cea3b695daf 100644 --- a/experimental/LinearQuotients/docs/src/introduction.md +++ b/experimental/LinearQuotients/docs/src/introduction.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Introduction diff --git a/experimental/LinearQuotients/docs/src/linear_quotients.md b/experimental/LinearQuotients/docs/src/linear_quotients.md index 61f8fd288b16..79ca7301cf6a 100644 --- a/experimental/LinearQuotients/docs/src/linear_quotients.md +++ b/experimental/LinearQuotients/docs/src/linear_quotients.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Construction and basic functionality diff --git a/experimental/OrthogonalDiscriminants/docs/src/introduction.md b/experimental/OrthogonalDiscriminants/docs/src/introduction.md index d7c9a8f27abb..7c02bf553d71 100644 --- a/experimental/OrthogonalDiscriminants/docs/src/introduction.md +++ b/experimental/OrthogonalDiscriminants/docs/src/introduction.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Introduction diff --git a/experimental/PartitionedPermutations/docs/src/introduction.md b/experimental/PartitionedPermutations/docs/src/introduction.md index 7893dd848568..439ae48e34ff 100644 --- a/experimental/PartitionedPermutations/docs/src/introduction.md +++ b/experimental/PartitionedPermutations/docs/src/introduction.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Partitioned Permutations diff --git a/experimental/QuadFormAndIsom/docs/src/enumeration.md b/experimental/QuadFormAndIsom/docs/src/enumeration.md index 1925c96243e1..444c183afbb9 100644 --- a/experimental/QuadFormAndIsom/docs/src/enumeration.md +++ b/experimental/QuadFormAndIsom/docs/src/enumeration.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Enumeration of isometries diff --git a/experimental/QuadFormAndIsom/docs/src/introduction.md b/experimental/QuadFormAndIsom/docs/src/introduction.md index fc96d4cc147b..f1fb978c0fb2 100644 --- a/experimental/QuadFormAndIsom/docs/src/introduction.md +++ b/experimental/QuadFormAndIsom/docs/src/introduction.md @@ -1,3 +1,8 @@ +```@meta +CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() +``` + # Quadratic forms and isometries This project is a complement to the code about *hermitian lattices* available diff --git a/experimental/QuadFormAndIsom/docs/src/latwithisom.md b/experimental/QuadFormAndIsom/docs/src/latwithisom.md index 7fb9347224c8..98abc956b8f5 100644 --- a/experimental/QuadFormAndIsom/docs/src/latwithisom.md +++ b/experimental/QuadFormAndIsom/docs/src/latwithisom.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Lattices with isometry diff --git a/experimental/QuadFormAndIsom/docs/src/primembed.md b/experimental/QuadFormAndIsom/docs/src/primembed.md index 3bf01561bdde..4343a00f7d99 100644 --- a/experimental/QuadFormAndIsom/docs/src/primembed.md +++ b/experimental/QuadFormAndIsom/docs/src/primembed.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` We introduce here the necessary definitions and results which lie behind the diff --git a/experimental/QuadFormAndIsom/docs/src/spacewithisom.md b/experimental/QuadFormAndIsom/docs/src/spacewithisom.md index 91f8ddbb423d..d65e79d41505 100644 --- a/experimental/QuadFormAndIsom/docs/src/spacewithisom.md +++ b/experimental/QuadFormAndIsom/docs/src/spacewithisom.md @@ -1,5 +1,6 @@ ```@meta CurrentModule = Oscar +DocTestSetup = Oscar.doctestsetup() ``` # Quadratic spaces with isometry From d0984f8b096e5d70854c8e011aa08ed351ea5791 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 13 Nov 2024 09:23:01 +0100 Subject: [PATCH 58/84] is_left and is_right for SubgroupTransversal (#4298) Also use is_left/is_right for those and cosets --- src/Groups/cosets.jl | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index 61cfc96b8477..e637a543d01f 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -47,7 +47,7 @@ function ==(C1::GroupCoset, C2::GroupCoset) end function Base.show(io::IO, ::MIME"text/plain", x::GroupCoset) - side = x.side === :left ? "Left" : "Right" + side = is_left(x) ? "Left" : "Right" io = pretty(io) println(io, "$side coset of ", Lowercase(), x.H) print(io, Indent()) @@ -57,7 +57,7 @@ function Base.show(io::IO, ::MIME"text/plain", x::GroupCoset) end function Base.show(io::IO, x::GroupCoset) - side = x.side === :left ? "Left" : "Right" + side = is_left(x) ? "Left" : "Right" if is_terse(io) print(io, "$side coset of a group") else @@ -146,7 +146,7 @@ Base.:*(g::GAPGroupElem, H::GAPGroup) = left_coset(H,g) function Base.:*(c::GroupCoset, y::GAPGroupElem) @assert y in c.G "element not in the group" - if c.side == :right + if is_right(c) return right_coset(c.H, representative(c)*y) else return left_coset(c.H^y, representative(c)*y) @@ -155,7 +155,7 @@ end function Base.:*(y::GAPGroupElem, c::GroupCoset) @assert y in c.G "element not in the group" - if c.side == :left + if is_left(c) return left_coset(c.H, y*representative(c)) else return right_coset(c.H^(y^-1), y*representative(c)) @@ -163,7 +163,7 @@ function Base.:*(y::GAPGroupElem, c::GroupCoset) end function Base.:*(c::GroupCoset, d::GroupCoset) - @req (c.side == :right && d.side == :left) "Wrong input" + @req (is_right(c) && is_left(d)) "Wrong input" return double_coset(c.H, representative(c)*representative(d), d.H) end @@ -325,11 +325,14 @@ end SubgroupTransversal{T<: GAPGroup, S<: GAPGroup, E<: GAPGroupElem} Type of left/right transversals of subgroups in groups. -The elements are encoded via a right transversal object in GAP. -(Note that GAP does not support left transversals.) Objects of this type are created by [`right_transversal`](@ref) and [`left_transversal`](@ref). + +# Note for developers + +The elements are encoded via a right transversal object in GAP. +(Note that GAP does not support left transversals.) """ struct SubgroupTransversal{T<: GAPGroup, S<: GAPGroup, E<: GAPGroupElem} <: AbstractVector{E} G::T # big group containing the subgroup @@ -341,7 +344,7 @@ end GAP.@install GapObj(T::SubgroupTransversal) = T.X function Base.show(io::IO, ::MIME"text/plain", x::SubgroupTransversal) - side = x.side === :left ? "Left" : "Right" + side = is_left(x) ? "Left" : "Right" println(io, "$side transversal of length $(length(x)) of") io = pretty(io) print(io, Indent()) @@ -351,7 +354,7 @@ function Base.show(io::IO, ::MIME"text/plain", x::SubgroupTransversal) end function Base.show(io::IO, x::SubgroupTransversal) - side = x.side === :left ? "Left" : "Right" + side = is_left(x) ? "Left" : "Right" if is_terse(io) print(io, "$side transversal of groups") else @@ -361,13 +364,17 @@ function Base.show(io::IO, x::SubgroupTransversal) end end +is_left(x::SubgroupTransversal) = x.side == :left + +is_right(x::SubgroupTransversal) = x.side == :right + Base.hash(x::SubgroupTransversal, h::UInt) = h # FIXME Base.length(T::SubgroupTransversal) = index(Int, T.G, T.H) function Base.getindex(T::SubgroupTransversal, i::Int) res = group_element(T.G, GapObj(T)[i]) - if T.side === :left + if is_left(T) res = inv(res) end return res From 25d1dd60edf12d8c2b32181927d174650e287295 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Wed, 13 Nov 2024 12:24:22 +0100 Subject: [PATCH 59/84] more functionality for collector objects (#4157) * more functionality for collector objects create an Oscar collector from a GAP collector * add `get_relative_order`, `get_power`, `get_conjugate` * re-add the use of `number_of_small_groups` * fix `get_conjugate` for unassigned value --- src/Groups/pcgroup.jl | 177 ++++++++++++++++++++++++++++++++++++++++- src/exports.jl | 4 + test/Groups/pcgroup.jl | 10 +++ 3 files changed, 188 insertions(+), 3 deletions(-) diff --git a/src/Groups/pcgroup.jl b/src/Groups/pcgroup.jl index 923170798e44..56b231a7404b 100644 --- a/src/Groups/pcgroup.jl +++ b/src/Groups/pcgroup.jl @@ -51,7 +51,7 @@ true collector(n::Int, ::Type{T} = ZZRingElem) where T <: IntegerUnion = GAP_Collector{T}(n) -# Provide functions for entering data into the collector. +# Provide functions for entering data into the collector and accessing data. # utility: # Convert a vector of pairs to a generator-exponent vector in GAP. @@ -98,13 +98,36 @@ function set_relative_order!(c::Collector{T}, i::Int, relord::T) where T <: Inte end end +""" + get_relative_order(c::Collector{T}, i::Int) where T <: IntegerUnion + +Get the relative order of the `i`-th generator of `c`. + +# Examples +```jldoctest +julia> c = collector(2, Int); + +julia> get_relative_order(c, 1) +0 + +julia> set_relative_order!(c, 1, 2) + +julia> get_relative_order(c, 1) +2 +``` +""" +function get_relative_order(c::Collector{T}, i::Int) where T <: IntegerUnion + @req (0 < i && i <= c.ngens) "the collector has only $(c.ngens) generators not $i" + return c.relorders[i] +end + """ set_relative_orders!(c::Collector{T}, relords::Vector{T}) Set all relative orders of the generators of `c`, where the length of `relords` must be equal to the number of generators of `c`, and `relords[i]` denotes the relative order of the `i`-th generator. -which must be either `0` (meaning infinite order) or a positive integer.. +which must be either `0` (meaning infinite order) or a positive integer. # Examples ```jldoctest @@ -131,6 +154,31 @@ function set_relative_orders!(c::Collector{T}, relords::Vector{T}) where T <: In end end +""" + get_relative_orders(c::Collector{T}) + +Get the `Vector{T}` of all relative orders of the generators of `c`. + +# Examples +```jldoctest +julia> c = collector(2); + +julia> get_relative_orders(c) +2-element Vector{ZZRingElem}: + 0 + 0 + +julia> set_relative_orders!(c, ZZRingElem[2, 0]) + +julia> get_relative_orders(c) +2-element Vector{ZZRingElem}: + 2 + 0 +""" +function get_relative_orders(c::Collector{T}) where T <: IntegerUnion + return c.relorders +end + """ set_power!(c::Collector{T}, i::Int, rhs::Vector{Pair{Int, T}}) where T <: IntegerUnion @@ -165,6 +213,35 @@ function set_power!(c::Collector{T}, i::Int, rhs::Vector{Pair{Int, T}}) where T end end +""" + get_power(c::Collector{T}, i::Int) where T <: IntegerUnion + +Get the `Vector{Pair{Int, T}}` that describes the `c.relorders[i]`-th power +of the `i`-th generator of `c`. + +# Examples +```jldoctest +julia> c = collector(2, Int); + +julia> set_relative_order!(c, 1, 2) + +julia> set_relative_order!(c, 2, 3) + +julia> get_power(c, 1) +Pair{Int64, Int64}[] + +julia> set_power!(c, 1, [2 => 1]) + +julia> get_power(c, 1) +1-element Vector{Pair{Int64, Int64}}: + 2 => 1 +``` +""" +function get_power(c::Collector{T}, i::Int) where T <: IntegerUnion + @req 0 < i <= c.ngens "the collector has only $(c.ngens) generators not $i" + return c.powers[i] +end + """ set_conjugate!(c::Collector{T}, j::Int, i::Int, rhs::Vector{Pair{Int, T}}) where T <: IntegerUnion @@ -199,6 +276,36 @@ function set_conjugate!(c::Collector{T}, j::Int, i::Int, rhs::Vector{Pair{Int, T end end +""" + get_conjugate(c::Collector{T}, j::Int, i::Int) where T <: IntegerUnion + +Get the `Vector{Pair{Int, T}}` that describes the conjugate of the `j`-th +generator of `c` by the `i`-th generator of `c`, for `i < j`. + +# Examples +```jldoctest +julia> c = collector(2, Int); + +julia> set_relative_orders!(c, [2, 3]) + +julia> get_conjugate(c, 2, 1) +1-element Vector{Pair{Int64, Int64}}: + 2 => 1 + +julia> set_conjugate!(c, 2, 1, [2 => 2]) + +julia> get_conjugate(c, 2, 1) +1-element Vector{Pair{Int64, Int64}}: + 2 => 2 +``` +""" +function get_conjugate(c::Collector{T}, j::Int, i::Int) where T <: IntegerUnion + @req 0 < i <= c.ngens "the collector has only $(c.ngens) generators not $i" + @req i < j "only for i < j, but i = $i, j = $j" + conj = c.conjugates + return isassigned(conj, i, j) ? conj[i, j] : [j => T(1)] +end + """ set_commutator!(c::Collector{T}, j::Int, i::Int, rhs::Vector{Pair{Int, T}}) where T <: IntegerUnion @@ -215,7 +322,7 @@ julia> set_commutator!(c, 2, 1, [2 => 1]) ``` """ function set_commutator!(c::Collector{T}, j::Int, i::Int, rhs::Vector{Pair{Int, T}}) where T <: IntegerUnion - @req 0 < i <= c.ngens "the collector has only $(c.ngens) generators not $i" + @req 0 < j <= c.ngens "the collector has only $(c.ngens) generators not $j" @req i < j "only for i < j, but i = $i, j = $j" if length(rhs) > 0 && rhs[1].first == j # freely reduce @@ -365,3 +472,67 @@ function pc_group(c::GAP_Collector) end end + +# Create an Oscar collector from a GAP collector. + +""" + collector([::Type{T} = ZZRingElem, ]G::PcGroup) where T <: IntegerUnion + +Return a collector object for `G`. + +# Examples +```jldoctest +julia> g = small_group(12, 3) +Pc group of order 12 + +julia> c = collector(g); + +julia> gc = pc_group(c) +Pc group of order 12 + +julia> is_isomorphic(g, gc) +true +``` +""" +function collector(::Type{T}, G::PcGroup) where T <: IntegerUnion + Fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(GapObj(G))) + GapC = GAP.getbangproperty(Fam, :rewritingSystem)::GapObj + + n = GAP.getbangindex(GapC, 3)::Int + c = collector(n, T) + + c.relorders = Vector{T}(GAP.getbangindex(GapC, 6)::GapObj) + + Gap_powers = GAP.gap_to_julia(GAP.getbangindex(GapC, 7)::GapObj, recursive = false) + for i in 1:length(Gap_powers) + if Gap_powers[i] !== nothing + l = GAPWrap.ExtRepOfObj(Gap_powers[i]) + c.powers[i] = Pair{Int,T}[l[k-1] => T(l[k]) for k in 2:2:length(l)] + end + end + + Gap_conj = GAP.gap_to_julia(GAP.getbangindex(GapC, 8)::GapObj, recursive = false) + for i in 1:length(Gap_conj) + Gap_conj_i = GAP.gap_to_julia(Gap_conj[i]::GapObj, recursive = false) + for j in 1:length(Gap_conj_i) + if Gap_conj_i[j] !== nothing + l = GAPWrap.ExtRepOfObj(Gap_conj_i[j]) + c.conjugates[j,i] = Pair{Int,T}[l[k-1] => T(l[k]) for k in 2:2:length(l)] + end + end + end + +#TODO: deal also with the from-the-left-collector from the Polycyclic package + +# c.X = GapC +# c.F = FPGroup(GAP.getbangproperty(GAP.getbangindex(GapC, 1)::GapObj, :freeGroup)::GapObj) +#TODO: Set these known data. +# Currently this does not work because somehow `GroupByRws` +# requires a *mutable* GAP collector, and `GapC` is immutable. +# (Change `pc_group` to not call `GroupByRWS` in this case? +# Forbid `set_power!` etc. in this case?) + + return c +end + +collector(G::PcGroup) = collector(ZZRingElem, G) diff --git a/src/exports.jl b/src/exports.jl index ff4e43fb6eef..4f855fc39f9c 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -618,6 +618,10 @@ export gens, has_gens export gens_of_rational_equivalence_classes export geometric_genus export geometric_irreducible_components +export get_conjugate +export get_power +export get_relative_order +export get_relative_orders export getindex_safe export girth export gkz_vector diff --git a/test/Groups/pcgroup.jl b/test/Groups/pcgroup.jl index 8ec1f2c3cd3c..983446ed8011 100644 --- a/test/Groups/pcgroup.jl +++ b/test/Groups/pcgroup.jl @@ -82,3 +82,13 @@ end @test GAP.Globals.IsMutable(cgg) @test cgg !== c.X end + +@testset "create collectors from polycyclic groups" begin + for i in rand(1:number_of_small_groups(96), 10) + g = small_group(96, i) + c = collector(Int64, g) + gc = pc_group(c) + f = hom(g, gc, gens(gc)) + @test is_bijective(f) + end +end From d15b0c94bc16c002a9334f7ed998aca48e17f8f5 Mon Sep 17 00:00:00 2001 From: Aaruni Kaushik Date: Wed, 13 Nov 2024 14:03:46 +0100 Subject: [PATCH 60/84] Patch search index (#4292) * Patch search index This removes anything in search index that is not also present in doc.main. That is, the search function will not return "hidden" pages, which are not available via normal navigation. * Move code to the right place, so its trigerred not just on interactive use * Add JSON to docs Project.toml * docs/search: improve doc.main parsing, use .html or / depending on local_build --------- Co-authored-by: Benjamin Lorenz --- docs/Project.toml | 2 ++ docs/make_work.jl | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index 07d7ae149a80..6dad0075f817 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,7 +1,9 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" [compat] Documenter = "1.1" DocumenterCitations = "~1.3.4" +JSON = "0.21.4" diff --git a/docs/make_work.jl b/docs/make_work.jl index b3a0aec16741..91efab27a67c 100644 --- a/docs/make_work.jl +++ b/docs/make_work.jl @@ -4,7 +4,7 @@ # module BuildDoc -using Documenter, DocumenterCitations +using Documenter, DocumenterCitations, JSON include("documenter_helpers.jl") include("citation_style.jl") @@ -210,6 +210,56 @@ function doit( dstbase = normpath(Oscar.oscardir, "docs", "src", string(nameof(pkg))) rm(dstbase; recursive=true, force=true) end + + # postprocessing, for the search index + docspath = normpath(joinpath(Oscar.oscardir, "docs")) + @info "Patching search index." + # extract valid json from search_index.js + run(pipeline(`sed -n '2p;3q' $(joinpath(docspath, "build", "search_index.js"))`, stdout=(joinpath(docspath, "build", "search_index.json")))) # imperfect file, but JSON parses it + + # extract paths from doc.main + filelist=String[] + docmain = include(joinpath(docspath, "doc.main")) + while !isempty(docmain) + n = pop!(docmain) + if n isa Pair + push!(docmain, last(n)) + elseif n isa String + push!(filelist, n) + elseif n isa Array{String} + append!(filelist,n) + elseif n isa Array + append!(docmain,n) + else + error("err: $(typeof(n))") + end + end + suffix = local_build ? ".html" : "/" + filelist = replace.(filelist, r"\.md$"=>suffix) + + # read these files + iosearchindex = open(joinpath(docspath, "build", "search_index.json"), "r") + searchindex = JSON.parse(iosearchindex) + close(iosearchindex) + + newsearchindex = [] + + for item in searchindex + if split(item["location"], "#")[1] in filelist + push!(newsearchindex, item) + end + end + + + # combine this to valid javascript again, and overwrite input + ionewsearchindex = open(joinpath(docspath, "build", "search_index.js"), "w") + write(ionewsearchindex, """var documenterSearchIndex = {"docs":\n""") + JSON.print(ionewsearchindex, newsearchindex) + write(ionewsearchindex, "\n}") + close(ionewsearchindex) + + # clean up + rm(joinpath(docspath, "build", "search_index.json")) end end # module BuildDoc From 829ad412c71368e8b67489cc87975a426616fba6 Mon Sep 17 00:00:00 2001 From: Martin Bies Date: Wed, 13 Nov 2024 16:00:16 +0100 Subject: [PATCH 61/84] [ToricVarieties] Add hash function for NormalToricVariety (#4306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Göttgens --- .../ToricVarieties/NormalToricVarieties/constructors.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl index 507d2c082daf..8ee9acd8f07a 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl @@ -187,6 +187,10 @@ function Base.:(==)(tv1::NormalToricVariety, tv2::NormalToricVariety) error("Equality of normal toric varieties is computationally very demanding. More details in the documentation.") end +function Base.hash(tv::NormalToricVariety, h::UInt) + return hash(objectid(tv), h) +end + ###################### From c0c07a74ec159e6d172fafb7d7ffe4a74da551c4 Mon Sep 17 00:00:00 2001 From: Wolfram Decker Date: Wed, 13 Nov 2024 17:30:24 +0100 Subject: [PATCH 62/84] Intersection theory: hom --> map, more docu (#4304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Intersection theory: hom --> map, more docu --------- Co-authored-by: Lars Göttgens --- .../docs/src/AbstractBundles.md | 8 ++ .../docs/src/AbstractVarietyMaps.md | 8 +- .../IntersectionTheory/docs/src/examples.md | 2 +- .../src/IntersectionTheory.jl | 8 +- experimental/IntersectionTheory/src/Main.jl | 126 +++++++++++++----- experimental/IntersectionTheory/src/blowup.jl | 6 +- .../IntersectionTheory/test/runtests.jl | 26 ++-- 7 files changed, 131 insertions(+), 53 deletions(-) diff --git a/experimental/IntersectionTheory/docs/src/AbstractBundles.md b/experimental/IntersectionTheory/docs/src/AbstractBundles.md index 0e54dc3c17ed..804a67e15334 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractBundles.md +++ b/experimental/IntersectionTheory/docs/src/AbstractBundles.md @@ -89,6 +89,14 @@ exterior_power(F::AbstractBundle, k::Int) symmetric_power(F::AbstractBundle, k::Int) ``` +```@docs +pullback(f::AbstractVarietyMap, F::AbstractBundle) +``` + +```@docs +pushforward(f::AbstractVarietyMap, F::AbstractBundle) +``` + ## Tests on Abstract Bundles ```@docs diff --git a/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md b/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md index 830fdebe618d..d2c3bf83c3aa 100644 --- a/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md +++ b/experimental/IntersectionTheory/docs/src/AbstractVarietyMaps.md @@ -8,7 +8,11 @@ DocTestSetup = Oscar.doctestsetup() ## Constructors ```@docs -hom(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") +map(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") +``` + +```@docs +identity_map(X::AbstractVariety) ``` ## Underlying Data of an Abstract Variety Map @@ -28,7 +32,7 @@ dim(f::AbstractVarietyMap) ``` ```@docs -pullback(f::AbstractVarietyMap, x::MPolyDecRingElem) +pullback(f::AbstractVarietyMap, y::MPolyDecRingElem) ``` ```@docs diff --git a/experimental/IntersectionTheory/docs/src/examples.md b/experimental/IntersectionTheory/docs/src/examples.md index dcc97a382f7e..7aa595197e25 100644 --- a/experimental/IntersectionTheory/docs/src/examples.md +++ b/experimental/IntersectionTheory/docs/src/examples.md @@ -65,7 +65,7 @@ h julia> H = gens(P5)[1] H -julia> i = hom(P2, P5, [2*h]) +julia> i = map(P2, P5, [2*h]) AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 julia> Bl, E, j = blowup(i) diff --git a/experimental/IntersectionTheory/src/IntersectionTheory.jl b/experimental/IntersectionTheory/src/IntersectionTheory.jl index 6354f3e43a17..ac34405a95a9 100644 --- a/experimental/IntersectionTheory/src/IntersectionTheory.jl +++ b/experimental/IntersectionTheory/src/IntersectionTheory.jl @@ -6,7 +6,7 @@ import ..Oscar: AffAlgHom, Ring, MPolyDecRingElem, symmetric_power, exterior_pow import ..Oscar: basis, betti_numbers, chow_ring, codomain, degree, det, dim, domain, dual, gens, hilbert_polynomial, hom, integral, rank, signature, partitions import ..Oscar.AbstractAlgebra: combinations import ..Oscar.AbstractAlgebra.Generic: FunctionalMap -import ..Oscar: pullback, pushforward, base, OO, product, compose +import ..Oscar: pullback, pushforward, base, OO, product, compose, identity_map, map import ..Oscar: trivial_line_bundle import ..Oscar: intersection_matrix import ..Oscar: chern_class @@ -41,10 +41,13 @@ export dual_basis export euler export euler_pairing export graph +export hom export hyperplane_class +export identity_map export l_genus export linear_subspaces_on_hypersurface export line_bundle +export map export OO export point_class export pontryagin_class @@ -120,11 +123,14 @@ export dual_basis export euler export euler_pairing export graph +export hom export hyperplane_class export intersection_matrix +export identity_map export l_genus export linear_subspaces_on_hypersurface export line_bundle +export map export OO export point_class export pontryagin_class diff --git a/experimental/IntersectionTheory/src/Main.jl b/experimental/IntersectionTheory/src/Main.jl index 8091e6d56c8e..36e04ad1f0aa 100644 --- a/experimental/IntersectionTheory/src/Main.jl +++ b/experimental/IntersectionTheory/src/Main.jl @@ -342,15 +342,15 @@ end # AbstractVarietyMap # @doc raw""" - hom(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") + map(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") Return an abstract variety map `X` $\rightarrow$ `Y` by specifying the pullbacks of the generators of the Chow ring of `Y`. !!! note - The corresponding pushforward can be automatically computed in certain cases. + The corresponding pushforward will be automatically computed in certain cases. -In case of an inclusion $i:X\hookrightarrow Y$ where the class of `X` is not +In the case of an inclusion `X` $\hookrightarrow$ `Y` where the class of `X` is not present in the Chow ring of `Y`, use the argument `inclusion = true`. Then, a copy of `Y` will be created, with extra classes added so that one can pushforward all classes on `X`. @@ -361,20 +361,29 @@ pushforward all classes on `X`. julia> P2xP2 = abstract_projective_space(2, symbol = "k")*abstract_projective_space(2, symbol = "l") AbstractVariety of dim 4 -julia> P8 = abstract_projective_space(8) -AbstractVariety of dim 8 - julia> k, l = gens(P2xP2) 2-element Vector{MPolyQuoRingElem{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}}}: k l -julia> Se = hom(P2xP2, P8, [k+l]) # Segre embedding +julia> P8 = abstract_projective_space(8) +AbstractVariety of dim 8 + +julia> h = gens(P8)[1] +h + +julia> Se = map(P2xP2, P8, [k+l]) # Segre embedding AbstractVarietyMap from AbstractVariety of dim 4 to AbstractVariety of dim 8 -``` +julia> pullback(Se, h) +k + l + +julia> pushforward(Se, k+l) +6*h^5 + +``` """ -function hom(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") +function map(X::AbstractVariety, Y::AbstractVariety, fˣ::Vector, fₓ = nothing; inclusion::Bool = false, symbol::String = "x") AbstractVarietyMap(X, Y, fˣ, fₓ) # !inclusion && return AbstractVarietyMap(X, Y, fˣ, fₓ) # _inclusion(AbstractVarietyMap(X, Y, fˣ), symbol=symbol) @@ -398,7 +407,7 @@ AbstractVariety of dim 5 julia> h = gens(P2)[1] h -julia> i = hom(P2, P5, [2*h]) +julia> i = map(P2, P5, [2*h]) AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 julia> dim(i) @@ -425,13 +434,13 @@ AbstractBundle of rank 2 on AbstractVariety of dim 2 julia> PT = abstract_projective_bundle(T) AbstractVariety of dim 3 -julia> pi = structure_map(PT) +julia> pr = structure_map(PT) AbstractVarietyMap from AbstractVariety of dim 3 to AbstractVariety of dim 2 -julia> PBT = pullback(pi, T) +julia> PBT = pullback(pr, T) AbstractBundle of rank 2 on AbstractVariety of dim 3 -julia> PBT*OO(PT, 1) - OO(PT) == tangent_bundle(pi) # relative Euler sequence +julia> PBT*OO(PT, 1) - OO(PT) == tangent_bundle(pr) # relative Euler sequence true ``` @@ -469,12 +478,12 @@ AbstractVariety of dim 5 julia> h = gens(P2)[1] h +julia> i = map(P2, P5, [2*h]) +AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 + julia> H = gens(P5)[1] H -julia> i = hom(P2, P5, [2*h]) -AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 - julia> pullback(i, H) 2*h @@ -486,6 +495,29 @@ pullback(f::AbstractVarietyMap, x::MPolyDecRingOrQuoElem) = f.pullback(x) pullback(f::AbstractVarietyMap, F::AbstractBundle) Return the pullback of `F` via `f`. + +# Examples + +```jldoctest +julia> P2 = abstract_projective_space(2) +AbstractVariety of dim 2 + +julia> P5 = abstract_projective_space(5, symbol = "H") +AbstractVariety of dim 5 + +julia> h = gens(P2)[1] +h + +julia> i = map(P2, P5, [2*h]) +AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 + +julia> E = pullback(i, OO(P2,1)) +AbstractBundle of rank 1 on AbstractVariety of dim 2 + +julia> total_chern_class(E) +2*h + 1 + +``` """ pullback(f::AbstractVarietyMap, F::AbstractBundle) = AbstractBundle(f.domain, f.pullback(chern_character(F))) @@ -506,7 +538,7 @@ AbstractVariety of dim 5 julia> h = gens(P2)[1] h -julia> i = hom(P2, P5, [2*h]) +julia> i = map(P2, P5, [2*h]) AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 julia> pushforward(i, h) @@ -519,10 +551,38 @@ pushforward(f::AbstractVarietyMap, x::MPolyDecRingOrQuoElem) = f.pushforward(x) pushforward(f::AbstractVarietyMap, F::AbstractBundle) Return the pushforward of `F` via `f`, that is, return the alternating sum of all direct images of `F` via `f`. + +# Examples + +```jldoctest +julia> P2 = abstract_projective_space(2) +AbstractVariety of dim 2 + +julia> P5 = abstract_projective_space(5, symbol = "H") +AbstractVariety of dim 5 + +julia> h = gens(P2)[1] +h + +julia> i = map(P2, P5, [2*h]) +AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 + +julia> E = pushforward(i, OO(P2,1)) +AbstractBundle of rank 0 on AbstractVariety of dim 5 + +julia> total_chern_class(E) +168*H^5 + 42*H^4 + 8*H^3 + 1 + +``` """ pushforward(f::AbstractVarietyMap, F::AbstractBundle) = AbstractBundle(f.codomain, f.pushforward(chern_character(F) * todd_class(f))) # Grothendieck-Hirzebruch-Riemann-Roch -function identity_hom(X::V) where V <: AbstractVarietyT +@doc raw""" + identity_map(X::AbstractVariety) + +Return the identity map on `X`. +""" +function identity_map(X::AbstractVariety) AbstractVarietyMap(X, X, gens(X.ring), map_from_func(identity, X.ring, X.ring)) end @@ -1174,8 +1234,8 @@ julia> hilbert_polynomial(P2) hilbert_polynomial(X::AbstractVariety) = hilbert_polynomial(trivial_line_bundle(X)) # find canonically defined morphism from X to Y -function _hom(X::AbstractVariety, Y::AbstractVariety) - X == Y && return identity_hom(X) +function _map(X::AbstractVariety, Y::AbstractVariety) + X == Y && return identity_map(X) # first handle the case where X is a (fibered) product projs = get_attribute(X, :projections) if projs !== nothing @@ -1196,14 +1256,14 @@ end # morphisms for points are convenient, but are not desired when doing coercion @doc raw""" - hom(X::AbstractVariety, Y::AbstractVariety) + map(X::AbstractVariety, Y::AbstractVariety) -Return a canonically defined morphism from `X` to `Y`. +Return a canonically defined map from `X` to `Y`. """ -function hom(X::AbstractVariety, Y::AbstractVariety) - get_attribute(Y, :point) !== nothing && return hom(X, Y, [X(0)]) # Y is a point - get_attribute(X, :point) !== nothing && return hom(X, Y, repeat([X(0)], length(gens(Y.ring)))) # X is a point - _hom(X, Y) +function map(X::AbstractVariety, Y::AbstractVariety) + get_attribute(Y, :point) !== nothing && return map(X, Y, [X(0)]) # Y is a point + get_attribute(X, :point) !== nothing && return map(X, Y, repeat([X(0)], length(gens(Y.ring)))) # X is a point + _map(X, Y) end # product abstract_variety @@ -1281,7 +1341,7 @@ inclusion of the graph into the product. """ function graph(f::AbstractVarietyMap) X, Y = f.domain, f.codomain - hom(X, X * Y, vcat(gens(X), f.pullback.image)) + map(X, X * Y, vcat(gens(X), f.pullback.image)) end ############################################################################### @@ -1391,10 +1451,10 @@ function _coerce(F::AbstractBundle, G::AbstractBundle) X, Y = F.parent, G.parent X == Y && return F, G try - return F, pullback(_hom(X, Y), G) + return F, pullback(_map(X, Y), G) catch try - return pullback(_hom(Y, X), F), G + return pullback(_map(Y, X), F), G catch error("the sheaves are not on compatible varieties") end @@ -2049,7 +2109,7 @@ function degeneracy_locus(F::AbstractBundle, G::AbstractBundle, k::Int; class::B Gr = (m-k == 1) ? abstract_projective_bundle(F) : abstract_flag_bundle(F, m-k) S = Gr.bundles[1] D = zero_locus_section(dual(S) * G) - D.struct_map = hom(D, F.parent) # skip the flag abstract_variety + D.struct_map = map(D, F.parent) # skip the flag abstract_variety if isdefined(F.parent, :O1) D.O1 = pullback(D.struct_map, F.parent.O1) end @@ -2147,7 +2207,7 @@ function abstract_projective_space(n::Int; base::Ring=QQ, symbol::String="h") S = AbstractBundle(P, 1, 1-h) Q = trivial_line_bundle(P)*(n+1) - S P.bundles = [S, Q] - P.struct_map = hom(P, abstract_point(base=base), [P(1)]) + P.struct_map = map(P, abstract_point(base=base), [P(1)]) set_attribute!(P, :description => "Projective space of dim $n") set_attribute!(P, :grassmannian => :absolute) set_attribute!(P, :alg => true) @@ -2345,7 +2405,7 @@ function abstract_grassmannian(k::Int, n::Int; base::Ring = QQ, symbol::String = Gr.point = Gr((-1)^d*c[end]^(n-k)) Gr.T = dual(S) * Q Gr.bundles = [S, Q] - Gr.struct_map = hom(Gr, abstract_point(base=base), [Gr(1)]) + Gr.struct_map = map(Gr, abstract_point(base=base), [Gr(1)]) set_attribute!(Gr, :description => "Grassmannian Gr($k, $n)") set_attribute!(Gr, :grassmannian => :absolute) set_attribute!(Gr, :alg => true) @@ -2414,7 +2474,7 @@ function abs_flag(dims::Vector{Int}; base::Ring=QQ, symbol::String="c") Fl.O1 = simplify(sum((i-1)*chern_class(Fl.bundles[i], 1) for i in 1:l)) Fl.point = prod(top_chern_class(E)^dims[i] for (i,E) in enumerate(Fl.bundles[2:end])) Fl.T = sum(dual(Fl.bundles[i]) * sum([Fl.bundles[j] for j in i+1:l]) for i in 1:l-1) - Fl.struct_map = hom(Fl, abstract_point(base=base), [Fl(1)]) + Fl.struct_map = map(Fl, abstract_point(base=base), [Fl(1)]) set_attribute!(Fl, :description => "Flag abstract_variety Flag$(tuple(dims...))") if l == 2 set_attribute!(Fl, :grassmannian => :absolute) end set_attribute!(Fl, :alg => true) diff --git a/experimental/IntersectionTheory/src/blowup.jl b/experimental/IntersectionTheory/src/blowup.jl index 01fe613897b7..d21404c040fc 100644 --- a/experimental/IntersectionTheory/src/blowup.jl +++ b/experimental/IntersectionTheory/src/blowup.jl @@ -244,7 +244,7 @@ h julia> H = gens(P5)[1] H -julia> i = hom(P2, P5, [2*h]) +julia> i = map(P2, P5, [2*h]) AbstractVarietyMap from AbstractVariety of dim 2 to AbstractVariety of dim 5 julia> Bl, E, j = blowup(i) @@ -437,10 +437,10 @@ function blowup_points(X::AbstractVariety, n::Int; symbol::String = "e") Bl = X P = abstract_point(base = X.base) for i in 1:n - Bl = blowup(hom(P, Bl, [zero(P.ring) for j = 1:i]), symbol=symbs[i])[1] + Bl = blowup(map(P, Bl, [zero(P.ring) for j = 1:i]), symbol=symbs[i])[1] end set_attribute!(Bl, :description => "Blowup of $X at $n points") - Bl.struct_map = hom(Bl, X) + Bl.struct_map = map(Bl, X) if get_attribute(X, :alg) == true set_attribute!(Bl, :alg => true) end diff --git a/experimental/IntersectionTheory/test/runtests.jl b/experimental/IntersectionTheory/test/runtests.jl index f14742ffab53..529d31b8aabf 100644 --- a/experimental/IntersectionTheory/test/runtests.jl +++ b/experimental/IntersectionTheory/test/runtests.jl @@ -32,7 +32,7 @@ let pushforward = IntersectionTheory.pushforward @test schur_functor(A, [1,1]) == exterior_power(A, 2) @test schur_functor(A, [2]) == symmetric_power(A, 2) D = degeneracy_locus(A, B, 2) - @test pushforward(hom(D, X), D(1)) == degeneracy_locus(A, B, 2, class=true) + @test pushforward(map(D, X), D(1)) == degeneracy_locus(A, B, 2, class=true) # characteristic classes t = todd_class(2) @@ -51,11 +51,11 @@ let pushforward = IntersectionTheory.pushforward p = abstract_point() P2 = abstract_projective_space(2) - i = hom(P2, P2) + i = map(P2, P2) @test i.domain == P2 @test i.codomain == P2 - i = hom(p, P2) + i = map(p, P2) @test pushforward(i, p(1)) == P2.point @test pullback(i, P2.O1) == 0 @test i.T === tangent_bundle(i) @@ -81,7 +81,7 @@ let pushforward = IntersectionTheory.pushforward P5 = abstract_projective_space(5, symbol="H") h, H = P2.O1, P5.O1 - v = hom(P2, P5, [2h]) + v = map(P2, P5, [2h]) @test pullback(v, H) == 2h @test pullback(v, P5.point) == 0 @test v.pushforward(h) == 2H^4 @@ -91,14 +91,14 @@ let pushforward = IntersectionTheory.pushforward # test that hom works for product P, Q = abstract_projective_space(1), abstract_projective_space(1) PxQ = P * Q - p, q = hom(PxQ, P), hom(PxQ, Q) + p, q = map(PxQ, P), map(PxQ, Q) @test pushforward(p, PxQ.point) == P.point @test integral(pullback(p, P.point) * pullback(q, Q.point)) == 1 # # cubic containing a plane # P2 = abstract_projective_space(2) # Y = complete_intersection(abstract_projective_space(5), 3) - # i = hom(P2, Y, [P2.O1], inclusion=true) + # i = map(P2, Y, [P2.O1], inclusion=true) # Y1 = i.codomain # p = pushforward(i, P2(1)) # h = Y1.O1 @@ -243,7 +243,7 @@ let pushforward = IntersectionTheory.pushforward # blowup Veronese P2 = abstract_projective_space(2) P5 = abstract_projective_space(5) - i = hom(P2, P5, [2P2.O1]) + i = map(P2, P5, [2P2.O1]) Bl, E, j = blowup(i) c = top_chern_class(tangent_bundle(Bl)) @test integral(pushforward(structure_map(Bl), c)) == 12 @@ -257,7 +257,7 @@ let pushforward = IntersectionTheory.pushforward # blowup point in P2 P2 = abstract_projective_space(2) P = abstract_point(base = P2.base) - Bl, E, j = blowup(hom(P, P2, [zero(P.ring)])) + Bl, E, j = blowup(map(P, P2, [zero(P.ring)])) e = pushforward(j, E(1)) @test integral(e^2) == -1 @test integral(pullback(j, e)) == -1 @@ -266,14 +266,14 @@ let pushforward = IntersectionTheory.pushforward # blowup point in P7 P7 = abstract_projective_space(7) P = abstract_point(base = P2.base) - Bl, E, j = blowup(hom(P, P7, [zero(P.ring)])) + Bl, E, j = blowup(map(P, P7, [zero(P.ring)])) e = pushforward(j, E(1)) @test euler(Bl) == 14 # blowup twisted cubic P1 = abstract_projective_space(1) P3 = abstract_projective_space(3) - i = hom(P1, P3, [3P1.O1]) + i = map(P1, P3, [3P1.O1]) Bl, E, j = blowup(i) e = pushforward(j, E(1)) quad = pullback(structure_map(Bl), 2P3.O1) - e @@ -287,7 +287,7 @@ let pushforward = IntersectionTheory.pushforward (r, s, t) = gens(F) P1 = abstract_projective_space(1, base = F) P3 = abstract_projective_space(3, base = F) - i = hom(P1, P3, [3P1.O1]) + i = map(P1, P3, [3P1.O1]) Bl, E, j = blowup(i) e = pushforward(j, E(1)) rH, sH, tH = [pullback(structure_map(Bl), x * P3.O1) - e for x in [r,s,t]] @@ -295,7 +295,7 @@ let pushforward = IntersectionTheory.pushforward G = abstract_grassmannian(2, 5) P9 = abstract_projective_space(9) - i = hom(G, P9, [G.O1]) + i = map(G, P9, [G.O1]) Bl, E, j = blowup(i) e = pushforward(j, E(1)) quad = pullback(structure_map(Bl), 2P9.O1)-e @@ -310,7 +310,7 @@ let pushforward = IntersectionTheory.pushforward P3 = abstract_projective_space(3, base = F) C = zero_locus_section(OO(P2,d)) C.point = 1//(2-2g) * chern_class(C, 1) - i = hom(C, P3, [d * C.point]) + i = map(C, P3, [d * C.point]) Bl, E, j = blowup(i) e = pushforward(j, E(1)) rH, sH, tH = [pullback(structure_map(Bl), x * P3.O1) - e for x in [r,s,t]] From 10f22977a7cdd654b7b34d8a1c6ad531015859ec Mon Sep 17 00:00:00 2001 From: Erik Paemurru <143521159+paemurru@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:45:30 +0100 Subject: [PATCH 63/84] Move `dim` from schemes to rings (#4280) * Move `dim` from schemes to rings * dim for zzModRing and ZZModRing --------- Co-authored-by: Simon Brandhorst --- .../AffineSchemes/Objects/Attributes.jl | 42 +------------------ src/Rings/mpoly-affine-algebras.jl | 13 +++++- src/Rings/mpolyquo-localizations.jl | 31 +++++++++++++- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl index dd775e171fad..2d48a7f2ebfb 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl @@ -399,52 +399,14 @@ julia> dim(Y) # one dimension comes from ZZ and two from x1 and x2 3 ``` """ -dim(X::AbsAffineScheme) - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyQuoLocRing}) - error("Not implemented") -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyQuoLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:MPolyPowersOfElement}}) - return dim(closure(X)) -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyQuoLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:Union{MPolyComplementOfPrimeIdeal, MPolyComplementOfKPointIdeal}}}) - # Spec (R / I)_P - R = OO(X) - P = prime_ideal(inverted_set(R)) - I = saturated_ideal(modulus(R)) - return dim(I) - dim(P) -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyLocRing}) - error("Not implemented") -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:MPolyPowersOfElement}}) - # zariski open subset of A^n - return dim(closure(X)) -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:Union{MPolyComplementOfPrimeIdeal, MPolyComplementOfKPointIdeal}}}) - P = prime_ideal(inverted_set(OO(X))) - return codim(P) -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyRing}) - return dim(ideal(ambient_coordinate_ring(X), [zero(ambient_coordinate_ring(X))])) -end - -@attr Any function dim(X::AbsAffineScheme{<:Ring, <:MPolyQuoRing}) - return dim(modulus(OO(X))) -end +dim(X::AbsAffineScheme) = dim(OO(X)) @doc raw""" codim(X::AbsAffineScheme) Return the codimension of ``X`` in its ambient affine space. -Throws and error if ``X`` does not have an ambient affine space. +Throws an error if ``X`` does not have an ambient affine space. # Examples ```jldoctest diff --git a/src/Rings/mpoly-affine-algebras.jl b/src/Rings/mpoly-affine-algebras.jl index 56c9c926fb0d..c96f3d247dc6 100644 --- a/src/Rings/mpoly-affine-algebras.jl +++ b/src/Rings/mpoly-affine-algebras.jl @@ -20,8 +20,17 @@ julia> dim(A) ``` """ function dim(A::MPolyQuoRing) - I = A.I - return dim(I) + return dim(modulus(A)) +end + +function dim(A::zzModRing) + modulus(A) == 1 && error("Function `dim` gives wrong answers if the base ring is the zero ring.") + return 0 +end + +function dim(A::ZZModRing) + modulus(A) == 1 && error("Function `dim` gives wrong answers if the base ring is the zero ring.") + return 0 end @doc raw""" diff --git a/src/Rings/mpolyquo-localizations.jl b/src/Rings/mpolyquo-localizations.jl index 633652679a38..f2019b9acea2 100644 --- a/src/Rings/mpolyquo-localizations.jl +++ b/src/Rings/mpolyquo-localizations.jl @@ -2565,7 +2565,36 @@ function small_generating_set( end::Vector{elem_type(base_ring(I))} end -dim(R::MPolyQuoLocRing{<:Field, <:FieldElem, <:MPolyRing, <:MPolyRingElem, <:MPolyComplementOfPrimeIdeal}) = dim(saturated_ideal(modulus(R))) - dim(prime_ideal(inverted_set(R))) +@attr Int function dim(R::MPolyLocRing) + error("Not implemented") +end + +@attr Int function dim(R::MPolyQuoLocRing{<:Any, <:Any, <:MPolyRing, <:MPolyRingElem, <:Union{MPolyComplementOfPrimeIdeal, MPolyComplementOfKPointIdeal}}) + P = prime_ideal(inverted_set(R)) + I = saturated_ideal(modulus(R)) + return dim(I) - dim(P) +end + +@attr Int function dim(R::MPolyQuoLocRing{<:Any, <:Any, <:MPolyRing, <:MPolyRingElem, <:MPolyPowersOfElement}) + return dim(saturated_ideal(modulus(R))) +end + +@attr Int function dim(R::MPolyLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:MPolyPowersOfElement}) + # zariski open subset of A^n + return dim(base_ring(R)) +end + +@attr Int function dim(R::MPolyLocRing{<:Any,<:Any,<:MPolyRing,<:MPolyRingElem, <:MPolyComplementOfPrimeIdeal}) + P = prime_ideal(inverted_set(R)) + return codim(P) +end + + +@attr Int function dim(R::MPolyLocRing{<:Field,<:Any,<:MPolyRing,<:MPolyRingElem, <:MPolyComplementOfKPointIdeal}) + # localization of a polynomial ring over a field at a maximal ideal does not change the dimension + # because all maximal ideals have the same dimension in this case. + return dim(base_ring(R)) +end ######################################################################## # Localizations of graded rings # From 2a8e01fd70d97f3616affb4a786bf6b2a24e44fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 14 Nov 2024 09:30:00 +0100 Subject: [PATCH 64/84] Replace uses of `add_scaled_row!` by `addmul!` (#4308) --- Project.toml | 2 +- .../src/Morphisms/simplified_complexes.jl | 14 +++++++------- experimental/LieAlgebras/src/AbstractLieAlgebra.jl | 2 +- experimental/LieAlgebras/src/Types.jl | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Project.toml b/Project.toml index eb46435051c4..df7242405e92 100644 --- a/Project.toml +++ b/Project.toml @@ -29,7 +29,7 @@ AbstractAlgebra = "0.43.7" AlgebraicSolving = "0.8.0" Distributed = "1.6" GAP = "0.12.0" -Hecke = "0.34.3" +Hecke = "0.34.7" JSON = "^0.20, ^0.21" JSON3 = "1.13.2" LazyArtifacts = "1.6" diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl index f37708b45121..e0e9ef965493 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl @@ -496,7 +496,7 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) # clear the q-th column for (i, b) in a_col_del #A[i] = A[i] - uinv*b*a_row # original operation, replaced by inplace arithmetic below - Hecke.add_scaled_row!(a_row, A[i], -uinv*b) + addmul!(A[i], a_row, -uinv*b) end A[p] = sparse_row(R, [(q, u)]) @@ -505,7 +505,7 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) v = S[p] for (i, b) in a_col_del #S[i] = S[i] - uinv*b*v # original operation, replaced by inplace arithmetic below - Hecke.add_scaled_row!(v, S[i], -uinv*b) + addmul!(S[i], v, -uinv*b) end # Adjust Sinv_transp @@ -513,23 +513,23 @@ function _simplify_matrix!(A::SMat; find_pivot=nothing) inc_row = sparse_row(R) for (i, a) in a_col_del #is_zero(Sinv_transp[i]) && continue # can not be true since S is invertible - Hecke.add_scaled_row!(Sinv_transp[i], inc_row, a) + addmul!(inc_row, Sinv_transp[i], a) end - Hecke.add_scaled_row!(inc_row, Sinv_transp[p], uinv) + addmul!(Sinv_transp[p], inc_row, uinv) # Adjust T #T[q] = T[q] + sum(uinv*a*T[i] for (i, a) in a_row_del; init=sparse_row(R)) inc_row = sparse_row(R) for (i, a) in a_row_del # is_zero(T[i]) && continue # can not be true since T is invertible - Hecke.add_scaled_row!(T[i], inc_row, a) + addmul!(inc_row, T[i], a) end - Hecke.add_scaled_row!(inc_row, T[q], uinv) + addmul!(T[q], inc_row, uinv) # Adjust Tinv_transp v = deepcopy(Tinv_transp[q]) for (j, b) in a_row_del - Hecke.add_scaled_row!(v, Tinv_transp[j], -uinv*b) + addmul!(Tinv_transp[j], v, -uinv*b) end # Kept here for debugging. These must be true: diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index 24a854b438fa..0bb3157d9322 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -93,7 +93,7 @@ function bracket( iszero(cxi) && continue for (j, cyj) in enumerate(coefficients(y)) iszero(cyj) && continue - Hecke.add_scaled_row!(_struct_consts(L)[i, j], vec, cxi * cyj) + vec = addmul!(vec, _struct_consts(L)[i, j], cxi * cyj) end end return L(vec) diff --git a/experimental/LieAlgebras/src/Types.jl b/experimental/LieAlgebras/src/Types.jl index 0b5f7f8acf7a..8acf63cf1d32 100644 --- a/experimental/LieAlgebras/src/Types.jl +++ b/experimental/LieAlgebras/src/Types.jl @@ -181,13 +181,13 @@ abstract type LieAlgebraElem{C<:FieldElem} <: AbstractAlgebra.SetElem end begin row = sparse_row(R) for (k, k_val) in struct_consts[i, j] - Hecke.add_scaled_row!(struct_consts[k, l], row, k_val) + row = addmul!(row, k_val, struct_consts[k, l]) end for (k, k_val) in struct_consts[j, l] - Hecke.add_scaled_row!(struct_consts[k, i], row, k_val) + row = addmul!(row, k_val, struct_consts[k, i]) end for (k, k_val) in struct_consts[l, i] - Hecke.add_scaled_row!(struct_consts[k, j], row, k_val) + row = addmul!(row, k_val, struct_consts[k, j]) end row end for i in 1:dimL, j in 1:dimL, l in 1:dimL From 46d028f2ac342af8143ac28bf5971eb783fec613 Mon Sep 17 00:00:00 2001 From: Lars Kastner Date: Thu, 14 Nov 2024 12:08:34 +0100 Subject: [PATCH 65/84] issue/2142: Add subtype info to docs --- src/PolyhedralGeometry/Cone/properties.jl | 2 +- src/PolyhedralGeometry/Polyhedron/properties.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PolyhedralGeometry/Cone/properties.jl b/src/PolyhedralGeometry/Cone/properties.jl index 9aaf989ef0cb..6e56e125921c 100644 --- a/src/PolyhedralGeometry/Cone/properties.jl +++ b/src/PolyhedralGeometry/Cone/properties.jl @@ -534,7 +534,7 @@ is_fulldimensional(C::Cone) = pm_object(C).FULL_DIM::Bool Return the facets of `C` in the format defined by `as`. The allowed values for `as` are -* `Halfspace`, +* `Halfspace` (or its subtype `LinearHalfspace`), * `Cone`. # Examples diff --git a/src/PolyhedralGeometry/Polyhedron/properties.jl b/src/PolyhedralGeometry/Polyhedron/properties.jl index a7ad3ae9656a..c400ea1bce88 100644 --- a/src/PolyhedralGeometry/Polyhedron/properties.jl +++ b/src/PolyhedralGeometry/Polyhedron/properties.jl @@ -491,7 +491,7 @@ end Return the facets of `P` in the format defined by `as`. The allowed values for `as` are -* `Halfspace`, +* `Halfspace` (or its subtype `AffineHalfspace`), * `Polyhedron`, * `Pair`. From 6b8dac8d036dc1fad390e46d9074a5a61f227133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anne=20Fr=C3=BChbis-Kr=C3=BCger?= <77079696+afkafkafk13@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:28:58 +0100 Subject: [PATCH 66/84] Fix for ComplementOfKPointIdeal (#4310) * fix for complement_of_point_ideal --------- Co-authored-by: anne --- src/Rings/mpoly-localization_types.jl | 4 ++++ test/AlgebraicGeometry/Schemes/AffineSchemes.jl | 2 +- test/Rings/mpoly-localizations.jl | 5 +---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Rings/mpoly-localization_types.jl b/src/Rings/mpoly-localization_types.jl index 473358e93961..5db57ddceb2d 100644 --- a/src/Rings/mpoly-localization_types.jl +++ b/src/Rings/mpoly-localization_types.jl @@ -143,6 +143,9 @@ end Complement of a maximal ideal ``𝔪 = ⟨x₁-a₁,…,xₙ-aₙ⟩⊂ 𝕜[x₁,…xₙ]`` with ``aᵢ∈ 𝕜``. +!!! note +The coefficient ring is required to be a field. + ```jldoctest julia> R, (x, y) = polynomial_ring(QQ, [:x, :y]); @@ -178,6 +181,7 @@ mutable struct MPolyComplementOfKPointIdeal{ length(a) == ngens(R) || error("the number of variables in the ring does not coincide with the number of coordinates") n = length(a) kk = coefficient_ring(R) + @req kk isa Field "This localization is only available over fields" b = kk.(a) # fails if the input is not compatible S = new{typeof(kk), elem_type(kk), RingType, elem_type(R)}(R, b) return S diff --git a/test/AlgebraicGeometry/Schemes/AffineSchemes.jl b/test/AlgebraicGeometry/Schemes/AffineSchemes.jl index 0e1cde438b3a..7e245998c202 100644 --- a/test/AlgebraicGeometry/Schemes/AffineSchemes.jl +++ b/test/AlgebraicGeometry/Schemes/AffineSchemes.jl @@ -138,7 +138,7 @@ end Z = spec(R, S) @test dim(Z) == 4 - S = complement_of_point_ideal(R, [1, 1, 1]) + S = complement_of_prime_ideal(ideal(R, [x-1, y-1, z-1])) I = ideal(R, [x-1, y-1])*ideal(R, z) L, _ = localization(R, S) W, _ = quo(L, L(I)) diff --git a/test/Rings/mpoly-localizations.jl b/test/Rings/mpoly-localizations.jl index d95a7763458f..902c9b026ca9 100644 --- a/test/Rings/mpoly-localizations.jl +++ b/test/Rings/mpoly-localizations.jl @@ -9,8 +9,6 @@ const rng = Oscar.get_seeded_rng() I = ideal(R, f) S = Oscar.MPolyComplementOfPrimeIdeal(I) V, _ = localization(S) - T = Oscar.MPolyComplementOfKPointIdeal(R, [ZZ(1), ZZ(0)]) - W, _ = localization(T) k = QQ R, variab = k[:x, :y] @@ -72,8 +70,8 @@ const rng = Oscar.get_seeded_rng() x = v[1] y = v[2] f = (x^2 + y^2)^2 - S = Oscar.MPolyComplementOfKPointIdeal(R, [ZZ(0), ZZ(0)]) T = Oscar.MPolyPowersOfElement(R, [f]) + S = Oscar.MPolyComplementOfPrimeIdeal(ideal(R, [x, y])) U = Oscar.MPolyProductOfMultSets(R, [S, T]) @test f in U @test (f*(x-1) in U) @@ -241,7 +239,6 @@ end # d = Vector{elem_type(R)}() # d = [rand(R, 1:3, 0:4, 1:10)::elem_type(R) for i in 0:(abs(rand(Int))%3+1)] # S = Oscar.MPolyPowersOfElement(R, d) -# T = Oscar.MPolyComplementOfKPointIdeal(R, [kk(125), kk(-45)]) # U = Oscar.MPolyComplementOfPrimeIdeal(I) # # test_Ring_interface_recursive(localization(S)[1]) From f96891f916fee79c570ce2ba3f3ea89488ffb16c Mon Sep 17 00:00:00 2001 From: Matthias Zach <85350711+HechtiDerLachs@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:25:37 +0100 Subject: [PATCH 67/84] Add some dummy hash functions for the geometry (#4305) --- .../Schemes/AffineSchemes/Objects/Methods.jl | 5 +++++ .../Schemes/Divisors/AlgebraicCycles.jl | 4 ++++ src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl | 9 +++++++++ src/AlgebraicGeometry/Schemes/Gluing/Methods.jl | 4 ++++ .../Schemes/ProjectiveSchemes/Objects/Methods.jl | 3 +++ src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl | 3 +++ 6 files changed, 28 insertions(+) diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl index 724c56802dcf..6319cd4d9887 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl @@ -473,3 +473,8 @@ function _change_base_ring(phi::Any, @assert _has_coefficient_map(res_map) return L_red, res_map end + +function Base.hash(X::Scheme, u::UInt) + return u +end + diff --git a/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl b/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl index 82806d80c191..ea7b74e85290 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/AlgebraicCycles.jl @@ -534,3 +534,7 @@ function integral(W::AbsAlgebraicCycle; check::Bool=true) return result end +function Base.hash(X::AbsAlgebraicCycle, u::UInt) + return u +end + diff --git a/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl b/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl index 34c5e1255791..36f7e74ae448 100644 --- a/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl +++ b/src/AlgebraicGeometry/Schemes/Divisors/CartierDivisor.jl @@ -530,3 +530,12 @@ function _show_semi_compact(io::IO, C::CartierDivisor, cov::Covering, n::String) print(io, Dedent()) end end + +function Base.hash(X::CartierDivisor, u::UInt) + return u +end + +function Base.hash(X::EffectiveCartierDivisor, u::UInt) + return u +end + diff --git a/src/AlgebraicGeometry/Schemes/Gluing/Methods.jl b/src/AlgebraicGeometry/Schemes/Gluing/Methods.jl index 7024fc902e00..70b9e81c97e4 100644 --- a/src/AlgebraicGeometry/Schemes/Gluing/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/Gluing/Methods.jl @@ -236,3 +236,7 @@ function base_change(phi::Any, G::AbsGluing; return LazyGluing(domain(patch_change1), domain(patch_change2), gd) end +function Base.hash(X::AbsGluing, u::UInt) + return u +end + diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Objects/Methods.jl b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Objects/Methods.jl index 20e2c51d5841..cb9bbb3b3543 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Objects/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Objects/Methods.jl @@ -433,5 +433,8 @@ function Base.union(comp::Vector{<:AbsProjectiveScheme}) return result end +function Base.hash(X::AbsProjectiveScheme, u::UInt) + return u +end diff --git a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl index 2bf4fb32bdc8..cdd99771ec45 100644 --- a/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl +++ b/src/AlgebraicGeometry/Schemes/Sheaves/CoherentSheaves.jl @@ -1116,4 +1116,7 @@ function projectivization(E::AbsCoherentSheaf; return CoveredProjectiveScheme(X, C, on_patches, projective_gluings, check=false) end +function Base.hash(X::AbsCoherentSheaf, u::UInt) + return u +end From 499f7cebc9eb419f9edc41766e797b9489f53590 Mon Sep 17 00:00:00 2001 From: Simon Brandhorst Date: Thu, 14 Nov 2024 16:54:02 +0100 Subject: [PATCH 68/84] Move EllipticSurface to src and document it. (#4294) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move EllipticSurface and BlowupMorphism to src * Document EllipticSurface, exports, deprecate mordell_weil_lattice --------- Co-authored-by: Co-authored-by: HechtiDerLachs Co-authored-by: Lars Göttgens --- docs/doc.main | 1 + docs/oscar_references.bib | 35 + .../Surfaces/EllipticSurface.md | 97 + experimental/Schemes/src/Schemes.jl | 4 - experimental/Schemes/src/Types.jl | 185 - experimental/Schemes/src/elliptic_surface.jl | 3232 ----------------- src/AlgebraicGeometry/AlgebraicGeometry.jl | 5 + .../Schemes/BlowupMorphism}/BlowupMorphism.jl | 5 - .../Schemes/BlowupMorphism/Types.jl | 0 .../CoveredProjectiveScheme.jl | 0 .../Schemes/CoveredProjectiveScheme/Types.jl | 178 + src/AlgebraicGeometry/Schemes/main.jl | 8 + .../EllipticSurface/EllipticSurface.jl | 1360 +++++++ .../Surfaces/EllipticSurface/Morphisms.jl | 956 +++++ .../Surfaces/EllipticSurface/MoveMeToHecke.jl | 57 + .../Surfaces/EllipticSurface/NeighborStep.jl | 899 +++++ .../Surfaces/EllipticSurface/Types.jl | 97 + src/deprecations.jl | 1 + src/exports.jl | 24 +- ...elliptic_surface.jl => EllipticSurface.jl} | 2 +- test/runtests.jl | 2 +- 21 files changed, 3718 insertions(+), 3430 deletions(-) create mode 100644 docs/src/AlgebraicGeometry/Surfaces/EllipticSurface.md delete mode 100644 experimental/Schemes/src/elliptic_surface.jl rename {experimental/Schemes/src => src/AlgebraicGeometry/Schemes/BlowupMorphism}/BlowupMorphism.jl (99%) rename experimental/Schemes/src/BlowupMorphismTypes.jl => src/AlgebraicGeometry/Schemes/BlowupMorphism/Types.jl (100%) rename experimental/Schemes/src/CoveredProjectiveSchemes.jl => src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/CoveredProjectiveScheme.jl (100%) create mode 100644 src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/Types.jl create mode 100644 src/AlgebraicGeometry/Surfaces/EllipticSurface/EllipticSurface.jl create mode 100644 src/AlgebraicGeometry/Surfaces/EllipticSurface/Morphisms.jl create mode 100644 src/AlgebraicGeometry/Surfaces/EllipticSurface/MoveMeToHecke.jl create mode 100644 src/AlgebraicGeometry/Surfaces/EllipticSurface/NeighborStep.jl create mode 100644 src/AlgebraicGeometry/Surfaces/EllipticSurface/Types.jl rename test/AlgebraicGeometry/Schemes/{elliptic_surface.jl => EllipticSurface.jl} (99%) diff --git a/docs/doc.main b/docs/doc.main index 4d16abb281b3..7dcbde1f057d 100644 --- a/docs/doc.main +++ b/docs/doc.main @@ -198,6 +198,7 @@ ], "Surfaces" => [ "AlgebraicGeometry/Surfaces/K3Surfaces.md", + "AlgebraicGeometry/Surfaces/EllipticSurface.md", "AlgebraicGeometry/Surfaces/AdjunctionProcess.md", "AlgebraicGeometry/Surfaces/ParametrizationSurfaces.md", "AlgebraicGeometry/Surfaces/SurfacesP4.md", diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index c6e93053f394..d7c1881064a4 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -171,6 +171,19 @@ @Article{BDLPSS13 doi = {10.1016/j.jsc.2012.07.002} } +@Article{BE23, + author = {Brandhorst, Simon and Elkies, Noam D.}, + title = {Equations for a K3 Lehmer map}, + mrnumber = {4652575}, + journal = {J. Algebraic Geom.}, + fjournal = {Journal of Algebraic Geometry}, + volume = {32}, + number = {4}, + pages = {641--675}, + year = {2023}, + doi = {10.1090/jag/810} +} + @Misc{BEO23, author = {Besche, Hans Ulrich and Eick, Bettina and O'Brien, Eamonn}, title = {SmallGrp, The GAP Small Groups Library, Version 1.5.3}, @@ -357,6 +370,15 @@ @Article{BS09 doi = {10.1016/j.aim.2009.06.009} } +@Misc{BZ23, + author = {Brandhorst, Simon and Zach, Matthias}, + title = {Elliptic fibrations on Vinberg's most algebraic K3 surface}, + year = {2023}, + eprint = {2311.11766}, + archiveprefix = {arXiv}, + primaryclass = {math.AG} +} + @Book{Ben93, author = {Benson, David J.}, title = {Polynomial invariants of finite groups}, @@ -2093,6 +2115,19 @@ @Article{SS12 doi = {10.1016/j.jcta.2011.12.005} } +@Book{SS19, + author = {Schütt, Matthias and Shioda, Tetsuji}, + title = {Mordell-Weil lattices}, + mrnumber = {3970314}, + series = {Ergebnisse der Mathematik und ihrer Grenzgebiete. 3. Folge. A Series of Modern Surveys in Mathematics + [Results in Mathematics and Related Areas. 3rd Series. A Series of Modern Surveys in Mathematics]}, + volume = {70}, + publisher = {Springer, Singapore}, + pages = {xvi+431}, + year = {2019}, + doi = {10.1007/978-981-32-9301-4} +} + @Article{SV-D-V87, author = {Sommese, Andrew John and Van de Ven, A.}, title = {On the adjunction mapping}, diff --git a/docs/src/AlgebraicGeometry/Surfaces/EllipticSurface.md b/docs/src/AlgebraicGeometry/Surfaces/EllipticSurface.md new file mode 100644 index 000000000000..a76c7cad6d1a --- /dev/null +++ b/docs/src/AlgebraicGeometry/Surfaces/EllipticSurface.md @@ -0,0 +1,97 @@ +```@meta +CurrentModule = Oscar +``` + +# Elliptic Surfaces +See [SS19](@cite) for the theory of elliptic surfaces. + +```@docs +EllipticSurface +``` + +## Constructors +```@docs +elliptic_surface +kodaira_neron_model(E::EllipticCurve) +``` + +## Attributes +```@docs +generic_fiber(S::EllipticSurface) +zero_section(S::EllipticSurface) +euler_characteristic(X::EllipticSurface) +fibration(X::EllipticSurface) +weierstrass_chart(X::EllipticSurface) +weierstrass_chart_on_minimal_model(X::EllipticSurface) +weierstrass_model(X::EllipticSurface) +weierstrass_contraction(X::EllipticSurface) +fibration_on_weierstrass_model(X::EllipticSurface) +``` + +## Reducible fibers and the trivial lattice +```@docs +trivial_lattice(X::EllipticSurface) +reducible_fibers(S::EllipticSurface) +fiber_cartier(S::EllipticSurface, P::Vector = ZZ.([0,1])) +fiber_components(S::EllipticSurface, P; algorithm=:exceptional_divisors) +``` + +## Mordell Weil group and related lattices +Since we require that ``\pi \colon X \to C`` has a section, the generic fibre of ``\pi`` is an elliptic curve ``E/k(C)``. The group ``E(k(C))`` of its rational points is called the Mordell-Weil group. It is a finitely generated abelian group. In general it is difficult to compute. + +The height paring gives the free part of the Mordell-Weil group the structure of a quadratic lattice over the integers -- the so called Mordell-Weil lattice. + +```@docs +algebraic_lattice(X::EllipticSurface) +mordell_weil_sublattice(S::EllipticSurface) +mordell_weil_torsion(S::EllipticSurface) +section(X::EllipticSurface, P::EllipticCurvePoint) +basis_representation(X::EllipticSurface, D::AbsWeilDivisor) +``` + +### Updating the Mordell Weil group +Since the Mordell-Weil group is hard to compute, the user has to provide its generators, or at least generators of a subgroup +at construction of the surface. In some cases it may be convenient to enter the basis after creation, although it is in general not recommended. The following function are meant for this purpose. +```@docs +set_mordell_weil_basis!(X::EllipticSurface, mwl_basis::Vector{<:EllipticCurvePoint}) +update_mwl_basis!(S::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) +algebraic_lattice_primitive_closure!(S::EllipticSurface) +algebraic_lattice_primitive_closure(S::EllipticSurface, p) +``` + +## Morphisms +```@docs +isomorphism_from_generic_fibers +isomorphisms(X::EllipticSurface, Y::EllipticSurface) +translation_morphism(X::EllipticSurface, P::EllipticCurvePoint) +``` + +## Fibration hopping +The methods in this section are available only for elliptically fibered K3 surfaces. +A K3 surface ``X`` may admit several elliptic fibrations +```math +\pi \colon X \to \mathbb{P}^1. +``` +Fibration hopping is a way to compute them. +See e.g. [BE23](@cite) and [BZ23](@cite). + +A divisor ``F`` on ``X`` is called elliptic if it is primitive, isotropic and nef. +The linear system ``|F|`` of an elliptic divisor ``F`` induces a genus one fibration on ``X``. +Conversely, the class of a fiber ``F`` of an elliptic fibration is an elliptic divisor. +Two elliptic fibrations on ``X`` with elliptic divisors ``F_1`` and ``F_2`` are called ``n``-neighbors if their intersection number is ``F_1.F_2=n``. + +```@docs +linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int) +two_neighbor_step(X::EllipticSurface, F::Vector{QQFieldElem}) +elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) +pushforward_on_algebraic_lattices(f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) +``` +## Contact + +Please direct questions about this part of OSCAR to the following people: +* [Simon Brandhorst](https://www.math.uni-sb.de/ag/brandhorst/index.php?lang=en). +* [Matthias Zach](https://math.rptu.de/en/wgs/agag/people/members), + +You can ask questions in the [OSCAR Slack](https://www.oscar-system.org/community/#slack). + +Alternatively, you can [raise an issue on github](https://www.oscar-system.org/community/#how-to-report-issues). diff --git a/experimental/Schemes/src/Schemes.jl b/experimental/Schemes/src/Schemes.jl index 46573467e062..6a79c72052db 100644 --- a/experimental/Schemes/src/Schemes.jl +++ b/experimental/Schemes/src/Schemes.jl @@ -3,12 +3,8 @@ include("CoveredScheme.jl") include("ProjectiveModules.jl") include("SpaceGerms.jl") include("Tjurina.jl") -include("CoveredProjectiveSchemes.jl") include("Auxiliary.jl") -include("BlowupMorphismTypes.jl") -include("BlowupMorphism.jl") -include("elliptic_surface.jl") include("critical_locus.jl") include("ToricIdealSheaves/auxiliary.jl") diff --git a/experimental/Schemes/src/Types.jl b/experimental/Schemes/src/Types.jl index 59396ce9c518..e69de29bb2d1 100644 --- a/experimental/Schemes/src/Types.jl +++ b/experimental/Schemes/src/Types.jl @@ -1,185 +0,0 @@ -export AbsProjectiveScheme -export EmptyScheme -export ProjectiveScheme -export ProjectiveSchemeMor -export VarietyFunctionField -export VarietyFunctionFieldElem - -abstract type AbsProjectiveGluing{ - GluingType<:AbsGluing, - } -end - -@doc raw""" - LazyProjectiveGluing( - X::AbsProjectiveScheme, - Y::AbsProjectiveScheme, - BG::AbsGluing, - compute_function::Function, - gluing_data - ) - -Produce a container `pg` to host a non-computed `ProjectiveGluing` of ``X`` with ``Y``. - -The arguments consist of - - * the patches ``X`` and ``Y`` to be glued; - * a gluing `BG` of the `base_scheme`s of ``X`` and ``Y`` over which the - `ProjectiveGluing` to be computed sits; - * a function `compute_function` which takes a single argument `gluing_data` - of arbitrary type and actually carries out the computation; - * an arbitrary struct `gluing_data` that the user can fill with whatever - information is needed to properly feed their `compute_function`. - -The container `pg` can then be stored as the gluing of ``X`` and ``Y``. As soon -as it is asked about any data on the gluing beyond its two `patches`, it will -invoke the internally stored `compute_function` to actually carry out the computation -of the gluing and then serve the incoming request on the basis of that result. -The latter actual `ProjectiveGluing` will then be cached. -""" -mutable struct LazyProjectiveGluing{ - GluingType<:AbsGluing, - GluingDataType - } <: AbsProjectiveGluing{GluingType} - base_gluing::GluingType - patches::Tuple{AbsProjectiveScheme, AbsProjectiveScheme} - compute_function::Function - gluing_data::GluingDataType - underlying_gluing::AbsProjectiveGluing - - function LazyProjectiveGluing( - X::AbsProjectiveScheme, - Y::AbsProjectiveScheme, - BG::AbsGluing, - compute_function::Function, - gluing_data - ) - (base_scheme(X), base_scheme(Y)) == patches(BG) || error("gluing is incompatible with provided patches") - return new{typeof(BG), typeof(gluing_data)}(BG, (X, Y), compute_function, gluing_data) - end -end - -@doc raw""" - ProjectiveGluing( - G::GluingType, - incP::IncType, incQ::IncType, - f::IsoType, g::IsoType; - check::Bool=true - ) where {GluingType<:AbsGluing, IncType<:ProjectiveSchemeMor, IsoType<:ProjectiveSchemeMor} - -The `AbsProjectiveSchemeMorphism`s `incP` and `incQ` are open embeddings over open -embeddings of their respective `base_scheme`s. - - PX ↩ PU ≅ QV ↪ QY - π ↓ ↓ ↓ ↓ π - G : X ↩ U ≅ V ↪ Y - -This creates a gluing of the projective schemes `codomain(incP)` and `codomain(incQ)` -over a gluing `G` of their `base_scheme`s along the morphisms of `AbsProjectiveScheme`s -`f` and `g`, identifying `domain(incP)` and `domain(incQ)`, respectively. -""" -mutable struct ProjectiveGluing{ - GluingType<:AbsGluing, - IsoType1<:ProjectiveSchemeMor, - IncType1<:ProjectiveSchemeMor, - IsoType2<:ProjectiveSchemeMor, - IncType2<:ProjectiveSchemeMor, - } <: AbsProjectiveGluing{GluingType} - G::GluingType # the underlying gluing of the base schemes - inc_to_P::IncType1 - inc_to_Q::IncType2 - f::IsoType1 - g::IsoType2 - - ### - # Given two relative projective schemes and a gluing - # - # PX ↩ PU ≅ QV ↪ QY - # π ↓ ↓ ↓ ↓ π - # G : X ↩ U ≅ V ↪ Y - # - # this constructs the gluing of PX and QY along - # their open subsets PU and QV, given the two inclusions - # and isomorphisms over the gluing G in the base schemes. - function ProjectiveGluing( - G::GluingType, - incP::IncType1, incQ::IncType2, - f::IsoType1, g::IsoType2; - check::Bool=true - ) where {GluingType<:AbsGluing, IncType1<:ProjectiveSchemeMor,IncType2<:ProjectiveSchemeMor, IsoType1<:ProjectiveSchemeMor, IsoType2<:ProjectiveSchemeMor} - (X, Y) = patches(G) - (U, V) = gluing_domains(G) - @vprint :Gluing 1 "computing projective gluing\n" - @vprint :Gluing 2 "$(X), coordinates $(ambient_coordinates(X))\n" - @vprint :Gluing 2 "and\n" - @vprint :Gluing 2 "$(Y) coordinates $(ambient_coordinates(X))\n" - (fb, gb) = gluing_morphisms(G) - (PX, QY) = (codomain(incP), codomain(incQ)) - (PU, QV) = (domain(incP), domain(incQ)) - (base_scheme(PX) === X && base_scheme(QY) === Y) || error("base gluing is incompatible with the projective schemes") - domain(f) === codomain(g) === PU && domain(g) === codomain(f) === QV || error("maps are not compatible") - SPU = homogeneous_coordinate_ring(domain(f)) - SQV = homogeneous_coordinate_ring(codomain(f)) - @check begin - # check the commutativity of the pullbacks - all(y->(pullback(f)(SQV(OO(V)(y))) == SPU(pullback(fb)(OO(V)(y)))), gens(base_ring(OO(Y)))) || error("maps do not commute") - all(x->(pullback(g)(SPU(OO(U)(x))) == SQV(pullback(gb)(OO(U)(x)))), gens(base_ring(OO(X)))) || error("maps do not commute") - fc = map_on_affine_cones(f, check=false) - gc = map_on_affine_cones(g, check=false) - idCPU = compose(fc, gc) - idCPU == identity_map(domain(fc)) || error("composition of maps is not the identity") - idCQV = compose(gc, fc) - idCQV == identity_map(domain(gc)) || error("composition of maps is not the identity") - # idPU = compose(f, g) - # all(t->(pullback(idPU)(t) == t), gens(SPU)) || error("composition of maps is not the identity") - # idQV = compose(g, f) - # all(t->(pullback(idQV)(t) == t), gens(SQV)) || error("composition of maps is not the identity") - end - @vprint :Gluing 1 "done computing the projective gluing\n" - return new{GluingType, IsoType1, IncType1, IsoType2, IncType2}(G, incP, incQ, f, g) - end -end - -### Proper schemes π : Z → X over a covered base scheme X -# -# When {Uᵢ} is an affine covering of X, the datum stored -# consists of a list of projective schemes -# -# Zᵢ ⊂ ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) → Uᵢ -# -# with varying ambient spaces ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) and a list of -# identifications (transitions) -# -# Zᵢ ∩ π⁻¹(Uⱼ) ≅ Zⱼ ∩ π⁻¹(Uᵢ) -# -# of projective schemes over Uᵢ∩ Uⱼ for all pairs (i,j). -# -# These structs are designed to accommodate blowups of -# covered schemes along arbitrary centers, as well as -# projective bundles. - -@attributes mutable struct CoveredProjectiveScheme{BRT} <: Scheme{BRT} - Y::AbsCoveredScheme # the base scheme - BC::Covering # the reference covering of the base scheme - patches::IdDict{AbsAffineScheme, AbsProjectiveScheme} # the projective spaces over the affine patches in the base covering - gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing} # the transitions sitting over the affine patches in the gluing domains of the base scheme - - function CoveredProjectiveScheme( - Y::AbsCoveredScheme, - C::Covering, - projective_patches::IdDict{AbsAffineScheme, AbsProjectiveScheme}, - projective_gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing}; - check::Bool=true - ) - C in coverings(Y) || error("covering not listed") - for P in values(projective_patches) - any(x->x===base_scheme(P), patches(C)) || error("base scheme not found in covering") - end - for (U, V) in keys(gluings(C)) - (U, V) in keys(projective_gluings) || error("not all projective gluings were provided") - end - return new{base_ring_type(Y)}(Y, C, projective_patches, projective_gluings) - end -end - - diff --git a/experimental/Schemes/src/elliptic_surface.jl b/experimental/Schemes/src/elliptic_surface.jl deleted file mode 100644 index 594a9212e40c..000000000000 --- a/experimental/Schemes/src/elliptic_surface.jl +++ /dev/null @@ -1,3232 +0,0 @@ -export elliptic_surface, trivial_lattice, weierstrass_model, weierstrass_chart, algebraic_lattice, zero_section, section, weierstrass_contraction, fiber_components, generic_fiber, reducible_fibers, fibration_type, mordell_weil_lattice, elliptic_parameter, set_mordell_weil_basis!, EllipticSurface, weierstrass_chart_on_minimal_model, transform_to_weierstrass - -@doc raw""" - EllipticSurface{BaseField<:Field, BaseCurveFieldType} <: AbsCoveredScheme{BaseField} - -The type of a relatively minimal elliptic surface. - -A genus $1$-fibration is a proper map -$\pi \colon X \to C$ to a curve $C$ whose fibers are curves of -(arithmetic) genus $1$. -The fibration is relatively minimal if its fibers do not contain any ``(-1)``-curves. -We call the fibration elliptic if it comes equipped with a section. -This turns the generic fiber of $\pi$ into an elliptic curve $E/k(C)$ where -$k(C)$ is the function field of the curve $C$. - -For now functionality is restricted to $C = \mathbb{P}^1$. -""" -@attributes mutable struct EllipticSurface{BaseField<:Field, BaseCurveFieldType} <: AbsCoveredSurface{BaseField} - Y::CoveredScheme{BaseField} # the underlying_scheme - E::EllipticCurve{BaseCurveFieldType} - MWL::Vector{EllipticCurvePoint{BaseCurveFieldType}} # basis for the mordell weil group - MWLtors::Vector{EllipticCurvePoint{BaseCurveFieldType}} # torsion sections - Weierstrasschart::AbsAffineScheme - Weierstrassmodel::CoveredScheme - inc_Weierstrass::CoveredClosedEmbedding # inclusion of the weierstrass chart in its ambient projective bundle - inc_Y::CoveredClosedEmbedding # inclusion of Y in its ambient blown up projective bundle - euler_characteristic::Int - resolution_strategy::Symbol - # the following are temporary until we have a dedicated type for - # iterated blow ups - blowup::AbsCoveredSchemeMorphism - blowups::Vector{<:AbsCoveredSchemeMorphism} - # exceptionals not used for now - ambient_blowups::Vector{<:BlowupMorphism} - ambient_exceptionals::Vector{<:EffectiveCartierDivisor} - fibration::AbsCoveredSchemeMorphism # the projection to IP^1 - fibration_weierstrass_model::AbsCoveredSchemeMorphism # the projection from the Weierstrass model - - function EllipticSurface( - generic_fiber::EllipticCurve{F}, - euler_characteristic::Int, - mwl_basis::Vector{<:EllipticCurvePoint}; - resolution_strategy::Symbol=:iterative - ) where F - B = typeof(coefficient_ring(base_ring(base_field(generic_fiber)))) - S = new{B,F}() - S.E = generic_fiber - S.MWL = mwl_basis - S.euler_characteristic = euler_characteristic - set_attribute!(S, :is_irreducible=>true) - set_attribute!(S, :is_reduced=>true) - set_attribute!(S, :is_integral=>true) - set_attribute!(S, :is_equidimensional=>true) - S.resolution_strategy = resolution_strategy - return S - end - -end - -base_ring(X::EllipticSurface) = coefficient_ring(base_ring(base_field(generic_fiber(X)))) - -@doc raw""" - set_mordell_weil_basis!(X::EllipticSurface, mwl_basis::Vector{EllipticCurvePoint}) - -Set a basis for the Mordell-Weil lattice of ``X`` or at least of a sublattice. - -This invalidates previous computations depending on the generators of the -Mordell Weil lattice such as the `algebraic_lattice`. Use with care. - -The points in `mwl_basis` must be linearly independent. -""" -function set_mordell_weil_basis!(X::EllipticSurface, mwl_basis::Vector{<:EllipticCurvePoint}) - @req all(parent(P) == generic_fiber(X) for P in mwl_basis) "points must lie on the generic fiber" - X.MWL = mwl_basis - # clear old computations - if has_attribute(X, :algebraic_lattice) - delete!(X.__attrs, :algebraic_lattice) - end - if has_attribute(X, :mordell_weil_lattice) - delete!(X.__attrs, :mordell_weil_lattice) - end -end - -@doc raw""" - elliptic_surface(generic_fiber::EllipticCurve, - euler_characteristic::Int, - mwl_gens::Vector{<:EllipticCurvePoint}=EllipticCurvePoint[]; - is_basis::Bool=true) - -> EllipticSurface - -Return the relatively minimal elliptic surface with generic fiber ``E/k(t)``. - -This is also known as the Kodaira-Néron model of ``E``. - -Input: -- `generic_fiber` -- an elliptic curve over a function field -- `euler_characteristic` -- the Euler characteristic of the Kodaira-Néron model of ``E``. -- `mwl_gens` -- a vector of rational points of the generic fiber -- `is_basis` -- if set to `false` compute an LLL-reduced basis from `mwl_gens` - - -# Examples -```jldoctest -julia> Qt, t = polynomial_ring(QQ, :t); - -julia> Qtf = fraction_field(Qt); - -julia> E = elliptic_curve(Qtf, [0,0,0,0,t^5*(t-1)^2]); - -julia> X3 = elliptic_surface(E, 2) -Elliptic surface - over rational field -with generic fiber - -x^3 + y^2 - t^7 + 2*t^6 - t^5 - -``` -""" -function elliptic_surface(generic_fiber::EllipticCurve{BaseField}, - euler_characteristic::Int, - mwl_gens::Vector{<:EllipticCurvePoint}=EllipticCurvePoint[]; - resolution_strategy::Symbol=:iterative, - is_basis::Bool=true) where { - BaseField <: FracFieldElem{<:PolyRingElem{<:FieldElem}}} - @req all(parent(i)==generic_fiber for i in mwl_gens) "not a vector of points on $(generic_fiber)" - S = EllipticSurface(generic_fiber, euler_characteristic, mwl_gens; resolution_strategy) - if is_basis - return S - end - update_mwl_basis!(S, mwl_gens) - return S -end - -@doc raw""" - update_mwl_basis!(S::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) - -Compute a reduced basis of the sublattice of the Mordell-Weil lattice spanned -by `mwl_gens` and set these as the new generators of the Mordell-Weil lattice of -`S`. -""" -function update_mwl_basis!(S::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) - mwl, mwl_basis = _compute_mwl_basis(S, mwl_gens) - set_mordell_weil_basis!(S, mwl_basis) -end - -@doc raw""" - algebraic_lattice_primitive_closure(S::EllipticSurface, p) -> Vector{<:EllipticCurvePoint} - -Return sections ``P_1,\dots P_n`` of the generic fiber, such that together with -the generators of the algebraic lattice ``A``, they generate -``(1/p A \cap N)`` where ``N`` is the numerical lattice of ``S``. - -This proceeds by computing division points in the Mordell-Weil group -and using information coming from the discriminant group of the algebraic lattice -to do so. -""" -algebraic_lattice_primitive_closure(S::EllipticSurface, p) = algebraic_lattice_primitive_closure(S, ZZ(p)) - -function algebraic_lattice_primitive_closure(S::EllipticSurface, p::ZZRingElem) - L = algebraic_lattice(S)[3] - @req is_even(L) "not implemented" - Ld = intersect(dual(L) , (1//p * L)) - D = torsion_quadratic_module(Ld, L, modulus = 1, modulus_qf=2) - candidates = [x for x in D if !iszero(x) && iszero(quadratic_product(x))] - t = length(trivial_lattice(S)[1]) - r = rank(L) - cc = [(x->mod(x,p)).(p*lift(c)[t+1:end]) for c in candidates] - unique!(cc) - cc = [c for c in cc if !iszero(c)] - pts = [division_points(sum(v[i]*S.MWL[i] for i in 1:(r-t)),p) for v in cc] - return [i[1] for i in pts if length(i)>0] -end - -function algebraic_lattice_primitive_closure!(S::EllipticSurface, prime) - pts = algebraic_lattice_primitive_closure(S, prime) - update_mwl_basis!(S, vcat(pts, S.MWL)) - return pts -end - -@doc raw""" - algebraic_lattice_primitive_closure!(S::EllipticSurface) - -Compute the primitive closure of the algebraic lattice of `S` inside its -numerical lattice and update the generators of its Mordell--Weil group accordingly. - -The algorithm works by computing suitable divison points in its Mordell Weil group. -""" -function algebraic_lattice_primitive_closure!(S::EllipticSurface) - L = algebraic_lattice(S)[3] - for p in prime_divisors(ZZ(det(L))) - while true - pts = algebraic_lattice_primitive_closure!(S, p) - if length(pts)==0 - break - end - end - end - return S -end - -kodaira_neron_model(E::EllipticCurve) = elliptic_surface(E) - -function underlying_scheme(S::EllipticSurface) - if isdefined(S,:Y) - return S.Y - end - # trigger the computation - weierstrass_contraction(S) - return underlying_scheme(S) -end - -@doc raw""" - generic_fiber(S::EllipticSurface) -> EllipticCurve - -Return the generic fiber as an elliptic curve. -""" -generic_fiber(S::EllipticSurface) = S.E - -@doc raw""" - weierstrass_chart(X::EllipticSurface) - -Return the Weierstrass chart of ``X`` on its `weierstrass_model`. -""" -weierstrass_chart(X::EllipticSurface) = weierstrass_model(X)[1][1][1] - -@doc raw""" - weierstrass_chart_on_minimal_model(X::EllipticSurface) - -Return an affine chart ``U`` of ``X`` which is isomorphic to the `weierstrass_chart` -of ``X`` on its `weierstrass_model`, but with all singular fibers removed. - -More precisely, the affine coordinates of ``U`` are ``(x,y,t)`` and the chart is -constructed as the vanishing locus of -``y^2 + a_1(t) xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6`` -minus the reducible singular fibers. -""" -weierstrass_chart_on_minimal_model(X::EllipticSurface) = X[1][1] - -@doc raw""" - euler_characteristic(X::EllipticSurface) -> Int - -Return $\chi(\mathcal{O}_X)$. -""" -euler_characteristic(X::EllipticSurface) = X.euler_characteristic - -@doc raw""" - algebraic_lattice(X) -> Vector{AbsWeilDivisor}, ZZLat - -Return the sublattice `L` of ``Num(X)`` spanned by fiber components, -torsion sections and the sections provided at the construction of ``X``. - -The first return value is the basis of the ambient space of `L`. -The second consists of additional generators for `L` coming from torsion sections. -The third is ``L``. -""" -@attr Any function algebraic_lattice(X::EllipticSurface) - return _algebraic_lattice(X,X.MWL) -end - -function _algebraic_lattice(X::EllipticSurface, mwl_basis::Vector{<:EllipticCurvePoint}) - basisTriv, GTriv = trivial_lattice(X) - r = length(basisTriv) - l = length(mwl_basis) - sections = [section(X, i) for i in mwl_basis] - n = l+r - GA = zero_matrix(ZZ, n, n) - GA[1:r,1:r] = GTriv - GA[r+1:n,r+1:n] = -euler_characteristic(X)*identity_matrix(ZZ, l) - basisA = vcat(basisTriv, sections) - @vprint :EllipticSurface 2 "computing intersection numbers\n" - for i in 1:n - @vprint :EllipticSurface 3 "\nrow $(i): \n" - for j in max(i + 1, r + 1):n - if i!=2 && i <= r - I = components(basisA[i])[1] - J = components(basisA[j])[1] - if isone(I+J) - ij = 0 - else - ij = 1 - end - else - @vprint :EllipticSurface 4 "$(j) " - ij = intersect(basisA[i],basisA[j]) - end - GA[i,j] = ij - GA[j,i] = GA[i,j] - end - end - GA_QQ = change_base_ring(QQ,GA) - # primitive closure of the trivial lattice comes from torsion sections - tors = [section(X, h) for h in mordell_weil_torsion(X)] - torsV = QQMatrix[] - for T in tors - @vprint :EllipticSurface 2 "computing basis representation of torsion point $(T)\n" - vT = zero_matrix(QQ, 1, n) - for i in 1:r - if i== 2 - vT[1,i] = intersect(basisA[i], T) - else - @assert length(components(basisTriv[i])) == 1 - I = sum(components(basisA[i])) - J = components(T)[1] - if !isone(I+J) - @assert i!=2 # O does not meet any torsion section - vT[1,i] = 1 - end - end - end - for i in r+1:n - vT[1,i] = intersect(T,basisA[i]) - end - push!(torsV, solve(GA_QQ, vT; side=:left)) - end - gen_tors = zip(tors, torsV) - push!(torsV, identity_matrix(QQ,n)) - V = quadratic_space(QQ,GA) - L = lattice(V, reduce(vcat,torsV), isbasis=false) - return basisA, collect(gen_tors), L -end - -@doc raw""" - mordell_weil_lattice(S::EllipticSurface) -> Vector{EllipticCurvePoint}, ZZLat - -Return the (sublattice) of the Mordell-Weil lattice of ``S`` spanned -by the sections of ``S`` supplied at its construction. - -The Mordell Weil-Lattice is represented in the same vector space as the -algebraic lattice (with quadratic form rescaled by ``-1``). -""" -@attr ZZLat function mordell_weil_lattice(S::EllipticSurface) - NS = algebraic_lattice(S)[3] - t = length(trivial_lattice(S)[1]) - trivNS = basis_matrix(NS)[1:t,:] - R = basis_matrix(NS)[t+1:end,:] - V = ambient_space(NS) - P = orthogonal_projection(V, trivNS) - mwl = rescale(lattice(V,R*P.matrix),-1) - return mwl -end - -@doc raw""" - mordell_weil_torsion(S::EllipticSurface) -> Vector{EllipticCurvePoint} - -Return the torsion part of the Mordell-Weil group of the generic fiber of ``S``. -""" -@attr Any function mordell_weil_torsion(S::EllipticSurface) - E = generic_fiber(S) - O = E([0,1,0]) - N = trivial_lattice(S)[2] - tors = EllipticCurvePoint[] - d = det(N) - for p in prime_divisors(d) - if valuation(d, p) == 1 - continue - end - r = 1 - i = 0 - dp = typeof(O)[] - while true - i = i+1 - @vprint :EllipticSurface 2 "computing $(p^i)-torsion" - dp = division_points(O, p^i) - if length(dp) == r - break - end - r = length(dp) - end - for pt in dp - if pt != O - push!(tors, pt) - end - end - end - return tors -end - -function Base.show(io::IO, S::EllipticSurface) - io = pretty(io) - if is_terse(io) - print(io, "Elliptic surface") - else - E = generic_fiber(S) - print(io, "Elliptic surface with generic fiber ", equation(E)) - end -end - -function Base.show(io::IO, ::MIME"text/plain", S::EllipticSurface) - io = pretty(io) - println(io, "Elliptic surface") - println(io, Indent(), "over ", Lowercase(), base_ring(S)) - println(io, Dedent(), "with generic fiber") - print(io, Indent(), Lowercase(), equation(generic_fiber(S)), Dedent()) - if isdefined(S, :Y) - println(io) - println(io, "and relatively minimal model") - print(io, Indent(), Lowercase(), S.Y, Dedent()) - end - print(io, Dedent()) -end - -@doc raw""" - weierstrass_model(X::EllipticSurface) -> CoveredScheme, CoveredClosedEmbedding - -Return the Weierstrass model ``S`` of ``X`` and the inclusion in -its ambient projective bundle -$$S\subseteq \mathbb{P}( \mathcal{O}_{\mathbb{P}^1}(-2s) \oplus \mathcal{O}_{\mathbb{P}^1}(-3s) \oplus \mathcal{O}_{\mathbb{P}^1}).$$ -""" -function weierstrass_model(X::EllipticSurface) - if isdefined(X, :Weierstrassmodel) - return X.Weierstrassmodel, X.inc_Weierstrass - end - - s = euler_characteristic(X) - E = generic_fiber(X) - - kt = base_ring(base_field(E)) - k = coefficient_ring(kt) - - IP1 = projective_space(k, 1) - c = standard_covering(IP1) - # rename the variables on the affine charts - # to a more readable version - if k isa FqField - OO(c[1]).data.S = [:t] - OO(c[2]).data.S = [:s] - else - OO(c[1]).S = [:t] - OO(c[2]).S = [:s] - end - - O0 = twisting_sheaf(IP1, 0) - O4 = twisting_sheaf(IP1, -2*s) - O6 = twisting_sheaf(IP1, -3*s) - - bundleE = direct_sum([O0, O4, O6]) - - P_proj = projectivization(bundleE, var_names=[:z, :x, :y]) - P = covered_scheme(P_proj) - pr = covered_projection_to_base(P_proj) - @assert has_decomposition_info(default_covering(P)) - - # Create the singular Weierstrass model S of the elliptic K3 surface X - a = a_invariants(E) - U = affine_charts(P)[1] # the standard Weierstrass chart - (x, y, t) = gens(OO(U)) - @assert all(denominator(i)==1 for i in a) - a = [numerator(a)(t) for a in a] - (a1,a2,a3,a4,a6) = a - ft = y^2 + a1*x*y + a3*y - (x^3 + a2*x^2 + a4*x+a6) - I = IdealSheaf(P, U, [ft]; check=false) - - inc_S = CoveredClosedEmbedding(P, I) - Scov = domain(inc_S) # The ADE singular elliptic K3 surface - X.Weierstrasschart = Scov[1][1] - X.fibration_weierstrass_model = compose(inc_S, pr) - - X.Weierstrassmodel = Scov - X.inc_Weierstrass = inc_S - - set_attribute!(Scov, :is_irreducible=>true) - set_attribute!(Scov, :is_reduced=>true) - set_attribute!(Scov, :is_integral=>true) - set_attribute!(Scov, :is_equidimensional=>true) - return Scov, inc_S -end - -@doc raw""" - _separate_singularities!(X::EllipticSurface) -> Covering - -Create a covering of the ambient projective bundle $P$ -of the Weierstrass model $S$ of $X$ such that each chart -(of $X$) contains at most one singular point of $S$. -Append this covering to the list of coverings of $X$ and return it. -""" -function _separate_singularities!(X::EllipticSurface) - S, inc_S = weierstrass_model(X) - P = codomain(inc_S) - - I_sing = ideal_sheaf_of_singular_locus(S) - I_sing_P = SimplifiedIdealSheaf(pushforward(inc_S)(I_sing)) - - # Refine the covering over the reducible singular fibers - # to make sure that there is only a single singular point in each chart - refined_charts = AbsAffineScheme[] - U = P[1][1] # the weierstrass_chart - IsingU = I_sing_P(U)::MPolyIdeal - if isone(IsingU) - # we want one smooth weierstrass chart - push!(refined_charts, U) - set_attribute!(U, :is_smooth => true) - else - # there is at most one singularity in every fiber - # project the singular locus to an affine chart of P1 - disc = gens(eliminate(IsingU, coordinates(U)[1:2]))[1] - # The t-coordinates of the reducible fibers - redfib = [f[1] for f in factor(disc)] - # One chart with all reducible fibers taken out - UU = PrincipalOpenSubset(U, redfib) - set_attribute!(UU, :is_smooth => true) - push!(refined_charts, UU) - if length(redfib)==1 - # We need to recreate U as a PrincipalOpenSubset of itself here - # in order to maintain the correct tree-structure for refinements. - # In any Covering no patch is allowed to be an ancestor of another. - push!(refined_charts, PrincipalOpenSubset(U, one(OO(U)))) - else - for i in 1:length(redfib) - # We take out all but the i-th singular fiber - r = copy(redfib) - g = r[i] - deleteat!(r, i) - Uref = PrincipalOpenSubset(U, r) - push!(refined_charts, Uref) - end - end - end - - # Create a chart which contains the fiber over s=0 - # and no other reducible singular fibers - # these are visible in the charts that we have already - # i.e. we add the fiber at s=0 and remove all other singular fibers - V = P[1][4] - IsingV = I_sing_P(V) - if isone(IsingV) - push!(refined_charts, V) - else - # reducible singular fibers - disc = gens(eliminate(IsingV, coordinates(V)[1:2]))[1] - (x,y,s) = coordinates(V) - b, d = divides(disc, s) - if b - disc = d - end - redfib = [f[1] for f in factor(disc)] - if length(redfib)> 0 - push!(refined_charts, PrincipalOpenSubset(V, redfib)) - else - push!(refined_charts, V) - end - end - - # no extra singularities in the X = 1 chart - # therefore we just exclude all the singularities visible here - for W in [P[1][2],P[1][5]] - Ising = I_sing_P(W) - if isone(Ising) - push!(refined_charts, W) - continue - end - (z,y,s_or_t) = coordinates(W) - # reducible singular fibers - local disc = gens(eliminate(Ising, [z, s_or_t]))[1] - local redfib = [p for (p,e) in factor(disc)] - push!(refined_charts, PrincipalOpenSubset(W, redfib)) - end - - # no extra singularities on the the zero section - # This is the Y = 1 chart - # therefore we just exclude all the singularities visible here - for W in [P[1][3],P[1][6]] - local Ising = I_sing_P(W) - if isone(Ising) - push!(refined_charts, W) - continue - end - local (z,x,s_or_t) = coordinates(W) - # reducible singular fibers - local disc = gens(eliminate(Ising, [x, s_or_t]))[1] - local redfib = [p for (p,e) in factor(disc)] - push!(refined_charts, PrincipalOpenSubset(W, redfib)) - end - - - Cref = Covering(refined_charts) - inherit_gluings!(Cref, P[1]) - push!(P.coverings, Cref) - @assert has_decomposition_info(default_covering(P)) - inherit_decomposition_info!(P, Cref) - @assert has_decomposition_info(Cref) - # Now we have an extra covering where each chart just contains a single singularity - - @assert scheme(I_sing) === S - @assert scheme(I_sing_P) === P - return Cref -end - -@doc raw""" - weierstrass_contraction(X::EllipticSurface) -> SchemeMor - -Return the contraction morphism of ``X`` to its Weierstrass model. - -This triggers the computation of the `underlying_scheme` of ``X`` -as a blowup from its Weierstrass model. It may take a few minutes. -""" -function weierstrass_contraction(X::EllipticSurface) - algorithm = X.resolution_strategy - if algorithm == :iterative - return weierstrass_contraction_iterative(X) - elseif algorithm == :simultaneous - return weierstrass_contraction_simultaneous(X) - else - error("algorithm not recognized") - end -end - -function weierstrass_contraction_simultaneous(Y::EllipticSurface) - if isdefined(Y, :blowup) - return Y.blowup - end - S, inc_S = weierstrass_model(Y) - @assert has_attribute(S, :is_equidimensional) && get_attribute(S, :is_equidimensional) === true - - X0 = codomain(inc_S) - Y0 = S - set_attribute!(Y0, :is_reduced=>true) - set_attribute!(Y0, :is_irreducible=>true) - set_attribute!(Y0, :is_equidimensional=>true) - inc_Y0 = inc_S - I_sing_Y0 = AbsIdealSheaf[ideal_sheaf_of_singular_locus(Y0)] - #I_sing_Y0 = AbsIdealSheaf[simplify(ideal_sheaf_of_singular_locus(Y0))] - I_sing_X0 = simplify.(pushforward(inc_Y0).(I_sing_Y0)) - - # Prepare a covering which has a permanent weierstrass chart - U0 = X0[1][1] - disc = numerator(discriminant(generic_fiber(Y))) - U = PrincipalOpenSubset(U0, evaluate(disc, gens(OO(U0))[3])) - _find_chart(U, default_covering(X0)) - Cref = Covering(vcat([U], affine_charts(X0))) - inherit_gluings!(Cref, X0[1]) - push!(X0.coverings, Cref) - @assert has_decomposition_info(default_covering(X0)) - inherit_decomposition_info!(X0, Cref) - @assert has_decomposition_info(Cref) - - ambient_exceptionals = EffectiveCartierDivisor[] - varnames = [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o,:p,:q,:r,:u,:v,:w] - projectionsX = BlowupMorphism[] - projectionsY = AbsCoveredSchemeMorphism[] - count = 0 - - @vprint :EllipticSurface 2 "Blowing up Weierstrass model simultaneously in all singular points\n" - while true - count = count+1 - @vprint :EllipticSurface 1 "blowup number: $(count)\n" - @vprint :EllipticSurface 1 "number of ideal sheaves to be blown up: $(length(I_sing_X0))\n" - if length(I_sing_X0)==0 - # stop if smooth - break - end - # make sure we have a Weierstrass chart kept. - if count == 1 - cov = Cref - else - cov = simplified_covering(X0) - end - # take the first ideal sheaf and blow it up - J = SimplifiedIdealSheaf(I_sing_X0[1]) - pr_X1 = blow_up(J, covering=cov, var_name=varnames[1+mod(count, length(varnames))]) - - X1 = domain(pr_X1) - @vprint :EllipticSurface 1 "$(X1)\n" - E1 = exceptional_divisor(pr_X1) - - @vprint :EllipticSurface 2 "computing strict transforms\n" - # compute the exceptional divisors - ambient_exceptionals = EffectiveCartierDivisor[strict_transform(pr_X1, e) for e in ambient_exceptionals] - # move the divisors coming originally from S up to the next chart - push!(ambient_exceptionals, E1) - - Y1, inc_Y1, pr_Y1 = strict_transform(pr_X1, inc_Y0) - # Speed up the computation of singular loci - set_attribute!(Y1, :is_irreducible=> true) - set_attribute!(Y1, :is_reduced=>true) - set_attribute!(Y1, :is_integral=>true) - set_attribute!(Y1, :is_equidimensional=>true) - - # transform the singular loci - I_sing_X0 = AbsIdealSheaf[pullback(pr_X1, J) for J in I_sing_X0[2:end]] - - # Add eventual new components - @vprint :EllipticSurface 2 "computing singular locus\n" - I_sing_new = ideal_sheaf_of_singular_locus(Y1; focus=pullback(inc_Y1, ideal_sheaf(E1))) - #I_sing_new = pushforward(inc_Y1, I_sing_new) + ideal_sheaf(E1) # new components only along the exc. set - I_sing_new = simplify(pushforward(inc_Y1, I_sing_new)) - - @vprint :EllipticSurface 2 "decomposing singular locus\n" - !is_one(I_sing_new) && push!(I_sing_X0, I_sing_new) - - push!(projectionsX, pr_X1) - push!(projectionsY, pr_Y1) - simplify!(Y1) - - # set up for the next iteration - Y0 = Y1 - inc_Y0 = inc_Y1 - X0 = X1 - # Speed up the computation of singular loci - set_attribute!(Y0, :is_irreducible=> true) - set_attribute!(Y0, :is_reduced=>true) - set_attribute!(Y0, :is_integral=>true) - set_attribute!(Y0, :is_equidimensional=>true) - set_attribute!(X0, :is_irreducible=> true) - set_attribute!(X0, :is_reduced=>true) - set_attribute!(X0, :is_integral=>true) - end - Y.Y = Y0 - Y.blowups = projectionsY - - # We need to rewrap the last maps so that the domain is really Y - last_pr = pop!(projectionsY) - last_pr_wrap = CoveredSchemeMorphism(Y, codomain(last_pr), covering_morphism(last_pr)) - - push!(projectionsY, last_pr_wrap) - Y.ambient_blowups = projectionsX - - Y.ambient_exceptionals = ambient_exceptionals - piY = CompositeCoveredSchemeMorphism(reverse(projectionsY)) - Y.blowup = piY - - inc_Y0_wrap = CoveredClosedEmbedding(Y, codomain(inc_Y0), covering_morphism(inc_Y0), check=false) - Y.inc_Y = inc_Y0_wrap - - set_attribute!(Y, :is_irreducible=> true) - set_attribute!(Y, :is_reduced=>true) - set_attribute!(Y, :is_integral=>true) - return piY -end - - -function weierstrass_contraction_iterative(Y::EllipticSurface) - if isdefined(Y, :blowup) - return Y.blowup - end - S, inc_S = weierstrass_model(Y) - @assert has_attribute(S, :is_equidimensional) && get_attribute(S, :is_equidimensional) === true - Crefined = _separate_singularities!(Y) - # Blow up singular points (one at a time) until smooth - # and compute the strict transforms of the `divisors` - # collect the exceptional divisors - # blowup ambient spaces: X0 → X ⊂ - # blowup pi: Y → (S singular weierstrass model) - # - # initialization for the while loop - X0 = codomain(inc_S) - Y0 = S - set_attribute!(Y0, :is_reduced=>true) - set_attribute!(Y0, :is_irreducible=>true) - set_attribute!(Y0, :is_equidimensional=>true) - inc_Y0 = inc_S - I_sing_Y0 = maximal_associated_points(ideal_sheaf_of_singular_locus(Y0))::Vector{<:AbsIdealSheaf} - I_sing_X0 = pushforward(inc_Y0).(I_sing_Y0) - - - ambient_exceptionals = EffectiveCartierDivisor[] - varnames = [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o,:p,:q,:r,:u,:v,:w] - projectionsX = BlowupMorphism[] - projectionsY = AbsCoveredSchemeMorphism[] - count = 0 - - @vprint :EllipticSurface 2 "Blowing up Weierstrass model\n" - @vprint :EllipticSurface 2 "in $(Crefined)\n" - while true - count = count+1 - @vprint :EllipticSurface 1 "blowup number: $(count)\n" - @vprint :EllipticSurface 1 "number of singular points: $(length(I_sing_X0))\n" - if length(I_sing_X0)==0 - # stop if smooth - break - end - # make sure there is only one singular point per chart - if count == 1 - cov = Crefined - else - # the following leads to difficult bugs - cov = simplified_covering(X0) - end - # take the first singular point and blow it up - J = SimplifiedIdealSheaf(I_sing_X0[1]) - pr_X1 = blow_up(J, covering=cov, var_name=varnames[1+mod(count, length(varnames))]) - - # Set the attribute so that the strict_transform does some extra work - #isomorphism_on_open_subset(pr_X1) - - X1 = domain(pr_X1) - @vprint :EllipticSurface 1 "$(X1)\n" - E1 = exceptional_divisor(pr_X1) - - @vprint :EllipticSurface 2 "computing strict transforms\n" - # compute the exceptional divisors - ambient_exceptionals = EffectiveCartierDivisor[strict_transform(pr_X1, e) for e in ambient_exceptionals] - # move the divisors coming originally from S up to the next chart - push!(ambient_exceptionals, E1) - - Y1, inc_Y1, pr_Y1 = strict_transform(pr_X1, inc_Y0) - # Speed up the computation of singular loci - set_attribute!(Y1, :is_irreducible=> true) - set_attribute!(Y1, :is_reduced=>true) - set_attribute!(Y1, :is_integral=>true) - set_attribute!(Y1, :is_equidimensional=>true) - - # transform the singular loci - I_sing_X0 = AbsIdealSheaf[pullback(pr_X1, J) for J in I_sing_X0[2:end]] - - # Add eventual new components - @vprint :EllipticSurface 2 "computing singular locus\n" - I_sing_new = ideal_sheaf_of_singular_locus(Y1; focus=pullback(inc_Y1, ideal_sheaf(E1))) - #I_sing_new = pushforward(inc_Y1, I_sing_new) + ideal_sheaf(E1) # new components only along the exc. set - I_sing_new = pushforward(inc_Y1, I_sing_new) - - @vprint :EllipticSurface 2 "decomposing singular locus\n" - I_sing_X0 = vcat(I_sing_X0, maximal_associated_points(I_sing_new)) - - push!(projectionsX, pr_X1) - push!(projectionsY, pr_Y1) - simplify!(Y1) - - # set up for the next iteration - Y0 = Y1 - inc_Y0 = inc_Y1 - X0 = X1 - # Speed up the computation of singular loci - set_attribute!(Y0, :is_irreducible=> true) - set_attribute!(Y0, :is_reduced=>true) - set_attribute!(Y0, :is_integral=>true) - set_attribute!(Y0, :is_equidimensional=>true) - set_attribute!(X0, :is_irreducible=> true) - set_attribute!(X0, :is_reduced=>true) - set_attribute!(X0, :is_integral=>true) - end - Y.Y = Y0 - Y.blowups = projectionsY - - # We need to rewrap the last maps so that the domain is really Y - last_pr = pop!(projectionsY) - last_pr_wrap = CoveredSchemeMorphism(Y, codomain(last_pr), covering_morphism(last_pr)) - #set_attribute!(last_pr_wrap, :isomorphism_on_open_subset, get_attribute(last_pr, :isomorphism_on_open_subset)) - - push!(projectionsY, last_pr_wrap) - Y.ambient_blowups = projectionsX - - Y.ambient_exceptionals = ambient_exceptionals - piY = CompositeCoveredSchemeMorphism(reverse(projectionsY)) - Y.blowup = piY - - inc_Y0_wrap = CoveredClosedEmbedding(Y, codomain(inc_Y0), covering_morphism(inc_Y0), check=false) - Y.inc_Y = inc_Y0_wrap - - set_attribute!(Y, :is_irreducible=> true) - set_attribute!(Y, :is_reduced=>true) - set_attribute!(Y, :is_integral=>true) - return piY -end - -function _reducible_fibers_disc(X::EllipticSurface; sort::Bool=true) - E = generic_fiber(X) - j = j_invariant(E) - d = numerator(discriminant(E)) - kt = parent(d) - k = coefficient_ring(kt) - sing = Vector{elem_type(k)}[] - for (p,v) in factor(d) - if v == 1 - continue - end - r = k.(roots(p)) - if length(r) == 0 - error("not all reducible fibers are visible over $(base_ring(S))") - end - @assert length(r) ==1 - rt = r[1] - if v == 2 - # not a type II fiber - if j!=0 - push!(sing, [rt,k(1)]) - end - end - if v > 2 - push!(sing, [rt,k(1)]) - end - end - if sort - sort!(sing, by=x->by_total_order(x[1])) - end - # fiber over infinity is always last (if it is there) - if degree(d) <= 12*euler_characteristic(X) - 2 - push!(sing, k.([1, 0])) - end - return sing -end - -function by_total_order(x::FqFieldElem) - return [lift(ZZ, i) for i in absolute_coordinates(x)] -end - -by_total_order(x::QQFieldElem) = x - -function by_total_order(x::NumFieldElem) - return absolute_coordinates(x) -end - -# global divisors0 = [strict_transform(pr_X1, e) for e in divisors0] -# exceptionals_res = [pullback(inc_Y0)(e) for e in exceptionals] -@doc raw""" - _trivial_lattice(S::EllipticSurface; reducible_singular_fibers_in_PP1=_reducible_fibers_disc(S)) - -Internal function. Returns a list consisting of: -- basis of the trivial lattice -- gram matrix -- fiber_components without multiplicities - -The keyword argument `reducible_singular_fibers_in_PP1` must be a list of vectors of length `2` over -the base field representing the points in projective space over which there are reducible fibers. -Specify it to force this ordering of the basis vectors of the ambient space of the `algebraic_lattice` -""" -function _trivial_lattice(S::EllipticSurface; reducible_singular_fibers_in_PP1=_reducible_fibers_disc(S)) - get_attribute!(S, :_trivial_lattice) do - O = zero_section(S) - pt0, F = fiber(S) - set_attribute!(components(O)[1], :_self_intersection, -euler_characteristic(S)) - basisT = [F, O] - grams = [ZZ[0 1;1 -euler_characteristic(S)]] - sing = reducible_singular_fibers_in_PP1 - f = [[pt, fiber_components(S,pt)] for pt in sing] - fiber_componentsS = [] - for (pt, ft) in f - @vprint :EllipticSurface 2 "normalizing fiber: over $pt \n" - Ft0 = standardize_fiber(S, ft) - @vprint :EllipticSurface 2 "$(Ft0[1]) \n" - append!(basisT , Ft0[3][2:end]) - push!(grams,Ft0[4][2:end,2:end]) - push!(fiber_componentsS, vcat([pt], collect(Ft0))) - end - G = block_diagonal_matrix(grams) - # make way for some more pretty printing - for (pt,root_type,_,comp) in fiber_componentsS - for (i,I) in enumerate(comp) - name = string(root_type[1], root_type[2]) - set_attribute!(components(I)[1], :name, string("Component ", name, "_", i-1," of fiber over ", Tuple(pt))) - set_attribute!(components(I)[1], :_self_intersection, -2) - end - end - return basisT, G, fiber_componentsS - end -end - -@doc raw""" - trivial_lattice(X::EllipticSurface) -> Vector{AbsWeilDivisor}, ZZMatrix - -Return a basis for the trivial lattice as well as its gram matrix. - -The trivial lattice is the lattice spanned by fiber components and -the zero section of $X$. -""" -function trivial_lattice(X::EllipticSurface) - T = _trivial_lattice(X)[1:2] - return T -end - -@doc raw""" - reducible_fibers(S::EllipticSurface) - -Return the reducible fibers of $S$. - -The output format is the following: -A list [F1, ..., Fn] where each entry Fi represents a reducible fiber. - -The list $F$ has the following entries: -- A point $P \in \mathbb{P}^{1}$ such that $F = \pi^{-1}(P)$; -- The ADE-type of the fiber; -- The fiber $F$ as a Weil divisor, including its multiplicities; -- The irreducible components of the fiber. The first component intersects the zero section; -- Their intersection matrix. -""" -function reducible_fibers(S::EllipticSurface) - return _trivial_lattice(S)[3] -end - - -@doc raw""" - standardize_fiber(S::EllipticSurface, f::Vector{<:AbsWeilDivisor}) - -Internal method. Used to prepare for [`reducible_fibers`](@ref). -`f` must be the list of the components of the reducible fiber `F`. -Output a list of tuples with each tuple as follows -- the root type of ``F``, e.g. `(:A, 3)` -- the class of ``F`` as a divisor with the appropriate multiplicities -- the irreducible components `[F0,...Fn]` of `F` sorted such that the first entry `F0` is the one intersecting the zero section. The others are sorted in some standard way -- gram matrix of the intersection of [F0,...,Fn], it is an extended ADE-lattice. -""" -function standardize_fiber(S::EllipticSurface, f::Vector{<:AbsWeilDivisor}) - @hassert :EllipticSurface 2 all(is_prime(i) for i in f) - f = copy(f) - O = components(zero_section(S))[1] - local f0 - for (i,D) in enumerate(f) - if !isone(O+components(D)[1]) - f0 = D - deleteat!(f,i) - break - end - end - r = length(f) - G = -2*identity_matrix(ZZ, r) - @vprintln :EllipticSurface 2 "computing intersections:" - for i in 1:r - @vprint :EllipticSurface 3 "\nrow $(i): \n" - for j in 1:i-1 - @vprint :EllipticSurface 4 "$(j) " - # we know the intersections are 0 or 1, so we can replace the line below by a shortcut. - # G[i, j] = G[j, i] = intersect(f[i], f[j]) - # In the examples treated, this led to roughly a factor 3 in speed and memory consumption. - if isone(components(f[i])[1]+components(f[j])[1]) - G[i,j] = 0 - else - G[i,j] = 1 - end - G[j,i] = G[i,j] - end - end - L = integer_lattice(gram=G) - rt,_ = root_lattice_recognition(L) - @assert length(rt)==1 - rt = rt[1] - R = root_lattice(rt[1], rt[2]) - b, I = _is_equal_up_to_permutation_with_permutation(G, -gram_matrix(R)) - @assert b - gensF = vcat([f0], f[I]) - Gext, v = extended_ade(rt[1],rt[2]) - Fdiv = sum(v[i]*gensF[i] for i in 1:length(gensF)) - return rt, Fdiv, gensF, Gext -end - -@doc raw""" - fiber_cartier(S::EllipticSurface, P::Vector = ZZ.([0,1])) -> EffectiveCartierDivisor - -Return the fiber of $\pi\colon X \to C$ over $P\in C$ as a Cartier divisor. -""" -function fiber_cartier(S::EllipticSurface, P::Vector = ZZ.([0,1])) - S0,_ = weierstrass_model(S) - underlying_scheme(S) # cache stuff - D = IdDict{AbsAffineScheme, RingElem}() - k = base_ring(S0) - P = k.(P) - - if P[1]!=0 && P[2]!=0 - t0 = P[1] * inv(P[2]) - for i in 1:3 - U = S0[1][i] - (_,_,t) = coordinates(U) - D[U] = t-t0 - end - s0 = inv(t0) - for i in 4:6 - U = S0[1][i] - (_,_,s) = coordinates(U) - D[U] = s - s0 - end - elseif P[1] != 0 - # it is the fiber at [1:0] - for i in 4:6 - U = S0[1][i] - (_,_,s) = coordinates(U) - D[U] = s - end - for i in 1:3 - U = S0[1][i] - (_,_,t) = coordinates(U) - D[U] = one(parent(t)) - end - elseif P[2] != 0 - # the fiber at [0:1] - for i in 1:3 - U = S0[1][i] - (_,_,t) = coordinates(U) - D[U] = t - end - for i in 4:6 - U = S0[1][i] - (_,_,s) = coordinates(U) - D[U] = one(parent(s)) - end - else - error("[0,0] is not a point in projective space") - end - F = EffectiveCartierDivisor(S0, D, trivializing_covering=S0[1], check=false) - return pullback(S.blowup)(F) -end - -@doc raw""" - fiber_components(S::EllipticSurface, P) -> Vector{<:AbsWeilDivisor} - -Return the fiber components of the fiber over the point $P \in C$. -""" -function fiber_components(S::EllipticSurface, P; algorithm=:exceptional_divisors) - @vprint :EllipticSurface 2 "computing fiber components over $(P)\n" - P = base_ring(S).(P) - W = codomain(S.inc_Weierstrass) - Fcart = fiber_cartier(S, P) - if isone(P[2]) - U = default_covering(W)[1] - (x,y,t) = coordinates(U) - F = PrimeIdealSheafFromChart(W, U, ideal(t - P[1])) - elseif isone(P[1]) - U = default_covering(W)[4] - (x,y,s) = coordinates(U) - F = PrimeIdealSheafFromChart(W, U, ideal(s - P[2])) - end - FF = ideal_sheaf(Fcart) - EE = exceptional_divisors(S) - EP = filter(E->issubset(FF, E), EE) - for bl in S.ambient_blowups - F = strict_transform(bl, F) - end - F = pullback(S.inc_Y, F) - F = weil_divisor(F, ZZ) - fiber_components = [weil_divisor(E, ZZ; check=false) for E in EP] - push!(fiber_components, F) - return fiber_components -end - -@attr Vector{<:AbsIdealSheaf} function exceptional_divisors(S::EllipticSurface) - PP = AbsIdealSheaf[] - @vprintln :EllipticSurface 2 "computing exceptional divisors" - # If we have resolution_strategy=:simultaneous, then the following is a non-trivial preprocessing step. - ambient_pts = reduce(vcat, maximal_associated_points.(ideal_sheaf.(S.ambient_exceptionals))) - for I in ambient_pts - @vprintln :EllipticSurface 4 "decomposing divisor " - mp = maximal_associated_points(pullback(S.inc_Y, I); use_decomposition_info=true) - append!(PP, mp) - end - @vprintln :EllipticSurface 3 "done" - return PP -end - -function fiber(X::EllipticSurface) - b, pt, F = irreducible_fiber(X) - if b - W = weil_divisor(F) - # we manually set the self-intersection - set_attribute!(components(W)[1], :_self_intersection, 0) - else - # all fibers are reducible pick the one over [0 : 1] - k = base_ring(X) - pt = [k(0),k(1)] - f = fiber_components(X, pt) - fiber_type, W, componentsW, gramW = standardize_fiber(X, f) - end - set_attribute!(W, :name=> "Fiber over ($(pt[1]), $(pt[2]))") - return pt, W -end - - -@doc raw""" - irreducible_fiber(S::EllipticSurface) -> Bool, Point, EffectiveCartierDivisor - -Return an irreducible fiber as a cartier divisor and whether it exists. - -The return value is a triple `(b, pt, F)` where - -- `b` is `true` if an irreducible fiber exists over the base field of `S` -- `pt` the base point of the fiber -- `F` the irreducible fiber which projects to `pt` -""" -function irreducible_fiber(S::EllipticSurface) - W = weierstrass_model(S) - d = numerator(discriminant(generic_fiber(S))) - kt = parent(d) - k = coefficient_ring(kt) - r = [k.(roots(i[1])) for i in factor(d) if i[2]>=2] - sing = reduce(append!,r, init=[]) - pt = k.([0,0]) # initialize - found = false - if is_finite(k) - for i in k - if !(i in sing) # true if the fiber over [i,1] is irreducible - pt = k.([i,1]) - found = true - break - end - end - if !found && (degree(d) >= 12*euler_characteristic(S) - 1) # irreducible at infinity? - pt = k.([1, 0]) - found = true - end - else - i = k(0) - while true - i = i+1 - if !(i in sing) - pt = k.([i,1]) - found = true - break - end - end - end - F = fiber_cartier(S, pt) - return found, pt, F -end - -@doc raw""" - section(X::EllipticSurface, P::EllipticCurvePoint) - -Given a rational point $P\in E(C)$ of the generic fiber $E/C$ of $\pi\colon X \to C$, -return its closure in $X$ as a `AbsWeilDivisor`. -""" -function section(X::EllipticSurface, P::EllipticCurvePoint) - if iszero(P[1])&&iszero(P[3]) - return zero_section(X) - end - return EllipticSurfaceSection(X, P) -end - -@attributes mutable struct EllipticSurfaceSection{ - CoveredSchemeType<:AbsCoveredScheme, - CoefficientRingType<:AbstractAlgebra.Ring, - CoefficientRingElemType<:AbstractAlgebra.RingElem - } <: AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} - D::WeilDivisor{CoveredSchemeType, CoefficientRingType, CoefficientRingElemType} - P::EllipticCurvePoint - - function EllipticSurfaceSection(X::EllipticSurface, P::EllipticCurvePoint; coefficient_ring::Ring=ZZ) - @vprint :EllipticSurface 3 "Computing a section from a point on the generic fiber\n" - weierstrass_contraction(X) # trigger required computations - PX = _section_on_weierstrass_ambient_space(X, P) - for f in X.ambient_blowups - PX = strict_transform(f , PX) - end - PY = pullback(X.inc_Y, PX) - set_attribute!(PY, :name, string("section: (",P[1]," : ",P[2]," : ",P[3],")")) - set_attribute!(PY, :_self_intersection, -euler_characteristic(X)) - W = WeilDivisor(PY, check=false) - set_attribute!(W, :is_prime=>true) - I = first(components(W)) - set_attribute!(I, :is_prime=>true) - return new{typeof(X), typeof(coefficient_ring), elem_type(coefficient_ring)}(W, P) - end -end - -underlying_divisor(D::EllipticSurfaceSection) = D.D -rational_point(D::EllipticSurfaceSection) = D.P - - -function _section_on_weierstrass_ambient_space(X::EllipticSurface, P::EllipticCurvePoint) - S0,incS0 = weierstrass_model(X) - X0 = codomain(incS0) - if P[3] == 0 - # zero section - V = X0[1][3] - (z,x,t) = coordinates(V) - return IdealSheaf(X0, V, [x,z]) - end - U = X0[1][1] - (x,y,t) = coordinates(U) - b = P - return ideal_sheaf(X0,U,[OO(U)(i) for i in [x*denominator(b[1])(t)-numerator(b[1])(t),y*denominator(b[2])(t)-numerator(b[2])(t)]]; check=false) -end - - -@doc raw""" - zero_section(S::EllipticSurface) -> AbsWeilDivisor - -Return the zero section of the relatively minimal elliptic -fibration \pi\colon X \to C$. -""" -@attr Any zero_section(S::EllipticSurface) = EllipticSurfaceSection(S, generic_fiber(S)([0,1,0])) - -################################################################################ -# -# Some linear systems on elliptic surfaces -# -################################################################################ - -@doc raw""" - _prop217(E::EllipticCurve, P::EllipticCurvePoint, k) - -Compute a basis for the linear system -``|O + P + kF|`` -on the minimal elliptic (K3) surface defined by E. -Here F is the class of a fiber O the zero section -and P any non-torsion section. - -The return value is a list of pairs ``(a(t),b(t))`` - -```jldoctest -julia> kt,t = polynomial_ring(GF(29),:t); - -julia> ktfield = fraction_field(kt); - -julia> bk = [((17*t^4 + 23*t^3 + 18*t^2 + 2*t + 6, 8*t^5 + 2*t^4 + 6*t^3 + 25*t^2 + 24*t + 5 )), - ((17*t^6 + 3*t^5 + 16*t^4 + 4*t^3 + 13*t^2 + 6*t + 5)//(t^2 + 12*t + 7), (4*t^8 + 19*t^7 + 14*t^6 + 18*t^5 + 27*t^4 + 13*t^3 + 9*t^2 + 14*t + 12)//(t^3 + 18*t^2 + 21*t + 13) ), - ((17*t^6 + 10*t^5 + 24*t^4 + 15*t^3 + 22*t^2 + 27*t + 5)//(t^2 + 16*t + 6), (20*t^8 + 24*t^7 + 22*t^6 + 12*t^5 + 21*t^4 + 21*t^3 + 9*t^2 + 21*t + 12)//(t^3 + 24*t^2 + 18*t + 19) ), - ((17*t^8 + 21*t^7 + 20*t^5 + 24*t^4 + 21*t^3 + 4*t^2 + 9*t + 13)//(t^4 + 17*t^3 + 12*t^2 + 28*t + 28), (23*t^11 + 25*t^10 + 8*t^9 + 7*t^8 + 28*t^7 + 16*t^6 + 7*t^5 + 23*t^4 + 9*t^3 + 27*t^2 + 13*t + 13)//(t^6 + 11*t^5 + 14*t^4 + 13*t^3 + 6*t^2 + 18*t + 12) )]; - -julia> E = elliptic_curve(ktfield,[3*t^8+24*t^7+22*t^6+15*t^5+28*t^4+20*t^3+16*t^2+26*t+16, 24*t^12+27*t^11+28*t^10+8*t^9+6*t^8+16*t^7+2*t^6+10*t^5+3*t^4+22*t^3+27*t^2+10*t+3]); - -julia> bk = [E(collect(i)) for i in bk]; - -julia> Oscar._prop217(E,bk[2],2) -5-element Vector{Tuple{FqPolyRingElem, FqPolyRingElem}}: - (t^2 + 12*t + 7, 0) - (t^3 + 8*t + 3, 0) - (t^4 + 23*t + 2, 0) - (25*t + 22, 1) - (12*t + 28, t) - -julia> Oscar._prop217(E,bk[1],1) -2-element Vector{Tuple{FqPolyRingElem, FqPolyRingElem}}: - (1, 0) - (t, 0) -``` -""" -function _prop217(E::EllipticCurve, P::EllipticCurvePoint, k) - @req !iszero(P[3]) "P must not be torsion" # seems like we cannot check this - xn = numerator(P[1]) - xd = denominator(P[1]) - yn = numerator(P[2]) - yd = denominator(P[2]) - OP = divexact(max(degree(xd), degree(xn) - 4), 2) - dega = k + 2*OP - degb = k + 2*OP - 2 - divexact(degree(xd), 2) #? - base = base_field(E) - Bt = base_ring(base) - B = coefficient_ring(Bt) - - R,ab = polynomial_ring(base,vcat([Symbol(:a,i) for i in 0:dega],[Symbol(:b,i) for i in 0:degb]),cached=false) - Rt, t1 = polynomial_ring(R,:t) - a = reduce(+,(ab[i+1]*t1^i for i in 0:dega), init=zero(Rt)) - b = reduce(+,(ab[2+dega+j]*t1^j for j in 0:degb), init=zero(Rt)) - c = a*xn(t1) - b*yn(t1) - r = mod(c, xd(t1)) - # setup the linear equations for coefficients of r to vanish - # and for the degree of c to be bounded above by - # k + 2*OP + 4 + degree(xd) - eq1 = collect(coefficients(r)) - eq2 = [coeff(c,i) for i in (k + 2*OP + 4 + degree(xd) + 1):degree(c)] - eqns = vcat(eq1, eq2) - - # collect the equations as a matrix - cc = [[coeff(j, abi) for abi in ab] for j in eqns] - mat = reduce(vcat,cc, init=elem_type(base)[]) - @assert all(is_one(denominator(x)) for x in mat) - @assert all(is_constant(numerator(x)) for x in mat) - mat2 = [constant_coefficient(numerator(x)) for x in mat] - M = matrix(B, length(eqns), length(ab), mat2) - # @assert M == matrix(base, cc) # does not work if length(eqns)==0 - K = kernel(M; side = :right) - kerdim = ncols(K) - result = Tuple{elem_type(Bt),elem_type(Bt)}[] - t = gen(Bt) - for j in 1:kerdim - aa = reduce(+, (K[i+1,j]*t^i for i in 0:dega), init=zero(Bt)) - bb = reduce(+, (K[dega+i+2,j]*t^i for i in 0:degb), init=zero(Bt)) - push!(result, (aa, bb)) - end - # confirm the computation - @assert kerdim == 2*k + OP # prediced by Riemann-Roch - for (a,b) in result - @assert mod(a*xn - b*yn, xd) == 0 - @assert degree(a) <= k + 2*OP - @assert degree(b) <= k + 2*OP - 2 - 1//2*degree(xd) - @assert degree(a*xn - b*yn) <= k + 2*OP + 4 + degree(xd) - end - return result -end - -function iszero(P::EllipticCurvePoint) - return iszero(P[1]) && isone(P[2]) && iszero(P[3]) -end - -@doc raw""" - linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int64) -> LinearSystem - -Compute the linear system ``|O + P + k F|`` on the elliptic surface ``X``. -Here ``F`` is the class of the fiber over ``[0:1]``, ``O`` the zero section -and ``P`` any section given as a point on the generic fiber. - -The linear system is represented in terms of the Weierstrass coordinates. -""" -function linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int64) - euler_characteristic(X) == 2 || error("linear system implemented only for elliptic K3s") - #FS = function_field(weierstrass_model(X)[1]) - FS = function_field(X) - U = weierstrass_chart_on_minimal_model(X) - (x,y,t) = ambient_coordinates(U) - - sections = elem_type(FS)[] - if iszero(P[3]) - append!(sections, [FS(t)^(i-k) for i in 0:k]) - append!(sections, [FS(t)^(i-k)*FS(x) for i in 0:k-4]) - else - xn = numerator(P[1]) - xd = denominator(P[1]) - yn = numerator(P[2]) - yd = denominator(P[2]) - - I = saturated_ideal(defining_ideal(U)) - IP = ideal([x*xd(t)-xn(t),y*yd(t)-yn(t)]) - @hassert :EllipticSurface 2 issubset(I, IP) || error("P does not define a point on the Weierstrasschart") - - @assert gcd(xn, xd)==1 - @assert gcd(yn, yd)==1 - ab = _prop217(generic_fiber(X), P, k) - d = divexact(yd, xd)(t) - den = t^k*(x*xd(t) - xn(t)) - for (a,b) in ab - c = divexact(b*yn - a*xn, xd) - num = a(t)*x+b(t)*d*y + c(t) - push!(sections, FS(num//den)) - end - end - return sections -end - -@doc raw""" - two_neighbor_step(X::EllipticSurface, F1::Vector{QQFieldElem}) - -Given an isotropic nef divisor ``F1`` with ``F1.F = 2``, -compute the linear system ``|F1|`` and return the corresponding generic fiber -as a double cover `C` of the projective line branched over four points. - -Input: -``F1`` is represented as a vector in the `algebraic_lattice(X)` - -Output: -A tuple `(C, (x1, y1, t1))` defined as follows. -- `C` is given by a polynomial `y1^2 - q(x1)` in `k(t)[x1,y1]` with `q` of degree 3 or 4. -- (x1,y1,t1) are expressed as rational functions in terms of the weierstrass coordinates `(x,y,t)`. -""" -function two_neighbor_step(X::EllipticSurface, F::Vector{QQFieldElem}) - E = generic_fiber(X) - basisNS, tors, NS = algebraic_lattice(X) - V = ambient_space(NS) - @req inner_product(V, F, F)==0 "not an isotropic divisor" - @req euler_characteristic(X) == 2 "not a K3 surface" - F0 = zeros(QQ,degree(NS)); F0[1]=1 - - @req inner_product(V, F, F0) == 2 "not a 2-neighbor" - - D1, D, P, l, c = horizontal_decomposition(X, F) - u = _elliptic_parameter(X, D1, D, P, l, c) - @assert scheme(parent(u)) === X - pr = weierstrass_contraction(X) - WX, _ = weierstrass_model(X) - # The following is a cheating version of the command u = pushforward(pr)(u) (the latter has now been deprecated!) - u = function_field(WX)(u[weierstrass_chart_on_minimal_model(X)]) - @assert scheme(parent(u)) === weierstrass_model(X)[1] - - # Helper function - my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) - - # transform to a quartic y'^2 = q(x) - if iszero(P[3]) # P = O - eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case1) - eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) -# function phi_func(x) -# y = phi1(x) -# n = numerator(y) -# d = denominator(y) -# return phi2(n)//phi2(d) -# end -# phi = MapFromFunc(domain(phi1), codomain(phi2), phi_func) -# # TODO: Verify that the construction below also works and replace by that, eventually. -# phi_alt = compose(phi1, extend_domain_to_fraction_field(phi2)) -# @assert phi.(gens(domain(phi))) == phi_alt.(gens(domain(phi))) - phi = compose(phi1, extend_domain_to_fraction_field(phi2)) - elseif iszero(2*P) # P is a 2-torsion section - eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case3) - #eqn1, phi1 = _conversion_case_3(X, u) - (x2, y2) = gens(parent(eqn1)) - - # Make sure the coefficient of y² is one (or a square) so that - # completing the square works. - c = my_const(coeff(eqn1, [x2, y2], [0, 2]))::AbstractAlgebra.Generic.FracFieldElem - eqn1 = inv(unit(factor(c)))*eqn1 - - eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) - phi = compose(phi1, extend_domain_to_fraction_field(phi2)) - else # P has infinite order - eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case2) - #eqn1, phi1 = _conversion_case_2(X, u) - (x2, y2) = gens(parent(eqn1)) - - # Make sure the coefficient of y² is one (or a square) so that - # completing the square works. - c = my_const(coeff(eqn1, [x2, y2], [0, 2]))::AbstractAlgebra.Generic.FracFieldElem - eqn1 = inv(unit(factor(c)))*eqn1 - - eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) - phi = compose(phi1, extend_domain_to_fraction_field(phi2)) - end - - return eqn2, phi -end - -@doc raw""" - horizontal_decomposition(X::EllipticSurface, L::Vector{QQFieldElem}) -> AbsWeilDivisor, EllipticCurvePoint - -Given a divisor ``L`` as a vector in the `algebraic_lattice(X)` -find a linearly equivalent divisor ``(n-1) O + P + V = D ~ L`` where -``O`` is the zero section, ``P`` is any section and ``V`` is vertical. - -Returns a tuple `(D1, D, P, l, c)` where `D` and `P` are as above and -``D <= D1 = (n-1)O + P + n_1F_1 + ... n_k F_k`` with ``l = n_1 + ... n_k`` minimal -and the `F_i` are some other fibers. -The rational function `c=c(t)` has divisor of zeros and poles`` -(c) = lF - n_0F_1 + ... n_k F_k`` -""" -function horizontal_decomposition(X::EllipticSurface, F::Vector{QQFieldElem}) - E = generic_fiber(X) - basisNS, tors, NS = algebraic_lattice(X) - V = ambient_space(NS) - @req F in algebraic_lattice(X)[3] "not in the algebraic lattice" - @req inner_product(V, F, F)==0 "not an isotropic divisor" - @req euler_characteristic(X) == 2 "not a K3 surface" - # how to give an ample divisor automagically in general? - # @req is_nef(X, F) "F is not nef" - l = F[1] - rk_triv = nrows(trivial_lattice(X)[2]) - n = rank(NS) - @assert degree(NS) == rank(NS) - p, P = _vertical_part(X,F) - D = section(X, P) - F2 = F - p - @vprint :EllipticSurface 4 "F2 = $(F2)\n" - D = D + ZZ(F2[2])*zero_section(X) - D1 = D - F2 = ZZ.(F2); F2[2] = 0 - l = F2[1] # number of fibers that we need - # find the fiber components meeting O necessary - F3 = F2 - (_,_,t) = ambient_coordinates(weierstrass_chart_on_minimal_model(X)) - c = t^0 - for (pt, rt, fiber, comp, gram) in reducible_fibers(X) - Fib0 = comp[1] - f0 = zeros(QQFieldElem, length(basisNS)) - for i in 1:length(basisNS) - if !isone(components(Fib0)[1]+components(basisNS[i])[1]) - if length(comp)==2 && 2=0 for i in 1:length(basisNS)) - D = D + sum(ZZ(F4[i])*basisNS[i] for i in 1:length(basisNS)) - @assert D<=D1 - return D1, D, P, Int(l), c -end - -@doc raw""" - elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) -> LinearSystem - -Return the elliptic parameter ``u`` of the divisor class `F`. - -The input `F` must be given with respect to the basis of -`algebraic_lattice(X)` and be an isotropic nef divisor. -This method assumes that $X$ is a K3 surface. -""" -function elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) - D1, D, P, l, c = horizontal_decomposition(X, F) - return _elliptic_parameter(X, D1, D, P, l, c) -end - -@doc raw""" - _elliptic_parameter(X::EllipticSurface, D::AbsWeilDivisor, l, c) - -Compute the linear system of ``D = (n-1) O + P + V``. -where V is vertical and `l` is the coefficient of the fiber class. -Assumes `D` nef and `D^2=0`. -Typically ``D`` is the output of `horizontal_decomposition`. -""" -function _elliptic_parameter(X::EllipticSurface, D1::AbsWeilDivisor, D::AbsWeilDivisor, P::EllipticCurvePoint, l::Int, c) - S, piS = weierstrass_model(X); - piX = weierstrass_contraction(X) - c = function_field(X)(c) - L = [i*c for i in linear_system(X, P, l)]; - LonX = linear_system(L, D1, check=false); - - LsubF, Tmat = subsystem(LonX, D); - LsubFonS = [sum(Tmat[i,j]*L[j] for j in 1:ncols(Tmat)) for i in 1:nrows(Tmat)] - - @assert length(LsubFonS)==2 - u2 = LsubFonS[2]//LsubFonS[1] - return u2 -end - - -@doc raw""" - extended_ade(ADE::Symbol, n::Int) - -Return the dual intersection matrix of an extended ade Dynkin diagram -as well as the isotropic vector (with positive coefficients in the roots). -""" -function extended_ade(ADE::Symbol, n::Int) - R = change_base_ring(ZZ,gram_matrix(root_lattice(ADE,n))) - G = block_diagonal_matrix([ZZ[2;],R]) - if ADE == :E && n == 8 - G[1,n] = -1 - G[n,1] = -1 - end - if ADE == :E && n == 7 - G[1,2] = -1 - G[2,1] = -1 - end - if ADE == :E && n == 6 - G[1,n+1] = -1 - G[n+1,1] = -1 - end - if ADE == :A && n > 0 - G[1,2] = -1 - G[2,1] = -1 - G[1,n+1] = -1 - G[n+1,1] = -1 - end - if ADE == :A && n ==1 0 - G[1,2]= -2 - G[2,1] = -2 - end - if ADE == :D - G[1,n] = -1 - G[n,1] = -1 - end - @assert rank(G) == n - return -G, kernel(G; side = :left) -end - -######################################################################## -# Reduction to positive characteristic -# -# We allow to store a reduction of an elliptic surface `X` to positive -# characteristic. The user needs to know what they're doing here! -# -# The functionality can be made available by specifying a reduction -# map for the `base_ring` (actually a field) of `X` to a field of -# positive characteristic. This can then be stored in `X` via -# `set_good_reduction_map!`. The latter unlocks certain features such -# as computation of intersection numbers in positive characteristic. -######################################################################## -function set_good_reduction_map!(X::EllipticSurface, red_map::Map) - has_attribute(X, :good_reduction_map) && error("reduction map has already been set") - kk0 = base_ring(X) - @assert domain(red_map) === kk0 - kkp = codomain(red_map) - @assert characteristic(kkp) > 0 - set_attribute!(X, :good_reduction_map=>red_map) -end - -function get_good_reduction_map(X::EllipticSurface) - is_zero(characteristic(base_ring(X))) || error("reduction to positive characteristic is only possible from characteristic zero") - has_attribute(X, :good_reduction_map) || error("no reduction map is available; please set it manually via `set_good_reduction_map!`") - return get_attribute(X, :good_reduction_map)::Map -end - -@attr Tuple{<:AbsCoveredScheme, <:AbsCoveredSchemeMorphism} function raw_good_reduction(X::EllipticSurface) - red_map = get_good_reduction_map(X) - X_red, bc_map = base_change(red_map, X) - set_attribute!(X_red, :is_irreducible=>true) - set_attribute!(X_red, :is_reduced=>true) - set_attribute!(X_red, :is_integral=>true) - set_attribute!(X_red, :is_equidimensional=>true) - return X_red, bc_map -end - -@attr Map function good_reduction_function_fields(X::EllipticSurface) - red_map = get_good_reduction_map(X) - E = generic_fiber(X) - Ft = base_field(E) - Pt = base_ring(Ft) - kk = coefficient_ring(Pt) - kk_red = codomain(red_map) - Pt_red, _ = polynomial_ring(kk_red, first(symbols(Pt)); cached=false) - Ft_red = fraction_field(Pt_red) - Ft_to_Ft_red = map_from_func(fr->Ft_red(map_coefficients(red_map, numerator(fr); parent=Pt_red), map_coefficients(red_map, denominator(fr); parent=Pt_red)), Ft, Ft_red) - return Ft_to_Ft_red -end - -@attr EllipticCurve function good_reduction_generic_fiber(X::EllipticSurface) - red_map = get_good_reduction_map(X) - E = generic_fiber(X) - Ft_to_Ft_red = good_reduction_function_fields(X) - E_red = base_change(Ft_to_Ft_red, E) - return E_red -end - -@attr Vector{<:EllipticCurvePoint} function good_reduction_rational_points(X::EllipticSurface) - red_map = good_reduction_function_fields(X) - result = Vector{EllipticCurvePoint}() - E_red = good_reduction_generic_fiber(X) - for P in X.MWL # TODO: Do we have a getter for this? - if is_infinite(P) - push!(result, infinity(E_red)) - continue - end - push!(result, E_red([red_map(P[1]), red_map(P[2])])) - end - return result -end - -@attr EllipticSurface function good_reduction(X::EllipticSurface) - red_map = get_good_reduction_map(X) - E_red = good_reduction_generic_fiber(X) - mwl_red = good_reduction_rational_points(X) - X_red = EllipticSurface(E_red, 2, mwl_red; resolution_strategy=X.resolution_strategy) -end - -@attr Tuple{<:MorphismFromRationalFunctions, <:MorphismFromRationalFunctions} function identifications_with_raw_good_reduction(X::EllipticSurface) - X_red = good_reduction(X) - W_red = weierstrass_chart_on_minimal_model(X_red) - X_red_raw, red_raw = raw_good_reduction(X) - red_raw_cov = covering_morphism(red_raw) - W_red_raw = domain(first(maps_with_given_codomain(red_raw_cov, weierstrass_chart_on_minimal_model(X)))) - - R_red = ambient_coordinate_ring(W_red) - R_red_raw = ambient_coordinate_ring(W_red_raw) - raw_to_red = morphism_from_rational_functions(X_red_raw, X_red, W_red_raw, W_red, fraction_field(R_red_raw).(gens(R_red_raw)); check=false) - set_attribute!(raw_to_red, :is_isomorphism=>true) - red_to_raw = morphism_from_rational_functions(X_red, X_red_raw, W_red, W_red_raw, fraction_field(R_red).(gens(R_red)); check=false) - set_attribute!(red_to_raw, :is_isomorphism=>true) - return raw_to_red, red_to_raw -end - - -function raw_reduction_of_algebraic_lattice(X::EllipticSurface) - return get_attribute!(X, :raw_reduction_of_algebraic_lattice) do - X_red_raw, bc = raw_good_reduction(X) - basis_ambient, _, _= algebraic_lattice(X) - return red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) - end::IdDict -end - -@attr ZZMatrix function good_reduction_algebraic_lattice(X::EllipticSurface) - div_red = raw_reduction_of_algebraic_lattice(X) - from, to = identifications_with_raw_good_reduction(X) - div_red_pf = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>pushforward(from, E) for (D, E) in div_red) - basis, _, _= algebraic_lattice(X) - X_red = good_reduction(X) - red_basis, _, _= algebraic_lattice(X_red) - result = matrix(ZZ, [div_red_pf[D] == E ? one(ZZ) : zero(ZZ) for D in basis, E in red_basis]) - result[1, 1] = one(ZZ) # identify the generic fibers - return result -end - -@attr MorphismFromRationalFunctions function good_reduction( - f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface} - ) - X = domain(f) - @assert X === codomain(f) "reduction to positive characteristic is only implemented for automorphisms" - W = weierstrass_chart_on_minimal_model(X) - @assert W === domain_chart(f) === codomain_chart(f) "morphism must be defined on the Weierstrass charts" - img_gens = coordinate_images(f) - red_map = get_good_reduction_map(X) - - X_red = good_reduction(X) - W_red = weierstrass_chart_on_minimal_model(X_red) - R = ambient_coordinate_ring(W_red) - FR = fraction_field(R) - - psi = fr -> FR(map_coefficients(red_map, numerator(fr); parent=R), map_coefficients(red_map, denominator(fr); parent=R)) - - img_gens_red = psi.(img_gens) - result = morphism_from_rational_functions(X_red, X_red, W_red, W_red, img_gens_red; check=false) - has_attribute(f, :is_isomorphism) && get_attribute(f, :is_isomorphism)===true && set_attribute!(result, :is_isomorphism=>true) - return result -end - - -@doc raw""" - basis_representation(X::EllipticSurface, D::AbsWeilDivisor) - -Return the vector representing the numerical class of `D` -with respect to the basis of the ambient space of `algebraic_lattice(X)`. -""" -function basis_representation(X::EllipticSurface, D::AbsWeilDivisor) - basis_ambient,_, NS = algebraic_lattice(X) - G = gram_matrix(ambient_space(NS)) - n = length(basis_ambient) - v = zeros(ZZRingElem, n) - @vprint :EllipticSurface 3 "computing basis representation of $D\n" - kk = base_ring(X) - if iszero(characteristic(kk)) && has_attribute(X, :good_reduction_map) - X_red_raw, bc = raw_good_reduction(X) - red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) - D_red = _reduce_as_prime_divisor(bc, D) - for (i, E) in enumerate(basis_ambient) - @vprintln :EllipticSurface 4 "intersecting in positive characteristic with $(i): $(basis_ambient[i])" - v[i] = intersect(red_dict[E], D_red) - end - else - for i in 1:n - @vprintln :EllipticSurface 4 "intersecting with $(i): $(basis_ambient[i])" - - v[i] = intersect(basis_ambient[i], D) - end - end - @vprint :EllipticSurface 3 "done computing basis representation\n" - return v*inv(G) -end - -### Some functions to do custom pullback of divisors along reduction maps. -# We assume that primeness is preserved along the reduction. In particular, the -# user is responsible for this to hold for all cases used! -# They specify the "good reduction" in the end. -function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::AbsWeilDivisor) - return WeilDivisor(domain(bc), coefficient_ring(D), - IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}( - _reduce_as_prime_divisor(bc, I) => c for (I, c) in coefficient_dict(D) - ) - ) -end - -function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::EllipticSurfaceSection) - P = rational_point(D) - is_infinite(P) && return _reduce_as_prime_divisor(bc, underlying_divisor(D)) - X = codomain(bc) - @assert parent(P) === generic_fiber(X) - W = weierstrass_chart_on_minimal_model(X) - R = ambient_coordinate_ring(W) - (x, y, t) = gens(R) - I = ideal(R, R.([evaluate(denominator(P[1]), t)*x-evaluate(numerator(P[1]), t), - evaluate(denominator(P[2]), t)*y-evaluate(numerator(P[2]), t)]) - ) - bc_loc = first(maps_with_given_codomain(covering_morphism(bc), W)) - bc_I = pullback(bc_loc)(I) - @assert is_one(dim(bc_I)) - set_attribute!(bc_I, :is_prime=>true) - J = PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), bc_I) - return WeilDivisor(domain(bc), coefficient_ring(D), - IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}(J => one(coefficient_ring(D))) - ) -end - -function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::AbsIdealSheaf) - result = pullback(bc, I) - has_attribute(I, :_self_intersection) && set_attribute!(result, :_self_intersection=> - (get_attribute(I, :_self_intersection)::Int)) - return result -end - -function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::PrimeIdealSheafFromChart) - U = original_chart(I) - bc_cov = covering_morphism(bc) - V = __find_chart(U, codomain(bc_cov)) - IV = I(V) - bc_loc = first(maps_with_given_codomain(bc_cov, V)) - J = pullback(bc_loc)(IV) - set_attribute!(J, :is_prime=>true) - return PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), J) -end - - -######################################################################## -# Internal functionality for Weierstrass transformation -######################################################################## - - -@doc raw""" - _normalize_hyperelliptic_curve(g::MPolyRingElem, parent=nothing) - -Transform ``a(x)y^2 + b(x)y - h(x)`` in ``K(t)[x,y]`` to ``y'^2 - h(x')`` -""" -function _normalize_hyperelliptic_curve(g::MPolyRingElem; parent::Union{MPolyRing, Nothing}=parent(g)) - R = Oscar.parent(g) - @assert ngens(R) == 2 "polynomial must be bivariate" - F = fraction_field(R) - kt = coefficient_ring(R) - (x, y) = gens(R) - - # Prepare the output ring - if parent===nothing - R1, (x1, y1) = R, gens(R) - else - R1 = parent - @assert coefficient_ring(R1) == coefficient_ring(R) "coefficient ring of output is incompatible with input" - (x1, y1) = gens(R1) - end - - # Get the coefficients of g as a univariate polynomial in y - ktx, X = polynomial_ring(kt, :X, cached=false) - ktxy, Y = polynomial_ring(ktx, :y, cached=false) - - # Maps to transform to univariate polynomials in y - split_map_R = hom(R, ktxy, [ktxy(X), Y]) - split_map_R1 = hom(R1, ktxy, [ktxy(X), Y]) - G = split_map_R(g) - @assert degree(G) == 2 "polynomial must be of degree 2 in its second variable" - - #complete the square - h, b, a = collect(coefficients(G)) - h = -h - u = unit(factor(a)) - a = inv(u)*a - b = inv(u)*b - success, sqa = is_square_with_sqrt(a) - @assert success "leading coefficient as univariate polynomial in the second variable must be a square" - - F1 = fraction_field(R1) - psi = hom(R1, F, F.([x, (2*evaluate(a, x)*y + evaluate(b, x))//(2*evaluate(sqa, x))])) - conv = MapFromFunc(ktx, R1, f->evaluate(f, x1)) - (a1, b1, sqa1) = conv.([a, b, sqa]) - phi = hom(R, F1, F1.([x1, (2*sqa1*y1-b1)//(2*a1)])) - phiF = MapFromFunc(F, F1, x-> phi(numerator(x))//phi(denominator(x))) - # the inverse map if wanted - # psiF = MapFromFunc(F1, F, x-> psi(numerator(x))//psi(denominator(x))) - # @assert all(phiF(psiF(F1(i)))==i for i in gens(R1)) - - # absorb squares into y1 - g1 = numerator(phi(g)) - G1 = split_map_R1(g1) - ff = factor(first(coefficients(G1))) - c = prod([p^div(i, 2) for (p, i) in ff], init=one(ktx)) - #d = sqrt(my_coeff(g1, y1, 2)) - d = last(coefficients(split_map_R1(g1))) - success, d = is_square_with_sqrt(d) - @assert success "leading coefficient must be a square" - - phi1 = hom(R1, F1, [F1(x1), F1(evaluate(c, x1), evaluate(d, x1))*y1]) - phiF1 = MapFromFunc(F1, F1, x-> phi1(numerator(x))//phi1(denominator(x))) - phi2 = compose(phi, phiF1) - g2 = numerator(phi1(g1)) - #c = my_coeff(g2, y1, 2) - c = last(coefficients(split_map_R1(g2))) - g2 = divexact(g2, evaluate(c, x1)) - @assert degree(g2, gen(parent, 1)) <= 4 "degree in the first variable is too high" - @assert degree(g2, gen(parent, 1)) >= 3 "degree in the first variable is too low" - return g2, phi2 -end - - -@doc raw""" - elliptic_surface(g::MPolyRingElem, P::Vector{<:RingElem}) - -Transform a bivariate polynomial `g` of the form `y^2 - Q(x)` with `Q(x)` of -degree at most ``4`` to Weierstrass form, apply Tate's algorithm and -return the corresponding relatively minimal elliptic surface -as well as the coordinate transformation. -""" -function elliptic_surface( - g::MPolyRingElem, P::Vector{<:RingElem}; - minimize::Bool=true, resolution_strategy::Symbol=:iterative - ) - R = parent(g) - (x, y) = gens(R) - P = base_ring(R).(P) - g2, phi2 = transform_to_weierstrass(g, x, y, P); - Y2, phi1 = _elliptic_surface_with_trafo(g2; minimize) - return Y2, phi2 * phi1 -end - -@doc raw""" - transform_to_weierstrass(g::MPolyRingElem, x::MPolyRingElem, y::MPolyRingElem, P::Vector{<:RingElem}) - -Transform a bivariate polynomial `g` of the form `y^2 - Q(x)` with `Q(x)` of degree ``≤ 4`` -to Weierstrass form. This returns a pair `(f, trans)` where `trans` is an endomorphism of the -`fraction_field` of `parent(g)` and `f` is the transform. The input `P` must be a rational point -on the curve defined by `g`, i.e. `g(P) == 0`. -""" -function transform_to_weierstrass(g::MPolyRingElem, x::MPolyRingElem, y::MPolyRingElem, P::Vector{<:RingElem}) - R = parent(g) - F = fraction_field(R) - @assert ngens(R) == 2 "input polynomial must be bivariate" - @assert x in gens(R) "second argument must be a variable of the parent of the first" - @assert y in gens(R) "third argument must be a variable of the parent of the first" - # In case of variables in the wrong order, switch and transform the result. - if x == R[2] && y == R[1] - switch = hom(R, R, reverse(gens(R))) - g_trans, trans = transform_to_weierstrass(switch(g), y, x, reverse(P)) - new_trans = MapFromFunc(F, F, f->begin - switch_num = switch(numerator(f)) - switch_den = switch(denominator(f)) - interm_res = trans(F(switch_num))//trans(F(switch_den)) - num = numerator(interm_res) - den = denominator(interm_res) - switch(num)//switch(den) - end - ) - return switch(g_trans), new_trans - end - - g = inv(coeff(g,[0,2]))*g # normalise g - kk = coefficient_ring(R) - kkx, X = polynomial_ring(kk, :x, cached=false) - kkxy, Y = polynomial_ring(kkx, :y, cached=false) - - imgs = [kkxy(X), Y] - split_map = hom(R, kkxy, imgs) - - G = split_map(g) - @assert degree(G) == 2 "input polynomial must be of degree 2 in y" - @assert all(h->degree(h)<=4, coefficients(G)) "input polynomial must be of degree <= 4 in x" - @assert iszero(coefficients(G)[1]) "coefficient of linear term in y must be zero" - @assert isone(coefficients(G)[2]) "leading coefficient in y must be one" - - if length(P) == 3 && isone(P[3]) - P = P[1:2] - end - - - if length(P) == 2 - @assert iszero(evaluate(g, P)) "point does not lie on the hypersurface" - (px, py) = P - else - px = P[1] - end - # assert g.subs({x:px,y:py})==0 - gx = -evaluate(g, [X + px, zero(X)]) - coeff_gx = collect(coefficients(gx)) - A = coeff(gx, 4) - B = coeff(gx, 3) - C = coeff(gx, 2) - D = coeff(gx, 1) - E = coeff(gx, 0) - #E, D, C, B, A = coeff_gx - if length(P)==3 - @req all(h->degree(h)<=3, coefficients(G)) "infinity (0:1:0) is not a point of this hypersurface" - # y^2 = B*x^3+C*x^2+C*x+D - x1 = F(inv(B)*x) - y1 = F(inv(B)*y) - trans = MapFromFunc(F, F, f->evaluate(numerator(f), [x1, y1])//evaluate(denominator(f), [x1, y1])) - f_trans = B^2*trans(F(g)) - result = numerator(B^2*f_trans) - return result, trans - elseif !iszero(E) - b = py - a4, a3, a2, a1, a0 = A,B,C,D,E - A = b - B = a1//(2*b) - C = (4*a2*b^2-a1^2)//(8*b^3) - D = -2*b - - x1 = x//y - y1 = (A*y^2+B*x*y+C*x^2+D*x^3)//y^2 - x1 = x1+px - - # TODO: The following are needed for the inverse. To be added eventually. - # x2 = (y-(A+B*x+C*x^2))//(D*x^2) - # y2 = x2//x - # x2 = evaluate(x2, [x-px, y]) - # y2 = evaluate(y2, [x-px, y]) - - # @assert x == evaluate(x1, [x2, y2]) - # @assert y == evaluate(y1, [x2, y2]) - else - # TODO compute the inverse transformation (x2,y2) - x1 = 1//x - y1 = y//x^2 - g1 = numerator(evaluate(g, [x1, y1])) - c = coeff(g1, [x], [3]) - x1 = evaluate(x1, [-x//c, y//c]) - y1 = evaluate(y1, [-x//c, y//c]) - x1 = x1+px - #@assert x == evaluate(x1, [x2, y2]) - #@assert y == evaluate(y1, [x2, y2]) - end - @assert F === parent(x1) "something is wrong with caching of fraction fields" - # TODO: eventually add the inverse. - trans = MapFromFunc(F, F, f->evaluate(numerator(f), [x1, y1])//evaluate(denominator(f), [x1, y1])) - f_trans = trans(F(g)) - fac = [a[1] for a in factor(numerator(f_trans)) if isone(a[2]) && _is_in_weierstrass_form(a[1])] - isone(length(fac)) || error("transform to weierstrass form did not succeed") - - # normalize the output - result = first(fac) - result = inv(first(coefficients(coeff(result, gens(parent(result)), [3, 0]))))*result - - return result, trans -end - -function _is_in_weierstrass_form(f::MPolyRingElem) - R = parent(f) - @req ngens(R) == 2 "polynomial must be bivariate" - # Helper function - my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) - - (x, y) = gens(R) - f = -inv(my_const(coeff(f, [x, y], [0, 2]))) * f - isone(-coeff(f, [x, y], [0, 2])) || return false - isone(coeff(f, [x, y], [3, 0])) || return false - - a6 = coeff(f, [x,y], [0,0]) - a4 = coeff(f, [x,y], [1,0]) - a2 = coeff(f, [x,y], [2,0]) - a3 = -coeff(f, [x,y], [0,1]) - a1 = -coeff(f, [x,y], [1,1]) - a_invars = [my_const(i) for i in [a1,a2,a3,a4,a6]] - (a1,a2,a3,a4,a6) = a_invars - return f == (-(y^2 + a1*x*y + a3*y) + (x^3 + a2*x^2 + a4*x + a6)) -end - -function evaluate(f::AbstractAlgebra.Generic.FracFieldElem{<:MPolyRingElem}, a::Vector{T}) where {T<:RingElem} - return evaluate(numerator(f), a)//evaluate(denominator(f), a) -end - -function evaluate(f::AbstractAlgebra.Generic.FracFieldElem{<:PolyRingElem}, a::RingElem) - return evaluate(numerator(f), a)//evaluate(denominator(f), a) -end - -function extend_domain_to_fraction_field(phi::Map{<:MPolyRing, <:Ring}) - ext_dom = fraction_field(domain(phi)) - return MapFromFunc(ext_dom, codomain(phi), x->phi(numerator(x))*inv(phi(denominator(x)))) -end - -######################################################################## -# The three conversions from Section 39.1 in -# A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" -# pp. 44--45. -######################################################################## - -function _elliptic_parameter_conversion(X::EllipticSurface, u::VarietyFunctionFieldElem; - case::Symbol=:case1, names=[:x, :y, :t] - ) - @req variety(parent(u)) === weierstrass_model(X)[1] "function field element must live on the weierstrass model of the first argument" - @req length(names) == 3 "need 3 variable names x, y, t" - U = weierstrass_chart(X) - R = ambient_coordinate_ring(U) - x, y, t = gens(R) - loc_eqn = first(gens(modulus(OO(U)))) - E = generic_fiber(X)::EllipticCurve - f = equation(E) - kk = base_ring(X) - kkt_frac_XY = parent(f)::MPolyRing - (xx, yy) = gens(kkt_frac_XY) - kkt_frac = coefficient_ring(kkt_frac_XY)::AbstractAlgebra.Generic.FracField - kkt = base_ring(kkt_frac)::PolyRing - T = first(gens(kkt)) - -# kk = base_ring(U) -# kkt, T = polynomial_ring(kk, :T, cached=false) -# kkt_frac = fraction_field(kkt) -# kkt_frac_XY, (xx, yy) = polynomial_ring(kkt_frac, [:X, :Y], cached=false) - R_to_kkt_frac_XY = hom(R, kkt_frac_XY, [xx, yy, kkt_frac_XY(T)]) - - f_loc = first(gens(modulus(OO(U)))) - @assert f == R_to_kkt_frac_XY(f_loc) && _is_in_weierstrass_form(f) "local equation is not in Weierstrass form" - a = a_invariants(E) - - u_loc = u[U]::AbstractAlgebra.Generic.FracFieldElem # the representative on the Weierstrass chart - - # Set up the ambient_coordinate_ring of the new Weierstrass-chart - kkt2, t2 = polynomial_ring(kk, names[3], cached=false) - kkt2_frac = fraction_field(kkt2) - S, (x2, y2) = polynomial_ring(kkt2_frac, names[1:2], cached=false) - FS = fraction_field(S) - - # Helper function - my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) - - # We verify the assumptions made on p. 44 of - # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" - # for the first case considered there. - @assert all(x->isone(denominator(x)), a) "local equation does not have the correct form" - a = numerator.(a) - @assert iszero(a[1]) "local equation does not have the correct form" - @assert degree(a[2]) <= 4 "local equation does not have the correct form" - @assert iszero(a[3]) "local equation does not have the correct form" - @assert degree(a[4]) <= 8 "local equation does not have the correct form" - @assert degree(a[5]) <= 12 "local equation does not have the correct form" # This is really a₆ in the notation of the paper, a₅ does not exist. - # reduce fraction - u_frac = R_to_kkt_frac_XY(numerator(u_loc))//R_to_kkt_frac_XY(denominator(u_loc)) - u_num = numerator(u_frac) - u_den = denominator(u_frac) - if case == :case1 - # D = 2O - u_poly = u_num*inv(u_den) # Will throw if the latter is not a unit - # Extract a(t) and b(t) as in the notation of the paper - a_t = my_const(coeff(u_poly, [xx, yy], [0, 0])) - b_t = my_const(coeff(u_poly, [xx, yy], [1, 0])) - - a_t = evaluate(a_t, x2) - b_t = evaluate(b_t, x2) - phi = hom(R, FS, FS.([(t2 - a_t)//b_t, y2, x2])) - f_trans = phi(f_loc) - return numerator(f_trans), phi - elseif case == :old - # D = O + P - @assert degree(u_num, 2) == 1 && degree(u_num, 1) <= 1 "numerator does not have the correct degree" - @assert degree(u_den, 1) == 1 && degree(u_den, 2) == 0 "denominator does not have the correct degree" - - # We expect a form as on p. 44, l. -4 - denom_unit = my_const(coeff(u_den, [xx, yy], [1, 0])) - x0 = -inv(denom_unit)*my_const(coeff(u_den, [xx, yy], [0, 0])) - b_t = inv(denom_unit)*my_const(coeff(u_num, [xx, yy], [0, 1])) - u_num = u_num - denom_unit * b_t * yy - a_t = inv(denom_unit)*my_const(coeff(u_num, [xx, yy], [1, 0])) - u_num = u_num - denom_unit * a_t * (xx - x0) - @assert is_constant(u_num) "numerator is not in the correct form" - y0 = my_const(coeff(u_num, [xx, yy], [0, 0])) * inv(denom_unit * b_t) - - @assert a_t + b_t*(yy + y0)//(xx - x0) == u_frac "decomposition failed" - # We have - # - # y ↦ (u - a_t) * (x - x₀) / b_t - y₀ = (t₂ - a_t(x₂)) * (y₂ - x₀(x₂)) / b_t(x₂) - y₀(x₂) - # x ↦ y₂ - # t ↦ x₂ - phi = hom(R, FS, FS.([y2, (t2 - evaluate(a_t, x2)) * (y2 - evaluate(x0, x2)) // evaluate(b_t, x2) - evaluate(y0, x2), x2])) - f_trans = phi(f_loc) - eqn1 = numerator(f_trans) - # According to - # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" - # p. 45, l. 1 we expect the following cancelation to be possible: - divisor_num = evaluate(numerator(x0), x2) - divisor_den = evaluate(denominator(x0), x2) - divisor = divisor_den * y2 - divisor_num - success, eqn1 = divides(eqn1, divisor) # This division must only be possible in the ring K(x2)[y2]. - # Hence, multiplying by the denominator `divisor_den` is - # merely an educated guess. - @assert success "division failed" - return eqn1, phi - elseif case == :case3 - # D = O + T - - @assert u_den == xx "elliptic parameter was not brought to the correct form" - @assert degree(u_num, 1) <= 1 && degree(u_num, 2) <= 1 "numerator does not have the correct degrees" - a_t = my_const(coeff(u_num, [xx, yy], [1, 0])) - b_t = my_const(coeff(u_num, [xx, yy], [0, 1])) - - # New Weierstrass equation is of the form - # - # x^2 = h(t, u) - # - # so y₂ = x, x₂ = t, and t₂ = u. - # - # We have u = a_t + b_t * y/x ⇒ y = (u - a_t) * x / b_t = (t₂ - a_t(x₂)) * y₂ / b_t(x₂) - phi = hom(R, FS, FS.([y2, (t2 - evaluate(a_t, x2)) * y2 // evaluate(b_t, x2), x2])) - f_trans = phi(f_loc) - eqn1 = numerator(f_trans) - # According to - # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" - # p. 45, l. 15 we expect the following cancelation to be possible: - success, eqn1 = divides(eqn1, y2) - @assert success "equation did not come out in the anticipated form" - return eqn1, phi - elseif case == :case2 - # D = O + P - @assert degree(u_num, 2) == 1 && degree(u_num, 1) <= 1 "numerator does not have the correct degree" - @assert degree(u_den, 1) == 1 && degree(u_den, 2) <= 1 "denominator does not have the correct degree" - - # u = (ax + by + c)/(a'x + b'y + c') - an = my_const(coeff(u_num, [xx, yy], [1, 0])) - bn = my_const(coeff(u_num, [xx, yy], [0, 1])) - cn = my_const(coeff(u_num, [xx, yy], [0, 0])) - - ad = my_const(coeff(u_den, [xx, yy], [1, 0])) - bd = my_const(coeff(u_den, [xx, yy], [0, 1])) - cd = my_const(coeff(u_den, [xx, yy], [0, 0])) - - @assert (an*xx+bn*yy+cn)//(ad*xx+bd*yy+cd) == u_frac "decomposition failed" - - - v = solve(matrix(parent(an), 2, 2, [-an, bn,-ad, bd]), matrix(parent(an), 2, 1, [cn, cd]); side=:right) - x0 = v[1,1] - y0 = v[2,1] - @assert evaluate(f_loc,[x0,y0,gen(parent(x0))])==0 - - ad = evaluate(ad,x2) - an = evaluate(an,x2) - bd = evaluate(bd,x2) - bn = evaluate(bn,x2) - cn = evaluate(cn,x2) - cd = evaluate(cd,x2) - #x0 = evaluate(x0,x2) - #y0 = evaluate(y0,x2) - - - imgy = -FS(((ad*t2 - an )*y2 + (cd*t2 -cn)) //(bd*t2 -bn)) - - # We have - # - # y ↦ -((ad u - an )x + (cd u -cn)) // (bd*u -bn) - # x ↦ y₂ - # t ↦ x₂ - phi = hom(R, FS, [y2, imgy, x2]) - f_trans = phi(f_loc) - eqn1 = numerator(f_trans) - # According to - # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" - # p. 45, l. 1 we expect the following cancellation to be possible: - divisor_num = evaluate(numerator(x0), x2) - divisor_den = evaluate(denominator(x0), x2) - divisor = divisor_den * y2 - divisor_num - success, eqn1 = divides(eqn1, divisor) # This division must only be possible in the ring K(x2)[y2]. - # Hence, multiplying by the denominator `divisor_den` is - # merely an educated guess. - @assert success "division failed" - return eqn1, phi - else - error("case not recognized") - end -end - -@doc raw""" - _compute_mwl_basis(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) -> ZZLat, Vector{<:EllipticCurvePoint} - -Return a tuple `(M, B)` where `B` is an LLL-reduced basis of the sublattice `M` of the -Mordell-Weil lattice of ``X`` generated by `mwl_gens`. -""" -function _compute_mwl_basis(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) - # it would be good to have the height pairing implemented - basis,tors, SX = _algebraic_lattice(X, mwl_gens) - basisTriv, GTriv = trivial_lattice(X) - r = length(basisTriv) - l = length(mwl_gens) - V = ambient_space(SX) - rk = rank(V) - G = ZZ.(gram_matrix(V)) - # project away from the trivial lattice - pr_mwl = orthogonal_projection(V,basis_matrix(SX)[1:r, :]) - BMWL = pr_mwl.matrix[r+1:end, :] - GB = gram_matrix(V,BMWL) - @assert rank(GB) == rk-r - _, u = hnf_with_transform(ZZ.(denominator(GB) * GB)) - B = u[1:rk-r,:] * BMWL - - MWL = lll(lattice(V, B, isbasis=false)) - u = solve(BMWL, basis_matrix(MWL); side=:left) - u = ZZ.(u) - mwl_basis = [sum(u[i,j] * mwl_gens[j] for j in 1:length(mwl_gens)) for i in 1:nrows(u)] - return MWL, mwl_basis -end - -function fibration_on_weierstrass_model(X::EllipticSurface) - if !isdefined(X, :fibration_weierstrass_model) - weierstrass_model(X) # trigger caching - end - return X.fibration_weierstrass_model -end - -function fibration(X::EllipticSurface) - if !isdefined(X, :fibration) - X.fibration = compose(weierstrass_contraction(X), fibration_on_weierstrass_model(X)) - end - return X.fibration -end - -function _local_pushforward(loc_map::AbsAffineSchemeMor, I::Ideal) - U_sub = domain(loc_map) - E, inc_E = sub(U_sub, I) # The subscheme of the divisor - E_simp = simplify(E) # Eliminate superfluous variables - id, id_inv = identification_maps(E_simp) - - comp = compose(compose(id, inc_E), loc_map) - - pb = pullback(comp) - K = kernel(pb) - return K -end - -function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) - @assert is_isomorphism(step) "morphism must be an isomorphism" - X = domain(step) - Y = codomain(step) - UX = weierstrass_chart_on_minimal_model(X) - UY = weierstrass_chart_on_minimal_model(Y) - @assert codomain_chart(step) === UY - fracs = coordinate_images(step) - - WY, _ = weierstrass_model(Y) - UWY = weierstrass_chart(Y) - - to_weierstrass_Y = morphism_from_rational_functions(X, WY, UX, UWY, fracs, check=false) - - fibration_proj_Y = fibration(Y) - - BY = codomain(fibration_proj_Y) - UBY = codomain(covering_morphism(fibration_proj_Y)[UY]) - - composit = morphism_from_rational_functions(X, BY, UX, UBY, [fracs[3]], check=false) - - lat_X = algebraic_lattice(X)[1] - if !has_attribute(lat_X[1], :is_prime) - ex, pt, F = irreducible_fiber(X) - ex || error("no irreducible fiber found; case not implemented") - lat_X[1] = weil_divisor(F) - set_attribute!(lat_X[1], :is_prime=>true) - set_attribute!(first(components(lat_X[1])), :is_prime=>true) - end - - # We first estimate for every element in the lattic of X whether its image - # will be a fiber component, or a (multi-)section. - pre_select = IdDict{AbsWeilDivisor, AbsIdealSheaf}() - - for D in lat_X - @assert length(components(D)) == 1 "divisors in the algebraic lattice must be prime" - I = first(components(D)) - @assert has_is_prime(I) && is_prime(I) "ideal sheaf must be known to be prime" - pre_select[D] = _pushforward_prime_divisor(composit, I) - end - - - # Now we map them one by one using the knowledge gained above - result = IdDict{AbsWeilDivisor, AbsWeilDivisor}() - co_ring = coefficient_ring(zero_section(Y)) - - n = length(lat_X) - mwr = rank(mordell_weil_lattice(X)) - for (i, D) in enumerate(lat_X) - @vprint :EllipticSurface 2 "$((i, D, pre_select[D]))\n" - # D is a non-section - Q = pre_select[D] - I = first(components(D)) - @vprint :EllipticSurface 2 "$(typeof(I))\n" - dom_chart = _find_good_representative_chart(I) - if i > n - mwr # if this is a section - dom_chart = weierstrass_chart_on_minimal_model(X) - end - - if dim(Q) == 0 - @vprint :EllipticSurface 3 "image will be a fiber component\n" - # find the fiber - if is_one(Q(UBY)) # fiber over infinity - # collect all components - comps = AbsWeilDivisor[] - for (pt, _, F, E, _) in reducible_fibers(Y) - if is_zero(pt[2]) # if this is in the fiber over the point at ∞ ∈ ℙ¹ - append!(comps, E[2:end]) - end - end - @vprint :EllipticSurface 3 "found total of $(length(comps)) possible components\n" - - # collect all charts - codomain_charts = AbsAffineScheme[] - if is_empty(comps) # The fiber over infinity - @vprint :EllipticSurface 3 "the image must be the fiber over infinity" - codomain_charts = affine_charts(Y) # TODO: How can we restrict the charts then? - else - codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(D->!isone(first(components(D))(V)), comps)] - end - @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible" - - if i > n - mwr # If D is a section - @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" - pt = X.MWL[i-(n-mwr)] - res = _pushforward_section(step, pt; divisor=D, codomain_charts) - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - else - @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" - loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; domain_chart = dom_chart, codomain_charts) - - loc_map === nothing && error("pushforward preparation did not succeed") - @assert !is_one(I(domain(loc_map))) - K = _local_pushforward(loc_map, I(domain(loc_map))) - @assert !is_one(K) - - JJ = ideal(OO(cod_chart), gens(K)) - res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) - - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - end - continue - end - @vprint :EllipticSurface 3 "image will not be in the fiber over infinity\n" - - # fiber over some point ≂̸ ∞. - t = first(gens(OO(UBY))) - - codomain_charts = copy(affine_charts(Y)) - - # Restrict the codomain charts if applicable - for (i, (p, _, F, E, _)) in enumerate(reducible_fibers(Y)) - p[2] == 0 && continue # Fiber over infinity already caught above - t0 = p[1]//p[2] - ideal(OO(UBY), t - t0) == Q(UBY) || continue - - # Collect all patches - codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(I->!isone(I(V)), components(F))] - break - end - @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible\n" - - if i > n - mwr # If D is a section - @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" - pt = X.MWL[i-(n-mwr)] - res = _pushforward_section(step, pt; divisor=D, codomain_charts) - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - else - @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" - loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; codomain_charts) - loc_map === nothing && error("preparation for pushforward did not succeed") - - @assert !is_one(I(domain(loc_map))) - K = _local_pushforward(loc_map, I(domain(loc_map))) - @assert !is_one(K) - JJ = ideal(OO(cod_chart), gens(K)) - res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) - - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - end - else - # "pushforward will be a section" - if i > n - mwr # If D is a section - pt = X.MWL[i-(n-mwr)] - res = _pushforward_section(step, pt; divisor=D, codomain_charts=[weierstrass_chart_on_minimal_model(Y)]) - if res === nothing - # The only section not visible in the weierstrass chart is the zero section - result[D] = zero_section(Y) - continue - end - - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - else - loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I, domain_chart = dom_chart, codomain_charts = [weierstrass_chart_on_minimal_model(Y)]) - - if loc_map === nothing - # The only section not visible in the weierstrass chart is the zero section - result[D] = zero_section(Y) - continue - end - - @assert !is_one(I(domain(loc_map))) - K = _local_pushforward(loc_map, I(domain(loc_map))) - @assert !is_one(K) - JJ = ideal(OO(cod_chart), gens(K)) - res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) - - result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) - end - end - end - - res = AbsWeilDivisor[result[D] for D in lat_X] - for a in res - set_attribute!(first(components(a)), :_self_intersection, -2) - end - # the first one is the class of the fiber; set that one back - set_attribute!(first(components(first(res))), :_self_intersection, 0) - return res -end - -#= -# The map is not dominant and can hence not be realized as a MorphismFromRationalFunctions. -# We keep the code for the moment as it will probably help us to reconstruct this map as a -# proper CoveredSchemeMorphism, once this is needed. -=# -function morphism_from_section( - X::EllipticSurface, P::EllipticCurvePoint; - divisor::AbsWeilDivisor=EllipticSurfaceSection(X, P) - ) - U = weierstrass_chart_on_minimal_model(X) - II = first(components(divisor)) - - # For the zero section we can not use the Weierstrass chart - if is_infinite(P) - return identity_map(X) - end - @assert !is_one(II(U)) - - C, inc_C = sub(II) - - UC = domain(first(maps_with_given_codomain(inc_C, U))) - - B = codomain(fibration(X)) - V = codomain(fibration(X)[weierstrass_chart_on_minimal_model(X)]) - - kkt = OO(V)::MPolyRing - @assert ngens(kkt) == 1 - t = first(gens(kkt)) - img_gens = [evaluate(P.coordx, t), evaluate(P.coordy, t), t] - - Fkkt = fraction_field(kkt) - img_gens2 = Fkkt.(img_gens) - # TODO: Cache? - iso = morphism_from_rational_functions(B, C, V, UC, img_gens2, check=false) - return iso, inc_C -end - -######################################################################## -# Translations by sections # -######################################################################## - -function translation_morphism(X::EllipticSurface, P::EllipticCurvePoint; - divisor::AbsWeilDivisor=EllipticSurfaceSection(X, P) - ) - E = generic_fiber(X) - @assert parent(P) === E "point does not lay on the underlying elliptic curve" - U = weierstrass_chart_on_minimal_model(X) - is_zero(P) && return identity_map(X) - - # We construct the translation by P as a morphism of rational functions - kT = base_field(E) - T = first(gens(kT)) - - R = ambient_coordinate_ring(U) - x, y, t = gens(R) - - a1, a2, a3, a4, a6 = [evaluate(a, t) for a in a_invariants(E)] - - p_x = evaluate(P[1], t) - p_y = evaluate(P[2], t) - - # Formulas adapted from Hecke/src/EllCrv/EllCrv.jl - m = (p_y - y)//(p_x - x) - pb_x = - x - p_x - a2 + a1*m + m^2 - pb_y = - y - m*(pb_x - x) - a1*pb_x - a3 - - F = fraction_field(R) - - result = morphism_from_rational_functions(X, X, U, U, F.([pb_x, pb_y, t]), check=true) - set_attribute!(result, :is_isomorphism=>true) - return result -end - -function _pushforward_section( - phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}, - P::EllipticCurvePoint; - divisor::AbsWeilDivisor=EllipticSurfaceSection(domain(phi), P), - codomain_charts::Vector{<:AbsAffineScheme} = affine_charts(codomain(phi)) - ) - X = domain(phi)::EllipticSurface - Y = codomain(phi)::EllipticSurface - D = divisor - I = first(components(D)) - iso, inc = morphism_from_section(X, P; divisor=D) - U = weierstrass_chart_on_minimal_model(X) - inc_loc = first(maps_with_given_codomain(inc, U)) - U_C = domain(inc_loc) - phi_loc, _, V = _prepare_pushforward_prime_divisor(phi, I; domain_chart=U, codomain_charts) - phi_loc === nothing && return nothing # Indicate that the given selection of codomain charts did not lead to a result - W = codomain(fibration(X)[U]) - iso_loc = _restrict_properly(cheap_realization(iso, W, U_C), U_C) - inc_dom_phi_loc = inclusion_morphism(domain(phi_loc)) - UU, to_U_C, to_U = fiber_product(inc_loc, inc_dom_phi_loc) - WW, a, b = fiber_product(iso_loc, to_U_C) - psi_loc = compose(compose(b, to_U), phi_loc) - K = kernel(pullback(psi_loc)) - J = ideal(OO(V), gens(K)) - JJ = PrimeIdealSheafFromChart(Y, V, J) - return JJ -end - -# Find a moebius transformation which sends a given set of three points in ℙ¹ to another set -# of three points. -function find_moebius_transformation( - orig_pts::Vector{<:Vector{<:FieldElem}}, - new_pts::Vector{<:Vector{<:FieldElem}} - ) - kk = parent(first(orig_pts)) - a = [a[1] for a in orig_pts] - b = [b[1] for b in new_pts] - @assert all(a->isone(a[2]), orig_pts) "not implemented for non-normalized or infinite points" - @assert all(a->isone(a[2]), new_pts) "not implemented for non-normalized or infinite points" - return find_moebius_transformation(a, b) -end - -function find_moebius_transformation( - orig_pts::Vector{<:FieldElem}, - new_pts::Vector{<:FieldElem} - ) - length(orig_pts) == 3 || error("exactly three points are needed") - @assert length(orig_pts) == length(new_pts) "number of points must coincide" - kk = parent(first(orig_pts)) - a = orig_pts - b = new_pts - - # Set up the matrix mapping the first three points to 0, 1, ∞ - A = kk[(a[2] - a[3]) (-a[1]*(a[2] - a[3])); (a[2] - a[1]) (-a[3]*(a[2] - a[1]))] - - # Set up the matrix mapping the second three points to 0, 1, ∞ - B = kk[(b[2] - b[3]) (-b[1]*(b[2] - b[3])); (b[2] - b[1]) (-b[3]*(b[2] - b[1]))] - - C = inv(B)*A - return x->(C[1,1]*x + C[1, 2], C[2,1]*x + C[2,2]) -end - -# Given a bivariate polynomial over a univariate function field, -# normalize the associated elliptic curve so that the usual constructor -# for elliptic surfaces digests it, and then return it, together with the -# transformation on the algebraic side. -# -# The transformation is a morphism from the fraction field of the -# parent of g to the fraction field of the `ambient_coordinate_ring` -# of the `weierstrass_chart` of the resulting surface. -function _elliptic_surface_with_trafo(g::MPolyRingElem{<:AbstractAlgebra.Generic.FracFieldElem}; minimize::Bool=true) - x, y = gens(parent(g)) - E = elliptic_curve(g, x, y) - kkt = base_field(E) - kk = coefficient_ring(base_ring(kkt)) - - FFt, t = rational_function_field(kk, :t) - - # The following three commands won't work unless we convert to a rational_function_field - EE = base_change(x->evaluate(x, t), E) - if minimize - EE = tates_algorithm_global(EE) - EE, _ = short_weierstrass_model(EE) - EE, _ = integral_model(EE) - end - - # ...and back. - E2 = base_change(x->evaluate(x, gen(kkt)), EE) - - @assert is_isomorphic(E, E2) - a, b, _ = rational_maps(isomorphism(E2, E)) - - eq_E = equation(E) - eq_E2 = equation(E2) - - h = evaluate(eq_E, [a, b]) - @assert divides(h, eq_E2)[1] - - cod = parent(a)::MPolyRing - - #phi = hom(R, cod, cod.([a, b])) - #Phi = extend_domain_to_fraction_field(phi) - - result = elliptic_surface(E2, 2) - W = weierstrass_chart(result) - R = ambient_coordinate_ring(W) - FR = fraction_field(R) - - help_map = hom(cod, FR, t->evaluate(t, FR(R[3])), FR.([R[1], R[2]])) - A = help_map(a) - B = help_map(b) - - res_map = hom(parent(g), FR, t->evaluate(t, FR(R[3])), [A, B]) - return result, extend_domain_to_fraction_field(res_map) -end - -# Given two abstractly isomorphic elliptic surfaces X and Y over ℙ¹, -# find all moebius transformation of the base which preserve the critical -# values of the projections, try to lift them to morphisms X -> Y and -# return the list of such morphisms for which the lift was successful. -function admissible_moebius_transformations( - X::EllipticSurface, - Y::EllipticSurface - ) - result = MorphismFromRationalFunctions[] - for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=false) - push!(result, _moebius_to_morphism_from_rational_functions(X, Y, img_gens)) - end - return result -end - -function admissible_moebius_transformations_on_weierstrass_chart( - X::EllipticSurface, - Y::EllipticSurface - ) - result = MapFromFunc[] - for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=true) - push!(result, _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens)) - end - return result -end - -function _admissible_moebius_transformations( - X::EllipticSurface, - Y::EllipticSurface; on_weierstrass_model=true - ) - EX = generic_fiber(X) - EY = generic_fiber(Y) - -# kkt = base_field(EX) -# @assert kkt === base_field(EY) "base fields of the generic fibers must coincide" - kk = base_ring(X) - @assert kk === base_ring(Y) "elliptic surfaces must be defined over the same field" - - dX = numerator(discriminant(EX))::PolyRingElem - dY = numerator(discriminant(EY))::PolyRingElem - - vX = roots(dX) - @assert all(is_one(degree(a)) for (a, k) in factor(dX)) "not all critical values are rational over the given ground field" - - vY = roots(dY) - @assert all(is_one(degree(a)) for (a, k) in factor(dY)) "not all critical values are rational over the given ground field" - -# for (c, _) in reducible_fibers(X) -# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" -# end -# for (c, _) in reducible_fibers(Y) -# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" -# end - - # Use the first three elements of vX and map them to three elements of vY. - # Then check whether the resulting transformation preserves everything. - - candidates = [] - - @assert length(vX) >= 3 "at least three reducible fibers are needed" - length(vX) == length(vY) || return candidates # No moebius transformation is possible in this case - kkt = base_field(EX) - t = gen(kkt) - p1 = vX[1:3] - for i in vY - for j in vY - i == j && continue - for k in vY - (i == k || j == k) && continue - p2 = [i, j, k] - mt = find_moebius_transformation(p1, p2) - any(is_zero(mt(x)[2]) for x in vX) && continue # reducible fibers over ∞ are not implemented at the moment. - any(!(mt(x)[1]//mt(x)[2] in vY) for x in vX) && continue # the transformation does not preserve all admissible fibers in this case - p, q = mt(t) - img_t = (p//q)::typeof(t) - EYbc = base_change(f->evaluate(f, img_t), EY) - is_isomorphic(EYbc, EX) || continue - iso_ell = isomorphism(EX, EYbc) - push!(candidates, _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model)) - end - end - end - return candidates -end - -function _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model) - EX = generic_fiber(X) - EY = generic_fiber(Y) - # Set up some variables - kkt = base_field(EX) - t = gen(kkt) - if on_weierstrass_model - WX = weierstrass_chart(X) - WY = weierstrass_chart(Y) - else - WX = weierstrass_chart_on_minimal_model(X) - WY = weierstrass_chart_on_minimal_model(Y) - end - RX = ambient_coordinate_ring(WX) - FRX = fraction_field(RX) - RY = ambient_coordinate_ring(WY) - FRY = fraction_field(RY) - - # Construct the isomorphism of elliptic surfaces explicitly - - a, b, _ = rational_maps(iso_ell) - kkTxy = parent(a) - to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) - A = to_FRX(a) - B = to_FRX(b) - P, Q = mt(FRX(RX[3])) - img_T = (P//Q)::elem_type(FRX) - img_gens = [A, B, img_T] - return img_gens -end - -function _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens) - WY = weierstrass_chart(Y) - WX = weierstrass_chart(X) - RX = ambient_coordinate_ring(WX) - FRX = fraction_field(RX) - RY = ambient_coordinate_ring(WY) - FRY = fraction_field(RY) - - return extend_domain_to_fraction_field(hom(RY, FRX, img_gens)) -end - -function _moebius_to_morphism_from_rational_functions(X, Y, img_gens) - WY = weierstrass_chart_on_minimal_model(Y) - WX = weierstrass_chart_on_minimal_model(X) - - loc_res = morphism_from_rational_functions(X, Y, WX, WY, img_gens; check=true) - set_attribute!(loc_res, :is_isomorphism=>true) - return loc_res -end - -# An internal helper routine to verify that a given isomorphism of elliptic surfaces -# does indeed give an isomorphism on their generic fibers. -function check_isomorphism_on_generic_fibers(phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) - X = domain(phi) - Y = codomain(phi) - @assert domain_chart(phi) === weierstrass_chart_on_minimal_model(X) - @assert codomain_chart(phi) === weierstrass_chart_on_minimal_model(Y) - EX = generic_fiber(X) - EY = generic_fiber(Y) - a, b, c = coordinate_images(phi) - - hX = equation(EX) - RX = parent(hX) - FX = fraction_field(RX) - kktX = coefficient_ring(RX) - - hY = equation(EY) - RY = parent(hY) - FY = fraction_field(RY) - kktY = coefficient_ring(RY) - - A = evaluate(a, [RX[1], RX[2], RX(gen(kktX))]) - B = evaluate(b, [RX[1], RX[2], RX(gen(kktX))]) - C = evaluate(c, [RX[1], RX[2], RX(gen(kktX))]) - - help_map = hom(RY, FX, t->evaluate(t, C), [A, B]) - - hh = help_map(hY) - - return divides(hX, numerator(hh))[1] -end - -function isomorphism_from_generic_fibers( - X::EllipticSurface, Y::EllipticSurface, f::Hecke.EllCrvIso - ) - EX = generic_fiber(X) - EY = generic_fiber(Y) - iso_ell = f - @req domain(f) == EX "must be an isomorphism of the generic fibers" - @req codomain(f) == EY "must be an isomorphism of the generic fibers" - a, b, _ = rational_maps(iso_ell) - kt = base_field(EX) - t = gen(kt) - - # Make sure we got something reasonable - h2 = equation(EY) - pb_h2 = evaluate(h2, [a, b]) - @assert divides(pb_h2, equation(parent(pb_h2), EX))[1] - - WX = weierstrass_chart_on_minimal_model(X) - RX = ambient_coordinate_ring(WX) - FRX = fraction_field(RX) - WY = weierstrass_chart_on_minimal_model(Y) - RY = ambient_coordinate_ring(WY) - FRY = fraction_field(RY) - - kkTxy = parent(a) - to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) - A = to_FRX(a) - B = to_FRX(b) - img_gens = [A, B, FRX(RX[3])] - m = morphism_from_rational_functions(X, Y, WX, WY, FRX.(img_gens); check=false) - set_attribute!(m, :is_isomorphism=>true) - return m -end - -# Given two elliptic surfaces X and Y with abstractly isomorphic generic -# fibers, construct the corresponding isomorphism X -> Y. -function isomorphism_from_generic_fibers( - X::EllipticSurface, Y::EllipticSurface - ) - EX = generic_fiber(X) - EY = generic_fiber(Y) - is_isomorphic(EX, EY) || error("generic fibers are not isomorphic") - iso_ell = isomorphism(EX, EY) - return isomorphism_from_generic_fibers(X, Y, iso_ell) -end - - -""" - pushforward_on_algebraic_lattices(f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) -> QQMatrix - -Return the pushforward `f_*: V_1 -> V_2` where `V_i` is the ambient quadratic space of the `algebraic_lattice`. - -This assumes that the image `f_*(V_1)` is contained in `V_2`. If this is not the case, you will get -``f_*`` composed with the orthogonal projection to `V_2`. -""" -function pushforward_on_algebraic_lattices(f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) - imgs_divs = _pushforward_lattice_along_isomorphism(f) - M = matrix([basis_representation(codomain(f),i) for i in imgs_divs]) - V1 = ambient_space(algebraic_lattice(domain(f))[3]) - V2 = ambient_space(algebraic_lattice(codomain(f))[3]) - # keep the check on since it is simple compared to all the other computations done here - fstar = hom(V1,V2, M; check=true) - return fstar -end - -# Given an irreducible divisor D on an elliptic surface X, try to extract a point -# on the generic fiber from it. The return value is `nothing` in case this does not succeed. -function point_on_generic_fiber_from_divisor(I::AbsIdealSheaf{<:EllipticSurface}; check::Bool=true) - X = scheme(I) - @check dim(I) == 1 "ideal sheaf must be of dimension one" - return point_on_generic_fiber_from_divisor(WeilDivisor(X, I; check=false); check) -end - -function point_on_generic_fiber_from_divisor(D::AbsWeilDivisor{<:EllipticSurface}; check::Bool=true) - X = ambient_scheme(D) - E = generic_fiber(X) - ex, pt, F = irreducible_fiber(X) - WF = weil_divisor(F) - # TODO: Also cover this case by considering the class of a reducible fiber? - !ex && error("no irreducible fiber exists on this algebraic surface") - @assert length(components(D)) == 1 "divisor must be irreducible" - - I = first(components(D)) - fib = fibration(X) - - # Check a necessary criterion for being a section - # J = pushforward(fib, I) - # is_one(dim(J)) || return nothing - is_zero(intersect(D, WF)) && return nothing -# @check begin -# J = pushforward(fib, I) -# is_one(dim(J)) -# end "given divisor can not be a section" - - #@check is_one(intersect(D, WF)) "intersection number with irreducible fiber is not one" - - WX = weierstrass_chart_on_minimal_model(X) - IWX = I(WX) - is_one(IWX) && return infinity(E) # Point must be the zero section - R = ambient_coordinate_ring(WX) - (x, y, t) = gens(R) - - # In case of a multisection do some extra preparation; see below. - !is_one(intersect(D, WF)) && return point_on_generic_fiber_from_divisor(_prepare_section(D)) - - g = gens(groebner_basis(saturated_ideal(IWX), ordering=lex(gens(R)))) - - # extract the coefficients for the section - kkt = base_field(E) - - # First extract the y-coordinate - i = findfirst(f->(is_zero(degree(f, 1)) && is_one(degree(f, 2))), g) - i === nothing && return nothing - #i === nothing && error("no suitable polynomial found to read off point coordinates") - f = g[i] - y_coord = one(kkt) - ev_vals = [zero(kkt), one(kkt), gen(kkt)] - num = zero(kkt) - den = zero(kkt) - for t in terms(f) - degree(t, 2) == 1 && (den = den - evaluate(t, ev_vals)) - degree(t, 2) == 0 && (num = num + evaluate(t, ev_vals)) - end - y_coord = num//den - - # Now extract the x-coordinate - i = findfirst(f->(is_one(degree(f, 1))), g) - i === nothing && return nothing - #i === nothing && error("no suitable polynomial found to read off point coordinates") - f = g[i] - x_coord = one(kkt) - ev_vals = [one(kkt), y_coord, gen(kkt)] - num = zero(kkt) - den = zero(kkt) - for t in terms(f) - degree(t, 1) == 1 && (den = den - evaluate(t, ev_vals)) - degree(t, 1) == 0 && (num = num + evaluate(t, ev_vals)) - end - x_coord = num//den - - is_zero(evaluate(equation(E), [x_coord, y_coord])) || return nothing - #@assert is_zero(evaluate(equation(E), [x_coord, y_coord])) "esteemed point does not lie on the curve" - P = E([x_coord, y_coord]) - return P -end - -# Given an isomorphism phi : X -> Y of elliptic surfaces and a full algebraic lattice L on X, -# push forward the divisors D from L to Y and try to extract points on the generic fiber from -# them. -# -# This returns a list consisting of the points on the generic fiber. -function extract_mordell_weil_basis(phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) - X = domain(phi) - Y = codomain(phi) - is_isomorphism(phi) || error("morphism must be an isomorphism") - pf_lat = _pushforward_lattice_along_isomorphism(phi) - points = EllipticCurvePoint[] - for D in pf_lat - P = point_on_generic_fiber_from_divisor(D) - P === nothing && continue - push!(points, P) - end - return points -end - -function _prepare_section(D::AbsWeilDivisor{<:EllipticSurface}) - X = ambient_scheme(D) - WX = weierstrass_chart_on_minimal_model(X) - R = ambient_coordinate_ring(WX) - I = first(components(D)) - IWX = I(WX) - # We have a multisection in this case. - # To get a section from it, apply arXiv:2103.15101, Algorithm 1. - - # Build up a helper ring - kkt = base_field(generic_fiber(X)) - f = equation(generic_fiber(X)) - kktXY = parent(f) - (xx, yy) = gens(kktXY) - for (c, e) in zip(coefficients(f), exponents(f)) - if e == [0, 2] - @assert is_one(c) "polynomial is not normalized" - end - end - f = yy^2 - f # prepare the f from the Lemma - #kktXY, (xx, yy) = polynomial_ring(kkt, [:X, :Y]; cached=false) - - @assert coefficient_ring(R) === coefficient_ring(base_ring(kkt)) - help_map = hom(R, kktXY, [xx, yy, kktXY(gen(kkt))]) - - J = ideal(kktXY, help_map.(gens(saturated_ideal(IWX)))) - - J_gens = gens(groebner_basis(J, ordering=lex([yy, xx]))) - i = findfirst(f->degree(f, 2) == 0, J_gens) - i === nothing && error("assertion of Lemma could not be verified") - g = J_gens[i] - i = findfirst(f->degree(f, 2) == 1, J_gens) - i === nothing && error("assertion of Lemma could not be verified") - h = J_gens[i] - c = zero(kkt) - for t in terms(h) - if degree(t, 2) == 1 - c = c + evaluate(t, [zero(kkt), one(kkt)]) - end - end - !isone(c) && (h = inv(c)*h) - h = yy - h - @assert J == ideal(kktXY, [g, yy-h]) - ff = equation(kktXY, generic_fiber(X)) - @assert parent(ff) === parent(f) - @assert ff == yy^2 - f - while total_degree(g) > 1 - g = divexact(h^2 - f, g) - p, q = divrem(h, g) - h = q - end - - F = fraction_field(R) - help_map_back = hom(kktXY, F, u->evaluate(u, F(R[3])), F.([R[1], R[2]])) - new_gens = [help_map_back(g), help_map_back(yy - h)] - sec_ideal = ideal(OO(WX), numerator.(new_gens)) - @assert dim(sec_ideal) == 1 - @assert is_prime(sec_ideal) - - # overwrite the local variables - I = PrimeIdealSheafFromChart(X, WX, sec_ideal) - return weil_divisor(I) -end - -# internal method used for two neighbor steps -# in the horizontal_decomposition -function _vertical_part(X::EllipticSurface, v::QQMatrix) - @req nrows(v)==1 "not a row vector" - _,tors, NS = algebraic_lattice(X) - E = generic_fiber(X) - @req ncols(v)==degree(NS) "vector of wrong size $(ncols(v))" - @req v in NS "not an element of the lattice" - mwl_rank = length(X.MWL) - rk_triv = rank(NS)-mwl_rank - n = rank(NS) - P = sum([ZZ(v[1,i])*X.MWL[i-rk_triv] for i in (rk_triv+1):n], init = E([0,1,0])) - p = zero_matrix(QQ, 1, rank(NS)) # the section part - p[1,end-mwl_rank+1:end] = v[1,end-mwl_rank+1:end] - p[1,2] = 1 - sum(p) # assert p.F = 1 by adding a multiple of the zero section O - - # P meets exactly one fiber component per fiber - # and that one must be simple, it can be the one meeting O or not - # assert this by adding fiber components under the additional condition that p stays in the algebraic lattice - simples = [] - E = identity_matrix(QQ,rank(NS)) - z = zero_matrix(QQ,1, rank(NS)) - r = 2 - for fiber in _trivial_lattice(X)[3] - fiber_type = fiber[2] - fiber_rk = fiber_type[2] - h = highest_root(fiber_type...) - simple_indices = [r+i for i in 1:ncols(h) if isone(h[1,i])] - simple_or_zero = [E[i:i,:] for i in simple_indices] - push!(simple_or_zero, z) - push!(simples,simple_or_zero) - r += fiber_rk - end - G = gram_matrix(ambient_space(NS)) - pG = (p*G)[1:1,3:r] - T = Tuple(simples) - GF = G[3:r,3:r] - candidates = QQMatrix[] - for s in Base.Iterators.ProductIterator(T) - g = sum(s)[:,3:r] - y = (g - pG) - xx = solve(GF,y;side=:left) - x = zero_matrix(QQ,1, rank(NS)) - x[:,3:r] = xx - if x in NS - push!(candidates,p+x) - end - end - @assert length(candidates)>0 - # Select the candidate congruent to v modulo Triv - mwg = _mordell_weil_group(X) - vmwg = mwg(vec(collect(v))) - candidates2 = [mwg(vec(collect(x))) for x in candidates] - i = findfirst(==(vmwg), candidates2) - t = mwg(vec(collect(p - candidates[i]))) - mwl_tors_gens = [mwg(vec(collect(i[2]))) for i in tors] - ag = abelian_group(zeros(ZZ,length(tors))) - mwlAb = abelian_group(mwg) - phi = hom(ag, mwlAb, mwlAb.(mwl_tors_gens)) - a = preimage(phi, mwlAb(t)) - for i in 1:ngens(ag) - P += a[i]*rational_point(tors[i][1]) - end - - p = candidates[i] - k = (p*G*transpose(p))[1,1] - # assert p^2 = -2 - p[1,1] = -k/2-1 - V = ambient_space(NS) - @hassert :EllipticSurface 1 inner_product(V, p, p)[1,1]== -2 - @hassert :EllipticSurface 1 mwg(vec(collect(p))) == mwg(vec(collect(p))) - @hassert :EllipticSurface 3 basis_representation(X,section(X,P))==vec(collect(p)) - return p, P -end - -function _vertical_part(X::EllipticSurface, v::Vector{QQFieldElem}) - vv = matrix(QQ,1,length(v),v) - p, P = _vertical_part(X,vv) - pp = vec(collect(p)) - return pp, P -end - -# TODO: Instead return an abelian group A and two maps. -# algebraic_lattice -> A -# A -> MWL= E(k(t)) -function _mordell_weil_group(X) - N = algebraic_lattice(X)[3] - V = ambient_space(N) - t = nrows(trivial_lattice(X)[2]) - Triv = lattice(V, identity_matrix(QQ,dim(V))[1:t,:]) - return torsion_quadratic_module(N, Triv;modulus=1, modulus_qf=1, check=false) -end - diff --git a/src/AlgebraicGeometry/AlgebraicGeometry.jl b/src/AlgebraicGeometry/AlgebraicGeometry.jl index 9e8392363439..09c30e3ce922 100644 --- a/src/AlgebraicGeometry/AlgebraicGeometry.jl +++ b/src/AlgebraicGeometry/AlgebraicGeometry.jl @@ -8,6 +8,11 @@ include("Surfaces/K3Auto.jl") include("Surfaces/AdjunctionProcess/AdjunctionProcess.jl") include("Surfaces/ParaRatSurfaces/ParametrizationSurfaces.jl") include("Surfaces/SurfacesP4.jl") +include("Surfaces/EllipticSurface/Types.jl") +include("Surfaces/EllipticSurface/EllipticSurface.jl") +include("Surfaces/EllipticSurface/NeighborStep.jl") +include("Surfaces/EllipticSurface/Morphisms.jl") +include("Surfaces/EllipticSurface/MoveMeToHecke.jl") include("RationalPoint/Types.jl") include("RationalPoint/PointSet.jl") include("RationalPoint/AffineRationalPoint.jl") diff --git a/experimental/Schemes/src/BlowupMorphism.jl b/src/AlgebraicGeometry/Schemes/BlowupMorphism/BlowupMorphism.jl similarity index 99% rename from experimental/Schemes/src/BlowupMorphism.jl rename to src/AlgebraicGeometry/Schemes/BlowupMorphism/BlowupMorphism.jl index 9a0aa5a5dffd..17b72a3f9462 100644 --- a/experimental/Schemes/src/BlowupMorphism.jl +++ b/src/AlgebraicGeometry/Schemes/BlowupMorphism/BlowupMorphism.jl @@ -1,8 +1,3 @@ -export BlowupMorphism -export center -export exceptional_divisor -export projection - ######################################################################## # `AbsDesingMor` and `AbsBlowupMorphism` # diff --git a/experimental/Schemes/src/BlowupMorphismTypes.jl b/src/AlgebraicGeometry/Schemes/BlowupMorphism/Types.jl similarity index 100% rename from experimental/Schemes/src/BlowupMorphismTypes.jl rename to src/AlgebraicGeometry/Schemes/BlowupMorphism/Types.jl diff --git a/experimental/Schemes/src/CoveredProjectiveSchemes.jl b/src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/CoveredProjectiveScheme.jl similarity index 100% rename from experimental/Schemes/src/CoveredProjectiveSchemes.jl rename to src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/CoveredProjectiveScheme.jl diff --git a/src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/Types.jl b/src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/Types.jl new file mode 100644 index 000000000000..40656fff78d7 --- /dev/null +++ b/src/AlgebraicGeometry/Schemes/CoveredProjectiveScheme/Types.jl @@ -0,0 +1,178 @@ +abstract type AbsProjectiveGluing{ + GluingType<:AbsGluing, + } +end + +@doc raw""" + LazyProjectiveGluing( + X::AbsProjectiveScheme, + Y::AbsProjectiveScheme, + BG::AbsGluing, + compute_function::Function, + gluing_data + ) + +Produce a container `pg` to host a non-computed `ProjectiveGluing` of ``X`` with ``Y``. + +The arguments consist of + + * the patches ``X`` and ``Y`` to be glued; + * a gluing `BG` of the `base_scheme`s of ``X`` and ``Y`` over which the + `ProjectiveGluing` to be computed sits; + * a function `compute_function` which takes a single argument `gluing_data` + of arbitrary type and actually carries out the computation; + * an arbitrary struct `gluing_data` that the user can fill with whatever + information is needed to properly feed their `compute_function`. + +The container `pg` can then be stored as the gluing of ``X`` and ``Y``. As soon +as it is asked about any data on the gluing beyond its two `patches`, it will +invoke the internally stored `compute_function` to actually carry out the computation +of the gluing and then serve the incoming request on the basis of that result. +The latter actual `ProjectiveGluing` will then be cached. +""" +mutable struct LazyProjectiveGluing{ + GluingType<:AbsGluing, + GluingDataType + } <: AbsProjectiveGluing{GluingType} + base_gluing::GluingType + patches::Tuple{AbsProjectiveScheme, AbsProjectiveScheme} + compute_function::Function + gluing_data::GluingDataType + underlying_gluing::AbsProjectiveGluing + + function LazyProjectiveGluing( + X::AbsProjectiveScheme, + Y::AbsProjectiveScheme, + BG::AbsGluing, + compute_function::Function, + gluing_data + ) + (base_scheme(X), base_scheme(Y)) == patches(BG) || error("gluing is incompatible with provided patches") + return new{typeof(BG), typeof(gluing_data)}(BG, (X, Y), compute_function, gluing_data) + end +end + +@doc raw""" + ProjectiveGluing( + G::GluingType, + incP::IncType, incQ::IncType, + f::IsoType, g::IsoType; + check::Bool=true + ) where {GluingType<:AbsGluing, IncType<:ProjectiveSchemeMor, IsoType<:ProjectiveSchemeMor} + +The `AbsProjectiveSchemeMorphism`s `incP` and `incQ` are open embeddings over open +embeddings of their respective `base_scheme`s. + + PX ↩ PU ≅ QV ↪ QY + π ↓ ↓ ↓ ↓ π + G : X ↩ U ≅ V ↪ Y + +This creates a gluing of the projective schemes `codomain(incP)` and `codomain(incQ)` +over a gluing `G` of their `base_scheme`s along the morphisms of `AbsProjectiveScheme`s +`f` and `g`, identifying `domain(incP)` and `domain(incQ)`, respectively. +""" +mutable struct ProjectiveGluing{ + GluingType<:AbsGluing, + IsoType1<:ProjectiveSchemeMor, + IncType1<:ProjectiveSchemeMor, + IsoType2<:ProjectiveSchemeMor, + IncType2<:ProjectiveSchemeMor, + } <: AbsProjectiveGluing{GluingType} + G::GluingType # the underlying gluing of the base schemes + inc_to_P::IncType1 + inc_to_Q::IncType2 + f::IsoType1 + g::IsoType2 + + ### + # Given two relative projective schemes and a gluing + # + # PX ↩ PU ≅ QV ↪ QY + # π ↓ ↓ ↓ ↓ π + # G : X ↩ U ≅ V ↪ Y + # + # this constructs the gluing of PX and QY along + # their open subsets PU and QV, given the two inclusions + # and isomorphisms over the gluing G in the base schemes. + function ProjectiveGluing( + G::GluingType, + incP::IncType1, incQ::IncType2, + f::IsoType1, g::IsoType2; + check::Bool=true + ) where {GluingType<:AbsGluing, IncType1<:ProjectiveSchemeMor,IncType2<:ProjectiveSchemeMor, IsoType1<:ProjectiveSchemeMor, IsoType2<:ProjectiveSchemeMor} + (X, Y) = patches(G) + (U, V) = gluing_domains(G) + @vprint :Gluing 1 "computing projective gluing\n" + @vprint :Gluing 2 "$(X), coordinates $(ambient_coordinates(X))\n" + @vprint :Gluing 2 "and\n" + @vprint :Gluing 2 "$(Y) coordinates $(ambient_coordinates(X))\n" + (fb, gb) = gluing_morphisms(G) + (PX, QY) = (codomain(incP), codomain(incQ)) + (PU, QV) = (domain(incP), domain(incQ)) + (base_scheme(PX) === X && base_scheme(QY) === Y) || error("base gluing is incompatible with the projective schemes") + domain(f) === codomain(g) === PU && domain(g) === codomain(f) === QV || error("maps are not compatible") + SPU = homogeneous_coordinate_ring(domain(f)) + SQV = homogeneous_coordinate_ring(codomain(f)) + @check begin + # check the commutativity of the pullbacks + all(y->(pullback(f)(SQV(OO(V)(y))) == SPU(pullback(fb)(OO(V)(y)))), gens(base_ring(OO(Y)))) || error("maps do not commute") + all(x->(pullback(g)(SPU(OO(U)(x))) == SQV(pullback(gb)(OO(U)(x)))), gens(base_ring(OO(X)))) || error("maps do not commute") + fc = map_on_affine_cones(f, check=false) + gc = map_on_affine_cones(g, check=false) + idCPU = compose(fc, gc) + idCPU == identity_map(domain(fc)) || error("composition of maps is not the identity") + idCQV = compose(gc, fc) + idCQV == identity_map(domain(gc)) || error("composition of maps is not the identity") + # idPU = compose(f, g) + # all(t->(pullback(idPU)(t) == t), gens(SPU)) || error("composition of maps is not the identity") + # idQV = compose(g, f) + # all(t->(pullback(idQV)(t) == t), gens(SQV)) || error("composition of maps is not the identity") + end + @vprint :Gluing 1 "done computing the projective gluing\n" + return new{GluingType, IsoType1, IncType1, IsoType2, IncType2}(G, incP, incQ, f, g) + end +end + +### Proper schemes π : Z → X over a covered base scheme X +# +# When {Uᵢ} is an affine covering of X, the datum stored +# consists of a list of projective schemes +# +# Zᵢ ⊂ ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) → Uᵢ +# +# with varying ambient spaces ℙʳ⁽ⁱ⁾(𝒪(Uᵢ)) and a list of +# identifications (transitions) +# +# Zᵢ ∩ π⁻¹(Uⱼ) ≅ Zⱼ ∩ π⁻¹(Uᵢ) +# +# of projective schemes over Uᵢ∩ Uⱼ for all pairs (i,j). +# +# These structs are designed to accommodate blowups of +# covered schemes along arbitrary centers, as well as +# projective bundles. + +@attributes mutable struct CoveredProjectiveScheme{BRT} <: Scheme{BRT} + Y::AbsCoveredScheme # the base scheme + BC::Covering # the reference covering of the base scheme + patches::IdDict{AbsAffineScheme, AbsProjectiveScheme} # the projective spaces over the affine patches in the base covering + gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing} # the transitions sitting over the affine patches in the gluing domains of the base scheme + + function CoveredProjectiveScheme( + Y::AbsCoveredScheme, + C::Covering, + projective_patches::IdDict{AbsAffineScheme, AbsProjectiveScheme}, + projective_gluings::IdDict{Tuple{AbsAffineScheme, AbsAffineScheme}, AbsProjectiveGluing}; + check::Bool=true + ) + C in coverings(Y) || error("covering not listed") + for P in values(projective_patches) + any(x->x===base_scheme(P), patches(C)) || error("base scheme not found in covering") + end + for (U, V) in keys(gluings(C)) + (U, V) in keys(projective_gluings) || error("not all projective gluings were provided") + end + return new{base_ring_type(Y)}(Y, C, projective_patches, projective_gluings) + end +end + + diff --git a/src/AlgebraicGeometry/Schemes/main.jl b/src/AlgebraicGeometry/Schemes/main.jl index 4777e8b57e97..e1cb678a1fee 100644 --- a/src/AlgebraicGeometry/Schemes/main.jl +++ b/src/AlgebraicGeometry/Schemes/main.jl @@ -191,3 +191,11 @@ include("Divisors/AlgebraicCycles.jl") include("Divisors/WeilDivisor.jl") include("Divisors/CartierDivisor.jl") include("Divisors/base_change.jl") + +######################################################################## +# Blowups +######################################################################## +include("CoveredProjectiveScheme/Types.jl") +include("CoveredProjectiveScheme/CoveredProjectiveScheme.jl") +include("BlowupMorphism/Types.jl") +include("BlowupMorphism/BlowupMorphism.jl") diff --git a/src/AlgebraicGeometry/Surfaces/EllipticSurface/EllipticSurface.jl b/src/AlgebraicGeometry/Surfaces/EllipticSurface/EllipticSurface.jl new file mode 100644 index 000000000000..d48976ebad50 --- /dev/null +++ b/src/AlgebraicGeometry/Surfaces/EllipticSurface/EllipticSurface.jl @@ -0,0 +1,1360 @@ +################################################################################################################### +# +# Constructors +# +################################################################################################################### + +@doc raw""" + elliptic_surface(generic_fiber::EllipticCurve, + euler_characteristic::Int, + mwl_gens::Vector{<:EllipticCurvePoint}=EllipticCurvePoint[]; + is_basis::Bool=true) + -> EllipticSurface + +Return the relatively minimal elliptic surface with generic fiber ``E/k(t)``. + +This is also known as the Kodaira-Néron model of ``E``. + +Input: +- `generic_fiber` -- an elliptic curve over a function field +- `euler_characteristic` -- the Euler characteristic of the Kodaira-Néron model of ``E``. +- `mwl_gens` -- a vector of rational points of the generic fiber +- `is_basis` -- if set to `false` compute a reduced basis from `mwl_gens` + +# Examples +```jldoctest +julia> Qt, t = polynomial_ring(QQ, :t); + +julia> Qtf = fraction_field(Qt); + +julia> E = elliptic_curve(Qtf, [0,0,0,0,t^5*(t-1)^2]); + +julia> X3 = elliptic_surface(E, 2) +Elliptic surface + over rational field +with generic fiber + -x^3 + y^2 - t^7 + 2*t^6 - t^5 + +``` +""" +function elliptic_surface(generic_fiber::EllipticCurve{BaseField}, + euler_characteristic::Int, + mwl_gens::Vector{<:EllipticCurvePoint}=EllipticCurvePoint[]; + resolution_strategy::Symbol=:iterative, + is_basis::Bool=true) where { + BaseField <: FracFieldElem{<:PolyRingElem{<:FieldElem}}} + @req all(parent(i)==generic_fiber for i in mwl_gens) "not a vector of points on $(generic_fiber)" + S = EllipticSurface(generic_fiber, euler_characteristic, mwl_gens; resolution_strategy) + if is_basis + return S + end + update_mwl_basis!(S, mwl_gens) + return S +end + +@doc raw""" + kodaira_neron_model(E::EllipticCurve) -> EllipticSurface + +Return the Kodaira-Neron model of the elliptic curve `E`. +""" +kodaira_neron_model(E::EllipticCurve) = elliptic_surface(E) + +@doc raw""" + elliptic_surface(g::MPolyRingElem, P::Vector{<:RingElem}) + +Transform a bivariate polynomial `g` of the form `y^2 - Q(x)` with `Q(x)` of +degree at most ``4`` to Weierstrass form, apply Tate's algorithm and +return the corresponding relatively minimal elliptic surface +as well as the coordinate transformation. +""" +function elliptic_surface( + g::MPolyRingElem, P::Vector{<:RingElem}; + minimize::Bool=true, resolution_strategy::Symbol=:iterative + ) + R = parent(g) + (x, y) = gens(R) + P = base_ring(R).(P) + g2, phi2 = transform_to_weierstrass(g, x, y, P); + Y2, phi1 = _elliptic_surface_with_trafo(g2; minimize) + return Y2, phi2 * phi1 +end + +@doc raw""" + fibration_on_weierstrass_model(X::EllipticSurface) + +Return the elliptic fibration ``W \to \mathbb{P}^1`` where ``W`` is the Weierstrass model of ``X``. +""" +function fibration_on_weierstrass_model(X::EllipticSurface) + if !isdefined(X, :fibration_weierstrass_model) + weierstrass_model(X) # trigger caching + end + return X.fibration_weierstrass_model +end + +@doc raw""" + fibration(X::EllipticSurface) + +Return the elliptic fibration ``X \to \mathbb{P}^1``. +""" +function fibration(X::EllipticSurface) + if !isdefined(X, :fibration) + X.fibration = compose(weierstrass_contraction(X), fibration_on_weierstrass_model(X)) + end + return X.fibration +end + +################################################################################################################### +# +# Basic attributes, properties +# +################################################################################################################### + +# Unlocks Scheme functionality +function underlying_scheme(X::EllipticSurface) + S = X + if isdefined(S,:Y) + return S.Y + end + # trigger the computation + weierstrass_contraction(S) + return underlying_scheme(S) +end + +base_ring(X::EllipticSurface) = coefficient_ring(base_ring(base_field(generic_fiber(X)))) + +@doc raw""" + generic_fiber(X::EllipticSurface) -> EllipticCurve + +Return the generic fiber as an elliptic curve. +""" +generic_fiber(X::EllipticSurface) = X.E + +@doc raw""" + weierstrass_chart(X::EllipticSurface) + +Return the Weierstrass chart of ``X`` on its `weierstrass_model`. +""" +weierstrass_chart(X::EllipticSurface) = weierstrass_model(X)[1][1][1] + +@doc raw""" + euler_characteristic(X::EllipticSurface) -> Int + +Return the Euler characteristic ``\chi(\mathcal{O}_X)``. +""" +euler_characteristic(X::EllipticSurface) = X.euler_characteristic + + +######################################################################################################## +# +# Printing +# +######################################################################################################## + +function Base.show(io::IO, X::EllipticSurface) + io = pretty(io) + if is_terse(io) + print(io, "Elliptic surface") + else + E = generic_fiber(X) + print(io, "Elliptic surface with generic fiber ", equation(E)) + end +end + +function Base.show(io::IO, ::MIME"text/plain", X::EllipticSurface) + io = pretty(io) + println(io, "Elliptic surface") + println(io, Indent(), "over ", Lowercase(), base_ring(X)) + println(io, Dedent(), "with generic fiber") + print(io, Indent(), Lowercase(), equation(generic_fiber(X)), Dedent()) + if isdefined(X, :Y) + println(io) + println(io, "and relatively minimal model") + print(io, Indent(), Lowercase(), X.Y, Dedent()) + end + print(io, Dedent()) +end + +######################################################################################################## +# +# Updating the Mordell-Weil generators. +# +######################################################################################################## +@doc raw""" + set_mordell_weil_basis!(X::EllipticSurface, mwl_basis::Vector{EllipticCurvePoint}) + +Set a basis for the Mordell-Weil sublattice of ``X`` or at least of a sublattice. + +This invalidates previous computations depending on the generators of the +Mordell Weil lattice such as the `algebraic_lattice`. Use with care. + +The points in `mwl_basis` must be linearly independent. +""" +function set_mordell_weil_basis!(X::EllipticSurface, mwl_basis::Vector{<:EllipticCurvePoint}) + @req all(parent(P) == generic_fiber(X) for P in mwl_basis) "points must lie on the generic fiber" + X.MWL = mwl_basis + # clear old computations + if has_attribute(X, :algebraic_lattice) + delete!(X.__attrs, :algebraic_lattice) + end + if has_attribute(X, :mordell_weil_sublattice) + delete!(X.__attrs, :mordell_weil_sublattice) + end +end + +@doc raw""" + _compute_mwl_basis(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) -> ZZLat, Vector{<:EllipticCurvePoint} + +Return a tuple `(M, B)` where `B` is an LLL-reduced basis of the sublattice `M` of the +Mordell-Weil lattice of ``X`` generated by `mwl_gens`. +""" +function _compute_mwl_basis(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) + # it would be good to have the height pairing implemented + basis,tors, SX = _algebraic_lattice(X, mwl_gens) + basisTriv, GTriv = trivial_lattice(X) + r = length(basisTriv) + l = length(mwl_gens) + V = ambient_space(SX) + rk = rank(V) + G = ZZ.(gram_matrix(V)) + # project away from the trivial lattice + pr_mwl = orthogonal_projection(V,basis_matrix(SX)[1:r, :]) + BMWL = pr_mwl.matrix[r+1:end, :] + GB = gram_matrix(V,BMWL) + @assert rank(GB) == rk-r + _, u = hnf_with_transform(ZZ.(denominator(GB) * GB)) + B = u[1:rk-r,:] * BMWL + + MWL = lll(lattice(V, B, isbasis=false)) + u = solve(BMWL, basis_matrix(MWL); side=:left) + u = ZZ.(u) + mwl_basis = [sum(u[i,j] * mwl_gens[j] for j in 1:length(mwl_gens)) for i in 1:nrows(u)] + return MWL, mwl_basis +end + +@doc raw""" + update_mwl_basis!(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) + +Compute a reduced basis of the sublattice of the Mordell-Weil lattice spanned +by `mwl_gens` and set these as the new generators of the Mordell-Weil lattice of +``X``. +""" +function update_mwl_basis!(X::EllipticSurface, mwl_gens::Vector{<:EllipticCurvePoint}) + mwl, mwl_basis = _compute_mwl_basis(X, mwl_gens) + set_mordell_weil_basis!(X, mwl_basis) +end + +@doc raw""" + algebraic_lattice_primitive_closure(X::EllipticSurface, p) -> Vector{<:EllipticCurvePoint} + +Return sections ``P_1,\dots P_n`` of the generic fiber, such that together with +the generators of the algebraic lattice ``A``, they generate +```math +\frac{1}{p} A \cap N +``` +where ``N`` is the numerical lattice of ``X``. + +The algorithm proceeds by computing division points in the Mordell-Weil subgroup of `X` +and using information coming from the discriminant group of the algebraic lattice +to do so. +""" +algebraic_lattice_primitive_closure(X::EllipticSurface, p) = algebraic_lattice_primitive_closure(X, ZZ(p)) + +function algebraic_lattice_primitive_closure(X::EllipticSurface, p::ZZRingElem) + S = X + L = algebraic_lattice(S)[3] + @req is_even(L) "not implemented" + Ld = intersect(dual(L) , (1//p * L)) + D = torsion_quadratic_module(Ld, L, modulus = 1, modulus_qf=2) + candidates = [x for x in D if !iszero(x) && iszero(quadratic_product(x))] + t = length(trivial_lattice(S)[1]) + r = rank(L) + cc = [(x->mod(x,p)).(p*lift(c)[t+1:end]) for c in candidates] + unique!(cc) + cc = [c for c in cc if !iszero(c)] + pts = [division_points(sum(v[i]*S.MWL[i] for i in 1:(r-t)),p) for v in cc] + return [i[1] for i in pts if length(i)>0] +end + +function algebraic_lattice_primitive_closure!(X::EllipticSurface, prime) + pts = algebraic_lattice_primitive_closure(X, prime) + update_mwl_basis!(X, vcat(pts, X.MWL)) + return pts +end + +@doc raw""" + algebraic_lattice_primitive_closure!(X::EllipticSurface) + +Compute the primitive closure of the algebraic lattice of ``X`` inside its +numerical lattice and update the generators of its Mordell--Weil group accordingly. + +The algorithm works by computing suitable divison points in its Mordell Weil group. +""" +function algebraic_lattice_primitive_closure!(X::EllipticSurface) + S = X + L = algebraic_lattice(S)[3] + for p in prime_divisors(ZZ(det(L))) + while true + pts = algebraic_lattice_primitive_closure!(S, p) + if length(pts)==0 + break + end + end + end + return S +end + + +################################################################################################################### +# +# Kodaira-Néron Model +# +################################################################################################################### + + + +@doc raw""" + weierstrass_chart_on_minimal_model(X::EllipticSurface) + +Return an affine chart ``U`` of ``X`` which is isomorphic to the `weierstrass_chart` +of ``X`` on its `weierstrass_model`, but with all singular fibers removed. + +More precisely, the affine coordinates of ``U`` are ``(x,y,t)`` and the chart is +constructed as the vanishing locus of +```math +y^2 + a_1(t) xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6 +``` +minus the reducible singular fibers. +""" +weierstrass_chart_on_minimal_model(X::EllipticSurface) = X[1][1] + +@doc raw""" + weierstrass_model(X::EllipticSurface) -> CoveredScheme, CoveredClosedEmbedding + +Return the Weierstrass model ``W`` of ``X`` and the inclusion in +its ambient projective bundle +```math +S\subseteq \mathbb{P}( \mathcal{O}_{\mathbb{P}^1}(-2s) \oplus \mathcal{O}_{\mathbb{P}^1}(-3s) \oplus \mathcal{O}_{\mathbb{P}^1}). +``` +""" +function weierstrass_model(X::EllipticSurface) + if isdefined(X, :Weierstrassmodel) + return X.Weierstrassmodel, X.inc_Weierstrass + end + + s = euler_characteristic(X) + E = generic_fiber(X) + + kt = base_ring(base_field(E)) + k = coefficient_ring(kt) + + IP1 = projective_space(k, 1) + c = standard_covering(IP1) + # rename the variables on the affine charts + # to a more readable version + if k isa FqField + OO(c[1]).data.S = [:t] + OO(c[2]).data.S = [:s] + else + OO(c[1]).S = [:t] + OO(c[2]).S = [:s] + end + + O0 = twisting_sheaf(IP1, 0) + O4 = twisting_sheaf(IP1, -2*s) + O6 = twisting_sheaf(IP1, -3*s) + + bundleE = direct_sum([O0, O4, O6]) + + P_proj = projectivization(bundleE, var_names=[:z, :x, :y]) + P = covered_scheme(P_proj) + pr = covered_projection_to_base(P_proj) + @assert has_decomposition_info(default_covering(P)) + + # Create the singular Weierstrass model S of the elliptic K3 surface X + a = a_invariants(E) + U = affine_charts(P)[1] # the standard Weierstrass chart + (x, y, t) = gens(OO(U)) + @assert all(denominator(i)==1 for i in a) + a = [numerator(a)(t) for a in a] + (a1,a2,a3,a4,a6) = a + ft = y^2 + a1*x*y + a3*y - (x^3 + a2*x^2 + a4*x+a6) + I = IdealSheaf(P, U, [ft]; check=false) + + inc_S = CoveredClosedEmbedding(P, I) + Scov = domain(inc_S) # The ADE singular elliptic K3 surface + X.Weierstrasschart = Scov[1][1] + X.fibration_weierstrass_model = compose(inc_S, pr) + + X.Weierstrassmodel = Scov + X.inc_Weierstrass = inc_S + + set_attribute!(Scov, :is_irreducible=>true) + set_attribute!(Scov, :is_reduced=>true) + set_attribute!(Scov, :is_integral=>true) + set_attribute!(Scov, :is_equidimensional=>true) + return Scov, inc_S +end + +@doc raw""" + weierstrass_contraction(X::EllipticSurface) -> SchemeMor + +Return the contraction morphism of ``X`` to its Weierstrass model. + +This triggers the computation of the `underlying_scheme` of ``X`` +as a blowup from its Weierstrass model. It may take a few minutes. +""" +function weierstrass_contraction(X::EllipticSurface) + algorithm = X.resolution_strategy + if algorithm == :iterative + return weierstrass_contraction_iterative(X) + elseif algorithm == :simultaneous + return weierstrass_contraction_simultaneous(X) + else + error("algorithm not recognized") + end +end + +@doc raw""" + _separate_singularities!(X::EllipticSurface) -> Covering + +Create a covering of the ambient projective bundle ``P`` +of the Weierstrass model ``W`` of ``X`` such that each chart +(of ``X``) contains at most one singular point of ``W``. +Append this covering to the list of coverings of ``X`` and return it. +""" +function _separate_singularities!(X::EllipticSurface) + S, inc_S = weierstrass_model(X) + P = codomain(inc_S) + + I_sing = ideal_sheaf_of_singular_locus(S) + I_sing_P = SimplifiedIdealSheaf(pushforward(inc_S)(I_sing)) + + # Refine the covering over the reducible singular fibers + # to make sure that there is only a single singular point in each chart + refined_charts = AbsAffineScheme[] + U = P[1][1] # the weierstrass_chart + IsingU = I_sing_P(U)::MPolyIdeal + if isone(IsingU) + # we want one smooth weierstrass chart + push!(refined_charts, U) + set_attribute!(U, :is_smooth => true) + else + # there is at most one singularity in every fiber + # project the singular locus to an affine chart of P1 + disc = gens(eliminate(IsingU, coordinates(U)[1:2]))[1] + # The t-coordinates of the reducible fibers + redfib = [f[1] for f in factor(disc)] + # One chart with all reducible fibers taken out + UU = PrincipalOpenSubset(U, redfib) + set_attribute!(UU, :is_smooth => true) + push!(refined_charts, UU) + if length(redfib)==1 + # We need to recreate U as a PrincipalOpenSubset of itself here + # in order to maintain the correct tree-structure for refinements. + # In any Covering no patch is allowed to be an ancestor of another. + push!(refined_charts, PrincipalOpenSubset(U, one(OO(U)))) + else + for i in 1:length(redfib) + # We take out all but the i-th singular fiber + r = copy(redfib) + g = r[i] + deleteat!(r, i) + Uref = PrincipalOpenSubset(U, r) + push!(refined_charts, Uref) + end + end + end + + # Create a chart which contains the fiber over s=0 + # and no other reducible singular fibers + # these are visible in the charts that we have already + # i.e. we add the fiber at s=0 and remove all other singular fibers + V = P[1][4] + IsingV = I_sing_P(V) + if isone(IsingV) + push!(refined_charts, V) + else + # reducible singular fibers + disc = gens(eliminate(IsingV, coordinates(V)[1:2]))[1] + (x,y,s) = coordinates(V) + b, d = divides(disc, s) + if b + disc = d + end + redfib = [f[1] for f in factor(disc)] + if length(redfib)> 0 + push!(refined_charts, PrincipalOpenSubset(V, redfib)) + else + push!(refined_charts, V) + end + end + + # no extra singularities in the X = 1 chart + # therefore we just exclude all the singularities visible here + for W in [P[1][2],P[1][5]] + Ising = I_sing_P(W) + if isone(Ising) + push!(refined_charts, W) + continue + end + (z,y,s_or_t) = coordinates(W) + # reducible singular fibers + local disc = gens(eliminate(Ising, [z, s_or_t]))[1] + local redfib = [p for (p,e) in factor(disc)] + push!(refined_charts, PrincipalOpenSubset(W, redfib)) + end + + # no extra singularities on the the zero section + # This is the Y = 1 chart + # therefore we just exclude all the singularities visible here + for W in [P[1][3],P[1][6]] + local Ising = I_sing_P(W) + if isone(Ising) + push!(refined_charts, W) + continue + end + local (z,x,s_or_t) = coordinates(W) + # reducible singular fibers + local disc = gens(eliminate(Ising, [x, s_or_t]))[1] + local redfib = [p for (p,e) in factor(disc)] + push!(refined_charts, PrincipalOpenSubset(W, redfib)) + end + + + Cref = Covering(refined_charts) + inherit_gluings!(Cref, P[1]) + push!(P.coverings, Cref) + @assert has_decomposition_info(default_covering(P)) + inherit_decomposition_info!(P, Cref) + @assert has_decomposition_info(Cref) + # Now we have an extra covering where each chart just contains a single singularity + + @assert scheme(I_sing) === S + @assert scheme(I_sing_P) === P + return Cref +end + + +function weierstrass_contraction_simultaneous(Y::EllipticSurface) + if isdefined(Y, :blowup) + return Y.blowup + end + S, inc_S = weierstrass_model(Y) + @assert has_attribute(S, :is_equidimensional) && get_attribute(S, :is_equidimensional) === true + + X0 = codomain(inc_S) + Y0 = S + set_attribute!(Y0, :is_reduced=>true) + set_attribute!(Y0, :is_irreducible=>true) + set_attribute!(Y0, :is_equidimensional=>true) + inc_Y0 = inc_S + I_sing_Y0 = AbsIdealSheaf[ideal_sheaf_of_singular_locus(Y0)] + #I_sing_Y0 = AbsIdealSheaf[simplify(ideal_sheaf_of_singular_locus(Y0))] + I_sing_X0 = simplify.(pushforward(inc_Y0).(I_sing_Y0)) + + # Prepare a covering which has a permanent weierstrass chart + U0 = X0[1][1] + disc = numerator(discriminant(generic_fiber(Y))) + U = PrincipalOpenSubset(U0, evaluate(disc, gens(OO(U0))[3])) + _find_chart(U, default_covering(X0)) + Cref = Covering(vcat([U], affine_charts(X0))) + inherit_gluings!(Cref, X0[1]) + push!(X0.coverings, Cref) + @assert has_decomposition_info(default_covering(X0)) + inherit_decomposition_info!(X0, Cref) + @assert has_decomposition_info(Cref) + + ambient_exceptionals = EffectiveCartierDivisor[] + varnames = [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o,:p,:q,:r,:u,:v,:w] + projectionsX = BlowupMorphism[] + projectionsY = AbsCoveredSchemeMorphism[] + count = 0 + + @vprint :EllipticSurface 2 "Blowing up Weierstrass model simultaneously in all singular points\n" + while true + count = count+1 + @vprint :EllipticSurface 1 "blowup number: $(count)\n" + @vprint :EllipticSurface 1 "number of ideal sheaves to be blown up: $(length(I_sing_X0))\n" + if length(I_sing_X0)==0 + # stop if smooth + break + end + # make sure we have a Weierstrass chart kept. + if count == 1 + cov = Cref + else + cov = simplified_covering(X0) + end + # take the first ideal sheaf and blow it up + J = SimplifiedIdealSheaf(I_sing_X0[1]) + pr_X1 = blow_up(J, covering=cov, var_name=varnames[1+mod(count, length(varnames))]) + + X1 = domain(pr_X1) + @vprint :EllipticSurface 1 "$(X1)\n" + E1 = exceptional_divisor(pr_X1) + + @vprint :EllipticSurface 2 "computing strict transforms\n" + # compute the exceptional divisors + ambient_exceptionals = EffectiveCartierDivisor[strict_transform(pr_X1, e) for e in ambient_exceptionals] + # move the divisors coming originally from S up to the next chart + push!(ambient_exceptionals, E1) + + Y1, inc_Y1, pr_Y1 = strict_transform(pr_X1, inc_Y0) + # Speed up the computation of singular loci + set_attribute!(Y1, :is_irreducible=> true) + set_attribute!(Y1, :is_reduced=>true) + set_attribute!(Y1, :is_integral=>true) + set_attribute!(Y1, :is_equidimensional=>true) + + # transform the singular loci + I_sing_X0 = AbsIdealSheaf[pullback(pr_X1, J) for J in I_sing_X0[2:end]] + + # Add eventual new components + @vprint :EllipticSurface 2 "computing singular locus\n" + I_sing_new = ideal_sheaf_of_singular_locus(Y1; focus=pullback(inc_Y1, ideal_sheaf(E1))) + #I_sing_new = pushforward(inc_Y1, I_sing_new) + ideal_sheaf(E1) # new components only along the exc. set + I_sing_new = simplify(pushforward(inc_Y1, I_sing_new)) + + @vprint :EllipticSurface 2 "decomposing singular locus\n" + !is_one(I_sing_new) && push!(I_sing_X0, I_sing_new) + + push!(projectionsX, pr_X1) + push!(projectionsY, pr_Y1) + simplify!(Y1) + + # set up for the next iteration + Y0 = Y1 + inc_Y0 = inc_Y1 + X0 = X1 + # Speed up the computation of singular loci + set_attribute!(Y0, :is_irreducible=> true) + set_attribute!(Y0, :is_reduced=>true) + set_attribute!(Y0, :is_integral=>true) + set_attribute!(Y0, :is_equidimensional=>true) + set_attribute!(X0, :is_irreducible=> true) + set_attribute!(X0, :is_reduced=>true) + set_attribute!(X0, :is_integral=>true) + end + Y.Y = Y0 + Y.blowups = projectionsY + + # We need to rewrap the last maps so that the domain is really Y + last_pr = pop!(projectionsY) + last_pr_wrap = CoveredSchemeMorphism(Y, codomain(last_pr), covering_morphism(last_pr)) + + push!(projectionsY, last_pr_wrap) + Y.ambient_blowups = projectionsX + + Y.ambient_exceptionals = ambient_exceptionals + piY = CompositeCoveredSchemeMorphism(reverse(projectionsY)) + Y.blowup = piY + + inc_Y0_wrap = CoveredClosedEmbedding(Y, codomain(inc_Y0), covering_morphism(inc_Y0), check=false) + Y.inc_Y = inc_Y0_wrap + + set_attribute!(Y, :is_irreducible=> true) + set_attribute!(Y, :is_reduced=>true) + set_attribute!(Y, :is_integral=>true) + return piY +end + + +function weierstrass_contraction_iterative(Y::EllipticSurface) + if isdefined(Y, :blowup) + return Y.blowup + end + S, inc_S = weierstrass_model(Y) + @assert has_attribute(S, :is_equidimensional) && get_attribute(S, :is_equidimensional) === true + Crefined = _separate_singularities!(Y) + # Blow up singular points (one at a time) until smooth + # and compute the strict transforms of the `divisors` + # collect the exceptional divisors + # blowup ambient spaces: X0 → X ⊂ + # blowup pi: Y → (S singular weierstrass model) + # + # initialization for the while loop + X0 = codomain(inc_S) + Y0 = S + set_attribute!(Y0, :is_reduced=>true) + set_attribute!(Y0, :is_irreducible=>true) + set_attribute!(Y0, :is_equidimensional=>true) + inc_Y0 = inc_S + I_sing_Y0 = maximal_associated_points(ideal_sheaf_of_singular_locus(Y0))::Vector{<:AbsIdealSheaf} + I_sing_X0 = pushforward(inc_Y0).(I_sing_Y0) + + + ambient_exceptionals = EffectiveCartierDivisor[] + varnames = [:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o,:p,:q,:r,:u,:v,:w] + projectionsX = BlowupMorphism[] + projectionsY = AbsCoveredSchemeMorphism[] + count = 0 + + @vprint :EllipticSurface 2 "Blowing up Weierstrass model\n" + @vprint :EllipticSurface 2 "in $(Crefined)\n" + while true + count = count+1 + @vprint :EllipticSurface 1 "blowup number: $(count)\n" + @vprint :EllipticSurface 1 "number of singular points: $(length(I_sing_X0))\n" + if length(I_sing_X0)==0 + # stop if smooth + break + end + # make sure there is only one singular point per chart + if count == 1 + cov = Crefined + else + # the following leads to difficult bugs + cov = simplified_covering(X0) + end + # take the first singular point and blow it up + J = SimplifiedIdealSheaf(I_sing_X0[1]) + pr_X1 = blow_up(J, covering=cov, var_name=varnames[1+mod(count, length(varnames))]) + + # Set the attribute so that the strict_transform does some extra work + #isomorphism_on_open_subset(pr_X1) + + X1 = domain(pr_X1) + @vprint :EllipticSurface 1 "$(X1)\n" + E1 = exceptional_divisor(pr_X1) + + @vprint :EllipticSurface 2 "computing strict transforms\n" + # compute the exceptional divisors + ambient_exceptionals = EffectiveCartierDivisor[strict_transform(pr_X1, e) for e in ambient_exceptionals] + # move the divisors coming originally from S up to the next chart + push!(ambient_exceptionals, E1) + + Y1, inc_Y1, pr_Y1 = strict_transform(pr_X1, inc_Y0) + # Speed up the computation of singular loci + set_attribute!(Y1, :is_irreducible=> true) + set_attribute!(Y1, :is_reduced=>true) + set_attribute!(Y1, :is_integral=>true) + set_attribute!(Y1, :is_equidimensional=>true) + + # transform the singular loci + I_sing_X0 = AbsIdealSheaf[pullback(pr_X1, J) for J in I_sing_X0[2:end]] + + # Add eventual new components + @vprint :EllipticSurface 2 "computing singular locus\n" + I_sing_new = ideal_sheaf_of_singular_locus(Y1; focus=pullback(inc_Y1, ideal_sheaf(E1))) + #I_sing_new = pushforward(inc_Y1, I_sing_new) + ideal_sheaf(E1) # new components only along the exc. set + I_sing_new = pushforward(inc_Y1, I_sing_new) + + @vprint :EllipticSurface 2 "decomposing singular locus\n" + I_sing_X0 = vcat(I_sing_X0, maximal_associated_points(I_sing_new)) + + push!(projectionsX, pr_X1) + push!(projectionsY, pr_Y1) + simplify!(Y1) + + # set up for the next iteration + Y0 = Y1 + inc_Y0 = inc_Y1 + X0 = X1 + # Speed up the computation of singular loci + set_attribute!(Y0, :is_irreducible=> true) + set_attribute!(Y0, :is_reduced=>true) + set_attribute!(Y0, :is_integral=>true) + set_attribute!(Y0, :is_equidimensional=>true) + set_attribute!(X0, :is_irreducible=> true) + set_attribute!(X0, :is_reduced=>true) + set_attribute!(X0, :is_integral=>true) + end + Y.Y = Y0 + Y.blowups = projectionsY + + # We need to rewrap the last maps so that the domain is really Y + last_pr = pop!(projectionsY) + last_pr_wrap = CoveredSchemeMorphism(Y, codomain(last_pr), covering_morphism(last_pr)) + #set_attribute!(last_pr_wrap, :isomorphism_on_open_subset, get_attribute(last_pr, :isomorphism_on_open_subset)) + + push!(projectionsY, last_pr_wrap) + Y.ambient_blowups = projectionsX + + Y.ambient_exceptionals = ambient_exceptionals + piY = CompositeCoveredSchemeMorphism(reverse(projectionsY)) + Y.blowup = piY + + inc_Y0_wrap = CoveredClosedEmbedding(Y, codomain(inc_Y0), covering_morphism(inc_Y0), check=false) + Y.inc_Y = inc_Y0_wrap + + set_attribute!(Y, :is_irreducible=> true) + set_attribute!(Y, :is_reduced=>true) + set_attribute!(Y, :is_integral=>true) + return piY +end + +################################################################################################################### +# +# Intersection theory, Neron-Severi-lattice, Mordell-Weil lattice +# +################################################################################################################### + +@doc raw""" + algebraic_lattice(X) -> Vector{AbsWeilDivisor}, ZZLat + +Return the sublattice ``L`` of the numerical lattice spanned by fiber components, +torsion sections and the sections provided at the construction of ``X``. + +The first return value is a list ``B`` of vectors corresponding to the standard basis of the ambient space of `L`. +The second consists of generators ``T`` for the torsion part of the Mordell-Weil group. +Together ``B`` and ``L`` generate the algebraic lattice. However, they are linearly dependent. +The third return value is the lattice ``L``. + +!!! warning + The ordering of the fiber components is in general not canonical and may change in between sessions. +""" +@attr Any function algebraic_lattice(X::EllipticSurface) + return _algebraic_lattice(X,X.MWL) +end + +function _algebraic_lattice(X::EllipticSurface, mwl_basis::Vector{<:EllipticCurvePoint}) + basisTriv, GTriv = trivial_lattice(X) + r = length(basisTriv) + l = length(mwl_basis) + sections = [section(X, i) for i in mwl_basis] + n = l+r + GA = zero_matrix(ZZ, n, n) + GA[1:r,1:r] = GTriv + GA[r+1:n,r+1:n] = -euler_characteristic(X)*identity_matrix(ZZ, l) + basisA = vcat(basisTriv, sections) + @vprint :EllipticSurface 2 "computing intersection numbers\n" + for i in 1:n + @vprint :EllipticSurface 3 "\nrow $(i): \n" + for j in max(i + 1, r + 1):n + if i!=2 && i <= r + I = components(basisA[i])[1] + J = components(basisA[j])[1] + if isone(I+J) + ij = 0 + else + ij = 1 + end + else + @vprint :EllipticSurface 4 "$(j) " + ij = intersect(basisA[i],basisA[j]) + end + GA[i,j] = ij + GA[j,i] = GA[i,j] + end + end + GA_QQ = change_base_ring(QQ,GA) + # primitive closure of the trivial lattice comes from torsion sections + tors = [section(X, h) for h in mordell_weil_torsion(X)] + torsV = QQMatrix[] + for T in tors + @vprint :EllipticSurface 2 "computing basis representation of torsion point $(T)\n" + vT = zero_matrix(QQ, 1, n) + for i in 1:r + if i== 2 + vT[1,i] = intersect(basisA[i], T) + else + @assert length(components(basisTriv[i])) == 1 + I = sum(components(basisA[i])) + J = components(T)[1] + if !isone(I+J) + @assert i!=2 # O does not meet any torsion section + vT[1,i] = 1 + end + end + end + for i in r+1:n + vT[1,i] = intersect(T,basisA[i]) + end + push!(torsV, solve(GA_QQ, vT; side=:left)) + end + gen_tors = zip(tors, torsV) + push!(torsV, identity_matrix(QQ,n)) + V = quadratic_space(QQ,GA) + L = lattice(V, reduce(vcat,torsV), isbasis=false) + return basisA, collect(gen_tors), L +end + +@doc raw""" + mordell_weil_sublattice(X::EllipticSurface) -> Vector{EllipticCurvePoint}, ZZLat + +Return the (sublattice) of the Mordell-Weil lattice of ``X`` spanned +by the sections of ``X`` supplied at its construction. + +The Mordell-Weil lattice is represented in the same vector space as the +algebraic lattice (with quadratic form rescaled by ``-1``). +""" +@attr ZZLat function mordell_weil_sublattice(X::EllipticSurface) + S = X + NS = algebraic_lattice(S)[3] + t = length(trivial_lattice(S)[1]) + trivNS = basis_matrix(NS)[1:t,:] + R = basis_matrix(NS)[t+1:end,:] + V = ambient_space(NS) + P = orthogonal_projection(V, trivNS) + mwl = rescale(lattice(V,R*P.matrix),-1) + return mwl +end + +@doc raw""" + mordell_weil_torsion(X::EllipticSurface) -> Vector{EllipticCurvePoint} + +Return the torsion part of the Mordell-Weil group of the generic fiber of ``X``. +""" +@attr Vector{EllipticCurvePoint} function mordell_weil_torsion(X::EllipticSurface) + S = X + E = generic_fiber(S) + O = E([0,1,0]) + N = trivial_lattice(S)[2] + tors = EllipticCurvePoint[] + d = det(N) + for p in prime_divisors(d) + if valuation(d, p) == 1 + continue + end + r = 1 + i = 0 + dp = typeof(O)[] + while true + i = i+1 + @vprint :EllipticSurface 2 "computing $(p^i)-torsion" + dp = division_points(O, p^i) + if length(dp) == r + break + end + r = length(dp) + end + for pt in dp + if pt != O + push!(tors, pt) + end + end + end + return tors +end + +@doc raw""" + section(X::EllipticSurface, P::EllipticCurvePoint) -> EllipticSurfaceSection + +Given a rational point ``P\in E(C)`` of the generic fiber ``E/C`` of ``\pi\colon X \to C``, +return its closure in ``X`` as a `AbsWeilDivisor`. +""" +function section(X::EllipticSurface, P::EllipticCurvePoint) + if iszero(P[1])&&iszero(P[3]) + return zero_section(X) + end + return EllipticSurfaceSection(X, P) +end + + +function _section_on_weierstrass_ambient_space(X::EllipticSurface, P::EllipticCurvePoint) + S0,incS0 = weierstrass_model(X) + X0 = codomain(incS0) + if P[3] == 0 + # zero section + V = X0[1][3] + (z,x,t) = coordinates(V) + return IdealSheaf(X0, V, [x,z]) + end + U = X0[1][1] + (x,y,t) = coordinates(U) + b = P + return ideal_sheaf(X0,U,[OO(U)(i) for i in [x*denominator(b[1])(t)-numerator(b[1])(t),y*denominator(b[2])(t)-numerator(b[2])(t)]]; check=false) +end + + +@doc raw""" + zero_section(X::EllipticSurface) -> AbsWeilDivisor + +Return the zero section of the relatively minimal elliptic +fibration ``\pi\colon X \to C``. +""" +@attr EllipticSurfaceSection zero_section(X::EllipticSurface) = EllipticSurfaceSection(X, generic_fiber(X)([0,1,0])) + + +@doc raw""" + basis_representation(X::EllipticSurface, D::AbsWeilDivisor) + +Return the vector representing the numerical class of ``D`` +with respect to the basis of the ambient space of `algebraic_lattice(X)`. +""" +function basis_representation(X::EllipticSurface, D::AbsWeilDivisor) + basis_ambient,_, NS = algebraic_lattice(X) + G = gram_matrix(ambient_space(NS)) + n = length(basis_ambient) + v = zeros(ZZRingElem, n) + @vprint :EllipticSurface 3 "computing basis representation of $D\n" + kk = base_ring(X) + if iszero(characteristic(kk)) && has_attribute(X, :good_reduction_map) + X_red_raw, bc = raw_good_reduction(X) + red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) + D_red = _reduce_as_prime_divisor(bc, D) + for (i, E) in enumerate(basis_ambient) + @vprintln :EllipticSurface 4 "intersecting in positive characteristic with $(i): $(basis_ambient[i])" + v[i] = intersect(red_dict[E], D_red) + end + else + for i in 1:n + @vprintln :EllipticSurface 4 "intersecting with $(i): $(basis_ambient[i])" + + v[i] = intersect(basis_ambient[i], D) + end + end + @vprint :EllipticSurface 3 "done computing basis representation\n" + return v*inv(G) +end + + +################################################################################################################### +# +# Fibers and trivial lattice +# +################################################################################################################### + +@doc raw""" + trivial_lattice(X::EllipticSurface) -> Vector{AbsWeilDivisor}, ZZMatrix + +Return a basis for the trivial lattice as well as its Gram matrix. + +The trivial lattice is the sublattice of the numerical lattice spanned by fiber components and +the zero section of ``X``. + +!!! warning + The ordering of the basis is in general not canonical and may change in between sessions. +""" +function trivial_lattice(X::EllipticSurface) + T = _trivial_lattice(X)[1:2] + return T +end + +@doc raw""" + _trivial_lattice(X::EllipticSurface; reducible_singular_fibers_in_PP1=_reducible_fibers_disc(S)) + +Internal function. Returns a list consisting of: +- basis of the trivial lattice +- gram matrix +- fiber_components without multiplicities + +The keyword argument `reducible_singular_fibers_in_PP1` must be a list of vectors of length `2` over +the base field representing the points in projective space over which there are reducible fibers. +Specify it to force this ordering of the basis vectors of the ambient space of the `algebraic_lattice` +""" +function _trivial_lattice(X::EllipticSurface; reducible_singular_fibers_in_PP1=_reducible_fibers_disc(X)) + S = X + get_attribute!(S, :_trivial_lattice) do + O = zero_section(S) + pt0, F = fiber(S) + set_attribute!(components(O)[1], :_self_intersection, -euler_characteristic(S)) + basisT = [F, O] + grams = [ZZ[0 1;1 -euler_characteristic(S)]] + sing = reducible_singular_fibers_in_PP1 + f = [[pt, fiber_components(S,pt)] for pt in sing] + fiber_componentsS = [] + for (pt, ft) in f + @vprint :EllipticSurface 2 "normalizing fiber: over $pt \n" + Ft0 = standardize_fiber(S, ft) + @vprint :EllipticSurface 2 "$(Ft0[1]) \n" + append!(basisT , Ft0[3][2:end]) + push!(grams,Ft0[4][2:end,2:end]) + push!(fiber_componentsS, vcat([pt], collect(Ft0))) + end + G = block_diagonal_matrix(grams) + # make way for some more pretty printing + for (pt,root_type,_,comp) in fiber_componentsS + for (i,I) in enumerate(comp) + name = string(root_type[1], root_type[2]) + set_attribute!(components(I)[1], :name, string("Component ", name, "_", i-1," of fiber over ", Tuple(pt))) + set_attribute!(components(I)[1], :_self_intersection, -2) + end + end + return basisT, G, fiber_componentsS + end +end + +function _reducible_fibers_disc(X::EllipticSurface; sort::Bool=true) + E = generic_fiber(X) + j = j_invariant(E) + d = numerator(discriminant(E)) + kt = parent(d) + k = coefficient_ring(kt) + sing = Vector{elem_type(k)}[] + for (p,v) in factor(d) + if v == 1 + continue + end + r = k.(roots(p)) + if length(r) == 0 + error("not all reducible fibers are visible over $(base_ring(S))") + end + @assert length(r) ==1 + rt = r[1] + if v == 2 + # not a type II fiber + if j!=0 + push!(sing, [rt,k(1)]) + end + end + if v > 2 + push!(sing, [rt,k(1)]) + end + end + if sort + sort!(sing, by=x->by_total_order(x[1])) + end + # fiber over infinity is always last (if it is there) + if degree(d) <= 12*euler_characteristic(X) - 2 + push!(sing, k.([1, 0])) + end + return sing +end + +function by_total_order(x::FqFieldElem) + return [lift(ZZ, i) for i in absolute_coordinates(x)] +end + +by_total_order(x::QQFieldElem) = x + +function by_total_order(x::NumFieldElem) + return absolute_coordinates(x) +end + + +@doc raw""" + reducible_fibers(X::EllipticSurface) + +Return the reducible fibers of ``X``. + +The output format is the following: +A list `[F1, ..., Fn]` where each entry `Fi` represents a reducible fiber. + +The list ``F`` has the following entries: +- A point ``P \in \mathbb{P}^{1}`` such that ``F = \pi^{-1}(P)``; +- The ADE-type of the fiber; +- The fiber ``F`` as a Weil divisor, including its multiplicities; +- The irreducible components of the fiber. The first component intersects the zero section; +- Their intersection matrix. +""" +function reducible_fibers(X::EllipticSurface) + return _trivial_lattice(X)[3] +end + + +@doc raw""" + standardize_fiber(X::EllipticSurface, f::Vector{<:AbsWeilDivisor}) + +Internal method. Used to prepare for [`reducible_fibers`](@ref). +`f` must be the list of the components of the reducible fiber `F`. +Output a list of tuples with each tuple as follows +- the root type of ``F``, e.g. `(:A, 3)` +- the class of ``F`` as a divisor with the appropriate multiplicities +- the irreducible components `[F0,...Fn]` of `F` sorted such that the first entry `F0` is the one intersecting the zero section. The others are sorted in some standard way +- gram matrix of the intersection of [F0,...,Fn], it is an extended ADE-lattice. +""" +function standardize_fiber(X::EllipticSurface, f::Vector{<:AbsWeilDivisor}) + S = X + @hassert :EllipticSurface 2 all(is_prime(i) for i in f) + f = copy(f) + O = components(zero_section(S))[1] + local f0 + for (i,D) in enumerate(f) + if !isone(O+components(D)[1]) + f0 = D + deleteat!(f,i) + break + end + end + r = length(f) + G = -2*identity_matrix(ZZ, r) + @vprintln :EllipticSurface 2 "computing intersections:" + for i in 1:r + @vprint :EllipticSurface 3 "\nrow $(i): \n" + for j in 1:i-1 + @vprint :EllipticSurface 4 "$(j) " + # we know the intersections are 0 or 1, so we can replace the line below by a shortcut. + # G[i, j] = G[j, i] = intersect(f[i], f[j]) + # In the examples treated, this led to roughly a factor 3 in speed and memory consumption. + if isone(components(f[i])[1]+components(f[j])[1]) + G[i,j] = 0 + else + G[i,j] = 1 + end + G[j,i] = G[i,j] + end + end + L = integer_lattice(gram=G) + rt,_ = root_lattice_recognition(L) + @assert length(rt)==1 + rt = rt[1] + R = root_lattice(rt[1], rt[2]) + b, I = _is_equal_up_to_permutation_with_permutation(G, -gram_matrix(R)) + @assert b + gensF = vcat([f0], f[I]) + Gext, v = extended_ade(rt[1],rt[2]) + Fdiv = sum(v[i]*gensF[i] for i in 1:length(gensF)) + return rt, Fdiv, gensF, Gext +end + +@doc raw""" + fiber_cartier(X::EllipticSurface, P::Vector = ZZ.([0,1])) -> EffectiveCartierDivisor + +Return the fiber of ``\pi\colon X \to C`` over ``P\in C`` as a Cartier divisor. +""" +function fiber_cartier(X::EllipticSurface, P::Vector = ZZ.([0,1])) + S = X + S0,_ = weierstrass_model(S) + underlying_scheme(S) # cache stuff + D = IdDict{AbsAffineScheme, RingElem}() + k = base_ring(S0) + P = k.(P) + + if P[1]!=0 && P[2]!=0 + t0 = P[1] * inv(P[2]) + for i in 1:3 + U = S0[1][i] + (_,_,t) = coordinates(U) + D[U] = t-t0 + end + s0 = inv(t0) + for i in 4:6 + U = S0[1][i] + (_,_,s) = coordinates(U) + D[U] = s - s0 + end + elseif P[1] != 0 + # it is the fiber at [1:0] + for i in 4:6 + U = S0[1][i] + (_,_,s) = coordinates(U) + D[U] = s + end + for i in 1:3 + U = S0[1][i] + (_,_,t) = coordinates(U) + D[U] = one(parent(t)) + end + elseif P[2] != 0 + # the fiber at [0:1] + for i in 1:3 + U = S0[1][i] + (_,_,t) = coordinates(U) + D[U] = t + end + for i in 4:6 + U = S0[1][i] + (_,_,s) = coordinates(U) + D[U] = one(parent(s)) + end + else + error("[0,0] is not a point in projective space") + end + F = EffectiveCartierDivisor(S0, D, trivializing_covering=S0[1], check=false) + return pullback(S.blowup)(F) +end + +@doc raw""" + fiber_components(X::EllipticSurface, P) -> Vector{<:AbsWeilDivisor} + +Return the fiber components of the fiber over the point ``P \in C``. +""" +function fiber_components(X::EllipticSurface, P; algorithm=:exceptional_divisors) + S = X + @vprint :EllipticSurface 2 "computing fiber components over $(P)\n" + P = base_ring(S).(P) + W = codomain(S.inc_Weierstrass) + Fcart = fiber_cartier(S, P) + if isone(P[2]) + U = default_covering(W)[1] + (x,y,t) = coordinates(U) + F = PrimeIdealSheafFromChart(W, U, ideal(t - P[1])) + elseif isone(P[1]) + U = default_covering(W)[4] + (x,y,s) = coordinates(U) + F = PrimeIdealSheafFromChart(W, U, ideal(s - P[2])) + end + FF = ideal_sheaf(Fcart) + EE = exceptional_divisors(S) + EP = filter(E->issubset(FF, E), EE) + for bl in S.ambient_blowups + F = strict_transform(bl, F) + end + F = pullback(S.inc_Y, F) + F = weil_divisor(F, ZZ) + fiber_components = [weil_divisor(E, ZZ; check=false) for E in EP] + push!(fiber_components, F) + return fiber_components +end + +@attr Vector{<:AbsIdealSheaf} function exceptional_divisors(X::EllipticSurface) + S = X + PP = AbsIdealSheaf[] + @vprintln :EllipticSurface 2 "computing exceptional divisors" + # If we have resolution_strategy=:simultaneous, then the following is a non-trivial preprocessing step. + ambient_pts = reduce(vcat, maximal_associated_points.(ideal_sheaf.(S.ambient_exceptionals))) + for I in ambient_pts + @vprintln :EllipticSurface 4 "decomposing divisor " + mp = maximal_associated_points(pullback(S.inc_Y, I); use_decomposition_info=true) + append!(PP, mp) + end + @vprintln :EllipticSurface 3 "done" + return PP +end + +function fiber(X::EllipticSurface) + b, pt, F = irreducible_fiber(X) + if b + W = weil_divisor(F) + # we manually set the self-intersection + set_attribute!(components(W)[1], :_self_intersection, 0) + else + # all fibers are reducible pick the one over [0 : 1] + k = base_ring(X) + pt = [k(0),k(1)] + f = fiber_components(X, pt) + fiber_type, W, componentsW, gramW = standardize_fiber(X, f) + end + set_attribute!(W, :name=> "Fiber over ($(pt[1]), $(pt[2]))") + return pt, W +end + + +@doc raw""" + irreducible_fiber(X::EllipticSurface) -> Bool, Point, EffectiveCartierDivisor + +Return an irreducible fiber as a cartier divisor and whether it exists. + +The return value is a triple `(b, pt, F)` where + +- `b` is `true` if an irreducible fiber exists over the base field of `S` +- `pt` the base point of the fiber +- `F` the irreducible fiber which projects to `pt` +""" +function irreducible_fiber(X::EllipticSurface) + S = X + W = weierstrass_model(S) + d = numerator(discriminant(generic_fiber(S))) + kt = parent(d) + k = coefficient_ring(kt) + r = [k.(roots(i[1])) for i in factor(d) if i[2]>=2] + sing = reduce(append!,r, init=[]) + pt = k.([0,0]) # initialize + found = false + if is_finite(k) + for i in k + if !(i in sing) # true if the fiber over [i,1] is irreducible + pt = k.([i,1]) + found = true + break + end + end + if !found && (degree(d) >= 12*euler_characteristic(S) - 1) # irreducible at infinity? + pt = k.([1, 0]) + found = true + end + else + i = k(0) + while true + i = i+1 + if !(i in sing) + pt = k.([i,1]) + found = true + break + end + end + end + F = fiber_cartier(S, pt) + return found, pt, F +end diff --git a/src/AlgebraicGeometry/Surfaces/EllipticSurface/Morphisms.jl b/src/AlgebraicGeometry/Surfaces/EllipticSurface/Morphisms.jl new file mode 100644 index 000000000000..ba8f47810feb --- /dev/null +++ b/src/AlgebraicGeometry/Surfaces/EllipticSurface/Morphisms.jl @@ -0,0 +1,956 @@ +######################################################################## +# Reduction to positive characteristic +# +# We allow to store a reduction of an elliptic surface `X` to positive +# characteristic. The user needs to know what they're doing here! +# +# The functionality can be made available by specifying a reduction +# map for the `base_ring` (actually a field) of `X` to a field of +# positive characteristic. This can then be stored in `X` via +# `set_good_reduction_map!`. The latter unlocks certain features such +# as computation of intersection numbers in positive characteristic. +######################################################################## +function set_good_reduction_map!(X::EllipticSurface, red_map::Map) + has_attribute(X, :good_reduction_map) && error("reduction map has already been set") + kk0 = base_ring(X) + @assert domain(red_map) === kk0 + kkp = codomain(red_map) + @assert characteristic(kkp) > 0 + set_attribute!(X, :good_reduction_map=>red_map) +end + +function get_good_reduction_map(X::EllipticSurface) + is_zero(characteristic(base_ring(X))) || error("reduction to positive characteristic is only possible from characteristic zero") + has_attribute(X, :good_reduction_map) || error("no reduction map is available; please set it manually via `set_good_reduction_map!`") + return get_attribute(X, :good_reduction_map)::Map +end + +@attr Tuple{<:AbsCoveredScheme, <:AbsCoveredSchemeMorphism} function raw_good_reduction(X::EllipticSurface) + red_map = get_good_reduction_map(X) + X_red, bc_map = base_change(red_map, X) + set_attribute!(X_red, :is_irreducible=>true) + set_attribute!(X_red, :is_reduced=>true) + set_attribute!(X_red, :is_integral=>true) + set_attribute!(X_red, :is_equidimensional=>true) + return X_red, bc_map +end + +@attr Map function good_reduction_function_fields(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E = generic_fiber(X) + Ft = base_field(E) + Pt = base_ring(Ft) + kk = coefficient_ring(Pt) + kk_red = codomain(red_map) + Pt_red, _ = polynomial_ring(kk_red, first(symbols(Pt)); cached=false) + Ft_red = fraction_field(Pt_red) + Ft_to_Ft_red = map_from_func(fr->Ft_red(map_coefficients(red_map, numerator(fr); parent=Pt_red), map_coefficients(red_map, denominator(fr); parent=Pt_red)), Ft, Ft_red) + return Ft_to_Ft_red +end + +@attr EllipticCurve function good_reduction_generic_fiber(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E = generic_fiber(X) + Ft_to_Ft_red = good_reduction_function_fields(X) + E_red = base_change(Ft_to_Ft_red, E) + return E_red +end + +@attr Vector{<:EllipticCurvePoint} function good_reduction_rational_points(X::EllipticSurface) + red_map = good_reduction_function_fields(X) + result = Vector{EllipticCurvePoint}() + E_red = good_reduction_generic_fiber(X) + for P in X.MWL # TODO: Do we have a getter for this? + if is_infinite(P) + push!(result, infinity(E_red)) + continue + end + push!(result, E_red([red_map(P[1]), red_map(P[2])])) + end + return result +end + +@attr EllipticSurface function good_reduction(X::EllipticSurface) + red_map = get_good_reduction_map(X) + E_red = good_reduction_generic_fiber(X) + mwl_red = good_reduction_rational_points(X) + X_red = EllipticSurface(E_red, 2, mwl_red; resolution_strategy=X.resolution_strategy) +end + +@attr Tuple{<:MorphismFromRationalFunctions, <:MorphismFromRationalFunctions} function identifications_with_raw_good_reduction(X::EllipticSurface) + X_red = good_reduction(X) + W_red = weierstrass_chart_on_minimal_model(X_red) + X_red_raw, red_raw = raw_good_reduction(X) + red_raw_cov = covering_morphism(red_raw) + W_red_raw = domain(first(maps_with_given_codomain(red_raw_cov, weierstrass_chart_on_minimal_model(X)))) + + R_red = ambient_coordinate_ring(W_red) + R_red_raw = ambient_coordinate_ring(W_red_raw) + raw_to_red = morphism_from_rational_functions(X_red_raw, X_red, W_red_raw, W_red, fraction_field(R_red_raw).(gens(R_red_raw)); check=false) + set_attribute!(raw_to_red, :is_isomorphism=>true) + red_to_raw = morphism_from_rational_functions(X_red, X_red_raw, W_red, W_red_raw, fraction_field(R_red).(gens(R_red)); check=false) + set_attribute!(red_to_raw, :is_isomorphism=>true) + return raw_to_red, red_to_raw +end + + +function raw_reduction_of_algebraic_lattice(X::EllipticSurface) + return get_attribute!(X, :raw_reduction_of_algebraic_lattice) do + X_red_raw, bc = raw_good_reduction(X) + basis_ambient, _, _= algebraic_lattice(X) + return red_dict = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>_reduce_as_prime_divisor(bc, D) for D in basis_ambient) + end::IdDict +end + +@attr ZZMatrix function good_reduction_algebraic_lattice(X::EllipticSurface) + div_red = raw_reduction_of_algebraic_lattice(X) + from, to = identifications_with_raw_good_reduction(X) + div_red_pf = IdDict{AbsWeilDivisor, AbsWeilDivisor}(D=>pushforward(from, E) for (D, E) in div_red) + basis, _, _= algebraic_lattice(X) + X_red = good_reduction(X) + red_basis, _, _= algebraic_lattice(X_red) + result = matrix(ZZ, [div_red_pf[D] == E ? one(ZZ) : zero(ZZ) for D in basis, E in red_basis]) + result[1, 1] = one(ZZ) # identify the generic fibers + return result +end + +@attr MorphismFromRationalFunctions function good_reduction( + f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface} + ) + X = domain(f) + @assert X === codomain(f) "reduction to positive characteristic is only implemented for automorphisms" + W = weierstrass_chart_on_minimal_model(X) + @assert W === domain_chart(f) === codomain_chart(f) "morphism must be defined on the Weierstrass charts" + img_gens = coordinate_images(f) + red_map = get_good_reduction_map(X) + + X_red = good_reduction(X) + W_red = weierstrass_chart_on_minimal_model(X_red) + R = ambient_coordinate_ring(W_red) + FR = fraction_field(R) + + psi = fr -> FR(map_coefficients(red_map, numerator(fr); parent=R), map_coefficients(red_map, denominator(fr); parent=R)) + + img_gens_red = psi.(img_gens) + result = morphism_from_rational_functions(X_red, X_red, W_red, W_red, img_gens_red; check=false) + has_attribute(f, :is_isomorphism) && get_attribute(f, :is_isomorphism)===true && set_attribute!(result, :is_isomorphism=>true) + return result +end + + +### Some functions to do custom pullback of divisors along reduction maps. +# We assume that primeness is preserved along the reduction. In particular, the +# user is responsible for this to hold for all cases used! +# They specify the "good reduction" in the end. +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::AbsWeilDivisor) + return WeilDivisor(domain(bc), coefficient_ring(D), + IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}( + _reduce_as_prime_divisor(bc, I) => c for (I, c) in coefficient_dict(D) + ) + ) +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, D::EllipticSurfaceSection) + P = rational_point(D) + is_infinite(P) && return _reduce_as_prime_divisor(bc, underlying_divisor(D)) + X = codomain(bc) + @assert parent(P) === generic_fiber(X) + W = weierstrass_chart_on_minimal_model(X) + R = ambient_coordinate_ring(W) + (x, y, t) = gens(R) + I = ideal(R, R.([evaluate(denominator(P[1]), t)*x-evaluate(numerator(P[1]), t), + evaluate(denominator(P[2]), t)*y-evaluate(numerator(P[2]), t)]) + ) + bc_loc = first(maps_with_given_codomain(covering_morphism(bc), W)) + bc_I = pullback(bc_loc)(I) + @assert is_one(dim(bc_I)) + set_attribute!(bc_I, :is_prime=>true) + J = PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), bc_I) + return WeilDivisor(domain(bc), coefficient_ring(D), + IdDict{AbsIdealSheaf, elem_type(coefficient_ring(D))}(J => one(coefficient_ring(D))) + ) +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::AbsIdealSheaf) + result = pullback(bc, I) + has_attribute(I, :_self_intersection) && set_attribute!(result, :_self_intersection=> + (get_attribute(I, :_self_intersection)::Int)) + return result +end + +function _reduce_as_prime_divisor(bc::AbsCoveredSchemeMorphism, I::PrimeIdealSheafFromChart) + U = original_chart(I) + bc_cov = covering_morphism(bc) + V = __find_chart(U, codomain(bc_cov)) + IV = I(V) + bc_loc = first(maps_with_given_codomain(bc_cov, V)) + J = pullback(bc_loc)(IV) + set_attribute!(J, :is_prime=>true) + return PrimeIdealSheafFromChart(domain(bc), domain(bc_loc), J) +end + +################################################################################################ +# +# Given a rational map f:X --> Y compute f_*: NS(X) -> NS(Y) +# +################################################################################################ + +function _local_pushforward(loc_map::AbsAffineSchemeMor, I::Ideal) + U_sub = domain(loc_map) + E, inc_E = sub(U_sub, I) # The subscheme of the divisor + E_simp = simplify(E) # Eliminate superfluous variables + id, id_inv = identification_maps(E_simp) + + comp = compose(compose(id, inc_E), loc_map) + + pb = pullback(comp) + K = kernel(pb) + return K +end + +function _pushforward_lattice_along_isomorphism(step::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) + @assert is_isomorphism(step) "morphism must be an isomorphism" + X = domain(step) + Y = codomain(step) + UX = weierstrass_chart_on_minimal_model(X) + UY = weierstrass_chart_on_minimal_model(Y) + @assert codomain_chart(step) === UY + fracs = coordinate_images(step) + + WY, _ = weierstrass_model(Y) + UWY = weierstrass_chart(Y) + + to_weierstrass_Y = morphism_from_rational_functions(X, WY, UX, UWY, fracs, check=false) + + fibration_proj_Y = fibration(Y) + + BY = codomain(fibration_proj_Y) + UBY = codomain(covering_morphism(fibration_proj_Y)[UY]) + + composit = morphism_from_rational_functions(X, BY, UX, UBY, [fracs[3]], check=false) + + lat_X = algebraic_lattice(X)[1] + if !has_attribute(lat_X[1], :is_prime) + ex, pt, F = irreducible_fiber(X) + ex || error("no irreducible fiber found; case not implemented") + lat_X[1] = weil_divisor(F) + set_attribute!(lat_X[1], :is_prime=>true) + set_attribute!(first(components(lat_X[1])), :is_prime=>true) + end + + # We first estimate for every element in the lattic of X whether its image + # will be a fiber component, or a (multi-)section. + pre_select = IdDict{AbsWeilDivisor, AbsIdealSheaf}() + + for D in lat_X + @assert length(components(D)) == 1 "divisors in the algebraic lattice must be prime" + I = first(components(D)) + @assert has_is_prime(I) && is_prime(I) "ideal sheaf must be known to be prime" + pre_select[D] = _pushforward_prime_divisor(composit, I) + end + + + # Now we map them one by one using the knowledge gained above + result = IdDict{AbsWeilDivisor, AbsWeilDivisor}() + co_ring = coefficient_ring(zero_section(Y)) + + n = length(lat_X) + mwr = rank(mordell_weil_sublattice(X)) + for (i, D) in enumerate(lat_X) + @vprint :EllipticSurface 2 "$((i, D, pre_select[D]))\n" + # D is a non-section + Q = pre_select[D] + I = first(components(D)) + @vprint :EllipticSurface 2 "$(typeof(I))\n" + dom_chart = _find_good_representative_chart(I) + if i > n - mwr # if this is a section + dom_chart = weierstrass_chart_on_minimal_model(X) + end + + if dim(Q) == 0 + @vprint :EllipticSurface 3 "image will be a fiber component\n" + # find the fiber + if is_one(Q(UBY)) # fiber over infinity + # collect all components + comps = AbsWeilDivisor[] + for (pt, _, F, E, _) in reducible_fibers(Y) + if is_zero(pt[2]) # if this is in the fiber over the point at ∞ ∈ ℙ¹ + append!(comps, E[2:end]) + end + end + @vprint :EllipticSurface 3 "found total of $(length(comps)) possible components\n" + + # collect all charts + codomain_charts = AbsAffineScheme[] + if is_empty(comps) # The fiber over infinity + @vprint :EllipticSurface 3 "the image must be the fiber over infinity" + codomain_charts = affine_charts(Y) # TODO: How can we restrict the charts then? + else + codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(D->!isone(first(components(D))(V)), comps)] + end + @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible" + + if i > n - mwr # If D is a section + @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" + pt = X.MWL[i-(n-mwr)] + res = _pushforward_section(step, pt; divisor=D, codomain_charts) + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + else + @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" + loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; domain_chart = dom_chart, codomain_charts) + + loc_map === nothing && error("pushforward preparation did not succeed") + @assert !is_one(I(domain(loc_map))) + K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) + + JJ = ideal(OO(cod_chart), gens(K)) + res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) + + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + end + continue + end + @vprint :EllipticSurface 3 "image will not be in the fiber over infinity\n" + + # fiber over some point ≂̸ ∞. + t = first(gens(OO(UBY))) + + codomain_charts = copy(affine_charts(Y)) + + # Restrict the codomain charts if applicable + for (i, (p, _, F, E, _)) in enumerate(reducible_fibers(Y)) + p[2] == 0 && continue # Fiber over infinity already caught above + t0 = p[1]//p[2] + ideal(OO(UBY), t - t0) == Q(UBY) || continue + + # Collect all patches + codomain_charts = AbsAffineScheme[V for V in affine_charts(Y) if any(I->!isone(I(V)), components(F))] + break + end + @vprint :EllipticSurface 3 "found $(length(codomain_charts)) charts where these components are visible\n" + + if i > n - mwr # If D is a section + @vprint :EllipticSurface 3 "divisor to be mapped is a section\n" + pt = X.MWL[i-(n-mwr)] + res = _pushforward_section(step, pt; divisor=D, codomain_charts) + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + else + @vprint :EllipticSurface 3 "divisor to be mapped is NOT a section\n" + loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I; codomain_charts) + loc_map === nothing && error("preparation for pushforward did not succeed") + + @assert !is_one(I(domain(loc_map))) + K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) + JJ = ideal(OO(cod_chart), gens(K)) + res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) + + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + end + else + # "pushforward will be a section" + if i > n - mwr # If D is a section + pt = X.MWL[i-(n-mwr)] + res = _pushforward_section(step, pt; divisor=D, codomain_charts=[weierstrass_chart_on_minimal_model(Y)]) + if res === nothing + # The only section not visible in the weierstrass chart is the zero section + result[D] = zero_section(Y) + continue + end + + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + else + loc_map, dom_chart, cod_chart = _prepare_pushforward_prime_divisor(step, I, domain_chart = dom_chart, codomain_charts = [weierstrass_chart_on_minimal_model(Y)]) + + if loc_map === nothing + # The only section not visible in the weierstrass chart is the zero section + result[D] = zero_section(Y) + continue + end + + @assert !is_one(I(domain(loc_map))) + K = _local_pushforward(loc_map, I(domain(loc_map))) + @assert !is_one(K) + JJ = ideal(OO(cod_chart), gens(K)) + res = PrimeIdealSheafFromChart(Y, cod_chart, JJ) + + result[D] = WeilDivisor(Y, co_ring, IdDict{AbsIdealSheaf, elem_type(co_ring)}(res::AbsIdealSheaf => one(co_ring)); check=false) + end + end + end + + res = AbsWeilDivisor[result[D] for D in lat_X] + for a in res + set_attribute!(first(components(a)), :_self_intersection, -2) + end + # the first one is the class of the fiber; set that one back + set_attribute!(first(components(first(res))), :_self_intersection, 0) + return res +end + +function _pushforward_section( + phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}, + P::EllipticCurvePoint; + divisor::AbsWeilDivisor=EllipticSurfaceSection(domain(phi), P), + codomain_charts::Vector{<:AbsAffineScheme} = affine_charts(codomain(phi)) + ) + X = domain(phi)::EllipticSurface + Y = codomain(phi)::EllipticSurface + D = divisor + I = first(components(D)) + iso, inc = morphism_from_section(X, P; divisor=D) + U = weierstrass_chart_on_minimal_model(X) + inc_loc = first(maps_with_given_codomain(inc, U)) + U_C = domain(inc_loc) + phi_loc, _, V = _prepare_pushforward_prime_divisor(phi, I; domain_chart=U, codomain_charts) + phi_loc === nothing && return nothing # Indicate that the given selection of codomain charts did not lead to a result + W = codomain(fibration(X)[U]) + iso_loc = _restrict_properly(cheap_realization(iso, W, U_C), U_C) + inc_dom_phi_loc = inclusion_morphism(domain(phi_loc)) + UU, to_U_C, to_U = fiber_product(inc_loc, inc_dom_phi_loc) + WW, a, b = fiber_product(iso_loc, to_U_C) + psi_loc = compose(compose(b, to_U), phi_loc) + K = kernel(pullback(psi_loc)) + J = ideal(OO(V), gens(K)) + JJ = PrimeIdealSheafFromChart(Y, V, J) + return JJ +end + +@doc raw""" + pushforward_on_algebraic_lattices(f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) -> QQMatrix + +Return the pushforward ``f_*: V_1 \to V_2`` where ``V_i`` is the ambient quadratic space of the `algebraic_lattice`. + +This assumes that the image ``f_*(V_1)`` is contained in ``V_2``. If this is not the case, you will get +``f_*`` composed with the orthogonal projection to ``V_2``. +""" +function pushforward_on_algebraic_lattices(f::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) + imgs_divs = _pushforward_lattice_along_isomorphism(f) + M = matrix([basis_representation(codomain(f),i) for i in imgs_divs]) + V1 = ambient_space(algebraic_lattice(domain(f))[3]) + V2 = ambient_space(algebraic_lattice(codomain(f))[3]) + # keep the check on since it is simple compared to all the other computations done here + fstar = hom(V1,V2, M; check=true) + return fstar +end + +# Given an irreducible divisor D on an elliptic surface X, try to extract a point +# on the generic fiber from it. The return value is `nothing` in case this does not succeed. +function point_on_generic_fiber_from_divisor(I::AbsIdealSheaf{<:EllipticSurface}; check::Bool=true) + X = scheme(I) + @check dim(I) == 1 "ideal sheaf must be of dimension one" + return point_on_generic_fiber_from_divisor(WeilDivisor(X, I; check=false); check) +end + +function point_on_generic_fiber_from_divisor(D::AbsWeilDivisor{<:EllipticSurface}; check::Bool=true) + X = ambient_scheme(D) + E = generic_fiber(X) + ex, pt, F = irreducible_fiber(X) + WF = weil_divisor(F) + # TODO: Also cover this case by considering the class of a reducible fiber? + !ex && error("no irreducible fiber exists on this algebraic surface") + @assert length(components(D)) == 1 "divisor must be irreducible" + + I = first(components(D)) + fib = fibration(X) + + # Check a necessary criterion for being a section + # J = pushforward(fib, I) + # is_one(dim(J)) || return nothing + is_zero(intersect(D, WF)) && return nothing +# @check begin +# J = pushforward(fib, I) +# is_one(dim(J)) +# end "given divisor can not be a section" + + #@check is_one(intersect(D, WF)) "intersection number with irreducible fiber is not one" + + WX = weierstrass_chart_on_minimal_model(X) + IWX = I(WX) + is_one(IWX) && return infinity(E) # Point must be the zero section + R = ambient_coordinate_ring(WX) + (x, y, t) = gens(R) + + # In case of a multisection do some extra preparation; see below. + !is_one(intersect(D, WF)) && return point_on_generic_fiber_from_divisor(_prepare_section(D)) + + g = gens(groebner_basis(saturated_ideal(IWX), ordering=lex(gens(R)))) + + # extract the coefficients for the section + kkt = base_field(E) + + # First extract the y-coordinate + i = findfirst(f->(is_zero(degree(f, 1)) && is_one(degree(f, 2))), g) + i === nothing && return nothing + #i === nothing && error("no suitable polynomial found to read off point coordinates") + f = g[i] + y_coord = one(kkt) + ev_vals = [zero(kkt), one(kkt), gen(kkt)] + num = zero(kkt) + den = zero(kkt) + for t in terms(f) + degree(t, 2) == 1 && (den = den - evaluate(t, ev_vals)) + degree(t, 2) == 0 && (num = num + evaluate(t, ev_vals)) + end + y_coord = num//den + + # Now extract the x-coordinate + i = findfirst(f->(is_one(degree(f, 1))), g) + i === nothing && return nothing + #i === nothing && error("no suitable polynomial found to read off point coordinates") + f = g[i] + x_coord = one(kkt) + ev_vals = [one(kkt), y_coord, gen(kkt)] + num = zero(kkt) + den = zero(kkt) + for t in terms(f) + degree(t, 1) == 1 && (den = den - evaluate(t, ev_vals)) + degree(t, 1) == 0 && (num = num + evaluate(t, ev_vals)) + end + x_coord = num//den + + is_zero(evaluate(equation(E), [x_coord, y_coord])) || return nothing + #@assert is_zero(evaluate(equation(E), [x_coord, y_coord])) "esteemed point does not lie on the curve" + P = E([x_coord, y_coord]) + return P +end + +# Given an isomorphism phi : X -> Y of elliptic surfaces and a full algebraic lattice L on X, +# push forward the divisors D from L to Y and try to extract points on the generic fiber from +# them. +# +# This returns a list consisting of the points on the generic fiber. +function extract_mordell_weil_basis(phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) + X = domain(phi) + Y = codomain(phi) + is_isomorphism(phi) || error("morphism must be an isomorphism") + pf_lat = _pushforward_lattice_along_isomorphism(phi) + points = EllipticCurvePoint[] + for D in pf_lat + P = point_on_generic_fiber_from_divisor(D) + P === nothing && continue + push!(points, P) + end + return points +end + +function _prepare_section(D::AbsWeilDivisor{<:EllipticSurface}) + X = ambient_scheme(D) + WX = weierstrass_chart_on_minimal_model(X) + R = ambient_coordinate_ring(WX) + I = first(components(D)) + IWX = I(WX) + # We have a multisection in this case. + # To get a section from it, apply arXiv:2103.15101, Algorithm 1. + + # Build up a helper ring + kkt = base_field(generic_fiber(X)) + f = equation(generic_fiber(X)) + kktXY = parent(f) + (xx, yy) = gens(kktXY) + for (c, e) in zip(coefficients(f), exponents(f)) + if e == [0, 2] + @assert is_one(c) "polynomial is not normalized" + end + end + f = yy^2 - f # prepare the f from the Lemma + #kktXY, (xx, yy) = polynomial_ring(kkt, [:X, :Y]; cached=false) + + @assert coefficient_ring(R) === coefficient_ring(base_ring(kkt)) + help_map = hom(R, kktXY, [xx, yy, kktXY(gen(kkt))]) + + J = ideal(kktXY, help_map.(gens(saturated_ideal(IWX)))) + + J_gens = gens(groebner_basis(J, ordering=lex([yy, xx]))) + i = findfirst(f->degree(f, 2) == 0, J_gens) + i === nothing && error("assertion of Lemma could not be verified") + g = J_gens[i] + i = findfirst(f->degree(f, 2) == 1, J_gens) + i === nothing && error("assertion of Lemma could not be verified") + h = J_gens[i] + c = zero(kkt) + for t in terms(h) + if degree(t, 2) == 1 + c = c + evaluate(t, [zero(kkt), one(kkt)]) + end + end + !isone(c) && (h = inv(c)*h) + h = yy - h + @assert J == ideal(kktXY, [g, yy-h]) + ff = equation(kktXY, generic_fiber(X)) + @assert parent(ff) === parent(f) + @assert ff == yy^2 - f + while total_degree(g) > 1 + g = divexact(h^2 - f, g) + p, q = divrem(h, g) + h = q + end + + F = fraction_field(R) + help_map_back = hom(kktXY, F, u->evaluate(u, F(R[3])), F.([R[1], R[2]])) + new_gens = [help_map_back(g), help_map_back(yy - h)] + sec_ideal = ideal(OO(WX), numerator.(new_gens)) + @assert dim(sec_ideal) == 1 + @assert is_prime(sec_ideal) + + # overwrite the local variables + I = PrimeIdealSheafFromChart(X, WX, sec_ideal) + return weil_divisor(I) +end + +#= +# The map is not dominant and can hence not be realized as a MorphismFromRationalFunctions. +# We keep the code for the moment as it will probably help us to reconstruct this map as a +# proper CoveredSchemeMorphism, once this is needed. +=# +function morphism_from_section( + X::EllipticSurface, P::EllipticCurvePoint; + divisor::AbsWeilDivisor=EllipticSurfaceSection(X, P) + ) + U = weierstrass_chart_on_minimal_model(X) + II = first(components(divisor)) + + # For the zero section we can not use the Weierstrass chart + if is_infinite(P) + return identity_map(X) + end + @assert !is_one(II(U)) + + C, inc_C = sub(II) + + UC = domain(first(maps_with_given_codomain(inc_C, U))) + + B = codomain(fibration(X)) + V = codomain(fibration(X)[weierstrass_chart_on_minimal_model(X)]) + + kkt = OO(V)::MPolyRing + @assert ngens(kkt) == 1 + t = first(gens(kkt)) + img_gens = [evaluate(P.coordx, t), evaluate(P.coordy, t), t] + + Fkkt = fraction_field(kkt) + img_gens2 = Fkkt.(img_gens) + # TODO: Cache? + iso = morphism_from_rational_functions(B, C, V, UC, img_gens2, check=false) + return iso, inc_C +end + +######################################################################## +# Translations by sections # +######################################################################## + +@doc raw""" + translation_morphism(X::EllipticSurface, P::EllipticCurvePoint) -> MorphismFromRationalFunctions + +Return the automorphism of ``X`` defined by fiberwise translation by the section ``P``. +""" +function translation_morphism(X::EllipticSurface, P::EllipticCurvePoint) + E = generic_fiber(X) + @assert parent(P) === E "point does not lay on the underlying elliptic curve" + U = weierstrass_chart_on_minimal_model(X) + is_zero(P) && return identity_map(X) + + # We construct the translation by P as a morphism of rational functions + kT = base_field(E) + T = first(gens(kT)) + + R = ambient_coordinate_ring(U) + x, y, t = gens(R) + + a1, a2, a3, a4, a6 = [evaluate(a, t) for a in a_invariants(E)] + + p_x = evaluate(P[1], t) + p_y = evaluate(P[2], t) + + # Formulas adapted from Hecke/src/EllCrv/EllCrv.jl + m = (p_y - y)//(p_x - x) + pb_x = - x - p_x - a2 + a1*m + m^2 + pb_y = - y - m*(pb_x - x) - a1*pb_x - a3 + + F = fraction_field(R) + + result = morphism_from_rational_functions(X, X, U, U, F.([pb_x, pb_y, t]), check=true) + set_attribute!(result, :is_isomorphism=>true) + return result +end + +######################################################################## +# Möbius transformations # +######################################################################## + +# Find a moebius transformation which sends a given set of three points in ℙ¹ to another set +# of three points. +function find_moebius_transformation( + orig_pts::Vector{<:Vector{<:FieldElem}}, + new_pts::Vector{<:Vector{<:FieldElem}} + ) + kk = parent(first(orig_pts)) + a = [a[1] for a in orig_pts] + b = [b[1] for b in new_pts] + @assert all(a->isone(a[2]), orig_pts) "not implemented for non-normalized or infinite points" + @assert all(a->isone(a[2]), new_pts) "not implemented for non-normalized or infinite points" + return find_moebius_transformation(a, b) +end + +function find_moebius_transformation( + orig_pts::Vector{<:FieldElem}, + new_pts::Vector{<:FieldElem} + ) + length(orig_pts) == 3 || error("exactly three points are needed") + @assert length(orig_pts) == length(new_pts) "number of points must coincide" + kk = parent(first(orig_pts)) + a = orig_pts + b = new_pts + + # Set up the matrix mapping the first three points to 0, 1, ∞ + A = kk[(a[2] - a[3]) (-a[1]*(a[2] - a[3])); (a[2] - a[1]) (-a[3]*(a[2] - a[1]))] + + # Set up the matrix mapping the second three points to 0, 1, ∞ + B = kk[(b[2] - b[3]) (-b[1]*(b[2] - b[3])); (b[2] - b[1]) (-b[3]*(b[2] - b[1]))] + + C = inv(B)*A + return x->(C[1,1]*x + C[1, 2], C[2,1]*x + C[2,2]) +end + +# Given two abstractly isomorphic elliptic surfaces X and Y over ℙ¹, +# find all moebius transformation of the base which preserve the critical +# values of the projections, try to lift them to morphisms X -> Y and +# return the list of such morphisms for which the lift was successful. +@doc raw""" + isomorphisms(X::EllipticSurface, Y::EllipticSurface) -> Vector{MorphismFromRationalFunctions} + +Given two elliptic surfaces `` X \to \mathbb{P}^1`` and `` Y \to \mathbb{P}^1`` return all +isomorphisms ``X \to Y`` such that there exists Möbius transformation ``\mathbb{P}^1 \to \mathbb{P}^1`` +fitting in the following commutative diagram. +```math +\begin{array}{ccc} + X & \to & Y \\ + \downarrow & & \downarrow\\ + \mathbb{P}^1 & \to & \mathbb{P}^1 +\end{array} +``` +""" +isomorphisms(X::EllipticSurface, Y::EllipticSurface) = admissible_moebius_transformations(X, Y) + +isomorphisms_on_weierstrass_chart(X::EllipticSurface, Y::EllipticSurface) = admissible_moebius_transformations_on_weierstrass_chart(X, Y) + +function admissible_moebius_transformations( + X::EllipticSurface, + Y::EllipticSurface + ) + result = MorphismFromRationalFunctions[] + for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=false) + push!(result, _moebius_to_morphism_from_rational_functions(X, Y, img_gens)) + end + return result +end + +function admissible_moebius_transformations_on_weierstrass_chart( + X::EllipticSurface, + Y::EllipticSurface + ) + result = MapFromFunc[] + for img_gens in _admissible_moebius_transformations(X, Y; on_weierstrass_model=true) + push!(result, _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens)) + end + return result +end + +function _admissible_moebius_transformations( + X::EllipticSurface, + Y::EllipticSurface; on_weierstrass_model=true + ) + EX = generic_fiber(X) + EY = generic_fiber(Y) + +# kkt = base_field(EX) +# @assert kkt === base_field(EY) "base fields of the generic fibers must coincide" + kk = base_ring(X) + @assert kk === base_ring(Y) "elliptic surfaces must be defined over the same field" + + dX = numerator(discriminant(EX))::PolyRingElem + dY = numerator(discriminant(EY))::PolyRingElem + + vX = roots(dX) + @assert all(is_one(degree(a)) for (a, k) in factor(dX)) "not all critical values are rational over the given ground field" + + vY = roots(dY) + @assert all(is_one(degree(a)) for (a, k) in factor(dY)) "not all critical values are rational over the given ground field" + +# for (c, _) in reducible_fibers(X) +# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" +# end +# for (c, _) in reducible_fibers(Y) +# @assert !is_zero(c[2]) "the case of reducible fibers over the point at infinity is not implemented" +# end + + # Use the first three elements of vX and map them to three elements of vY. + # Then check whether the resulting transformation preserves everything. + + candidates = [] + + @assert length(vX) >= 3 "at least three reducible fibers are needed" + length(vX) == length(vY) || return candidates # No moebius transformation is possible in this case + kkt = base_field(EX) + t = gen(kkt) + p1 = vX[1:3] + for i in vY + for j in vY + i == j && continue + for k in vY + (i == k || j == k) && continue + p2 = [i, j, k] + mt = find_moebius_transformation(p1, p2) + any(is_zero(mt(x)[2]) for x in vX) && continue # reducible fibers over ∞ are not implemented at the moment. + any(!(mt(x)[1]//mt(x)[2] in vY) for x in vX) && continue # the transformation does not preserve all admissible fibers in this case + p, q = mt(t) + img_t = (p//q)::typeof(t) + EYbc = base_change(f->evaluate(f, img_t), EY) + is_isomorphic(EYbc, EX) || continue + iso_ell = isomorphism(EX, EYbc) + push!(candidates, _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model)) + end + end + end + return candidates +end + +function _to_weierstrass_morphism(X, Y, mt, iso_ell; on_weierstrass_model) + EX = generic_fiber(X) + EY = generic_fiber(Y) + # Set up some variables + kkt = base_field(EX) + t = gen(kkt) + if on_weierstrass_model + WX = weierstrass_chart(X) + WY = weierstrass_chart(Y) + else + WX = weierstrass_chart_on_minimal_model(X) + WY = weierstrass_chart_on_minimal_model(Y) + end + RX = ambient_coordinate_ring(WX) + FRX = fraction_field(RX) + RY = ambient_coordinate_ring(WY) + FRY = fraction_field(RY) + + # Construct the isomorphism of elliptic surfaces explicitly + + a, b, _ = rational_maps(iso_ell) + kkTxy = parent(a) + to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) + A = to_FRX(a) + B = to_FRX(b) + P, Q = mt(FRX(RX[3])) + img_T = (P//Q)::elem_type(FRX) + img_gens = [A, B, img_T] + return img_gens +end + +function _moebius_to_pullback_on_weierstrass_chart(X, Y, img_gens) + WY = weierstrass_chart(Y) + WX = weierstrass_chart(X) + RX = ambient_coordinate_ring(WX) + FRX = fraction_field(RX) + RY = ambient_coordinate_ring(WY) + FRY = fraction_field(RY) + + return extend_domain_to_fraction_field(hom(RY, FRX, img_gens)) +end + +function _moebius_to_morphism_from_rational_functions(X, Y, img_gens) + WY = weierstrass_chart_on_minimal_model(Y) + WX = weierstrass_chart_on_minimal_model(X) + + loc_res = morphism_from_rational_functions(X, Y, WX, WY, img_gens; check=true) + set_attribute!(loc_res, :is_isomorphism=>true) + return loc_res +end + +# An internal helper routine to verify that a given isomorphism of elliptic surfaces +# does indeed give an isomorphism on their generic fibers. +function check_isomorphism_on_generic_fibers(phi::MorphismFromRationalFunctions{<:EllipticSurface, <:EllipticSurface}) + X = domain(phi) + Y = codomain(phi) + @assert domain_chart(phi) === weierstrass_chart_on_minimal_model(X) + @assert codomain_chart(phi) === weierstrass_chart_on_minimal_model(Y) + EX = generic_fiber(X) + EY = generic_fiber(Y) + a, b, c = coordinate_images(phi) + + hX = equation(EX) + RX = parent(hX) + FX = fraction_field(RX) + kktX = coefficient_ring(RX) + + hY = equation(EY) + RY = parent(hY) + FY = fraction_field(RY) + kktY = coefficient_ring(RY) + + A = evaluate(a, [RX[1], RX[2], RX(gen(kktX))]) + B = evaluate(b, [RX[1], RX[2], RX(gen(kktX))]) + C = evaluate(c, [RX[1], RX[2], RX(gen(kktX))]) + + help_map = hom(RY, FX, t->evaluate(t, C), [A, B]) + + hh = help_map(hY) + + return divides(hX, numerator(hh))[1] +end + +@doc raw""" + isomorphism_from_generic_fibers(X::EllipticSurface, Y::EllipticSurface, f::Hecke.EllCrvIso) -> MorphismFromRationalFunctions + +Given an isomorphism ``f`` between the generic fibers of ``X`` and ``Y``, return the corresponding +isomorphism of ``X`` and ``Y`` over ``\mathbb{P}^1``. +""" +function isomorphism_from_generic_fibers( + X::EllipticSurface, Y::EllipticSurface, f::Hecke.EllCrvIso + ) + EX = generic_fiber(X) + EY = generic_fiber(Y) + iso_ell = f + @req domain(f) == EX "must be an isomorphism of the generic fibers" + @req codomain(f) == EY "must be an isomorphism of the generic fibers" + a, b, _ = rational_maps(iso_ell) + kt = base_field(EX) + t = gen(kt) + + # Make sure we got something reasonable + h2 = equation(EY) + pb_h2 = evaluate(h2, [a, b]) + @assert divides(pb_h2, equation(parent(pb_h2), EX))[1] + + WX = weierstrass_chart_on_minimal_model(X) + RX = ambient_coordinate_ring(WX) + FRX = fraction_field(RX) + WY = weierstrass_chart_on_minimal_model(Y) + RY = ambient_coordinate_ring(WY) + FRY = fraction_field(RY) + + kkTxy = parent(a) + to_FRX = hom(kkTxy, FRX, x->evaluate(x, FRX(RX[3])), FRX.([RX[1], RX[2]])) + A = to_FRX(a) + B = to_FRX(b) + img_gens = [A, B, FRX(RX[3])] + m = morphism_from_rational_functions(X, Y, WX, WY, FRX.(img_gens); check=false) + set_attribute!(m, :is_isomorphism=>true) + return m +end + +@doc raw""" + isomorphism_from_generic_fibers(X::EllipticSurface, Y::EllipticSurface) -> MorphismFromRationalFunctions + +Given given two elliptic surfaces ``X`` and ``Y`` whose generic fibers are isomorphic, +return the corresponding isomorphism of ``X`` and ``Y`` over ``\mathbb{P}^1``. +""" +function isomorphism_from_generic_fibers( + X::EllipticSurface, Y::EllipticSurface + ) + EX = generic_fiber(X) + EY = generic_fiber(Y) + is_isomorphic(EX, EY) || error("generic fibers are not isomorphic") + iso_ell = isomorphism(EX, EY) + return isomorphism_from_generic_fibers(X, Y, iso_ell) +end diff --git a/src/AlgebraicGeometry/Surfaces/EllipticSurface/MoveMeToHecke.jl b/src/AlgebraicGeometry/Surfaces/EllipticSurface/MoveMeToHecke.jl new file mode 100644 index 000000000000..f8e307900f79 --- /dev/null +++ b/src/AlgebraicGeometry/Surfaces/EllipticSurface/MoveMeToHecke.jl @@ -0,0 +1,57 @@ + +function evaluate(f::AbstractAlgebra.Generic.FracFieldElem{<:MPolyRingElem}, a::Vector{T}) where {T<:RingElem} + return evaluate(numerator(f), a)//evaluate(denominator(f), a) +end + +function evaluate(f::AbstractAlgebra.Generic.FracFieldElem{<:PolyRingElem}, a::RingElem) + return evaluate(numerator(f), a)//evaluate(denominator(f), a) +end + +function extend_domain_to_fraction_field(phi::Map{<:MPolyRing, <:Ring}) + ext_dom = fraction_field(domain(phi)) + return MapFromFunc(ext_dom, codomain(phi), x->phi(numerator(x))*inv(phi(denominator(x)))) +end + + +function iszero(P::EllipticCurvePoint) + return iszero(P[1]) && isone(P[2]) && iszero(P[3]) +end + +@doc raw""" + extended_ade(ADE::Symbol, n::Int) + +Return the dual intersection matrix of an extended ade Dynkin diagram +as well as the isotropic vector (with positive coefficients in the roots). +""" +function extended_ade(ADE::Symbol, n::Int) + R = change_base_ring(ZZ,gram_matrix(root_lattice(ADE,n))) + G = block_diagonal_matrix([ZZ[2;],R]) + if ADE == :E && n == 8 + G[1,n] = -1 + G[n,1] = -1 + end + if ADE == :E && n == 7 + G[1,2] = -1 + G[2,1] = -1 + end + if ADE == :E && n == 6 + G[1,n+1] = -1 + G[n+1,1] = -1 + end + if ADE == :A && n > 0 + G[1,2] = -1 + G[2,1] = -1 + G[1,n+1] = -1 + G[n+1,1] = -1 + end + if ADE == :A && n ==1 0 + G[1,2]= -2 + G[2,1] = -2 + end + if ADE == :D + G[1,n] = -1 + G[n,1] = -1 + end + @assert rank(G) == n + return -G, kernel(G; side = :left) +end diff --git a/src/AlgebraicGeometry/Surfaces/EllipticSurface/NeighborStep.jl b/src/AlgebraicGeometry/Surfaces/EllipticSurface/NeighborStep.jl new file mode 100644 index 000000000000..f5706d9b7a1a --- /dev/null +++ b/src/AlgebraicGeometry/Surfaces/EllipticSurface/NeighborStep.jl @@ -0,0 +1,899 @@ +################################################################################ +# +# Some linear systems on elliptic surfaces +# +################################################################################ + +@doc raw""" + _prop217(E::EllipticCurve, P::EllipticCurvePoint, k) + +Compute a basis for the linear system +``|O + P + kF|`` +on the minimal elliptic (K3) surface defined by E. +Here F is the class of a fiber O the zero section +and P any non-torsion section. + +The return value is a list of pairs ``(a(t),b(t))`` + +```jldoctest +julia> kt,t = polynomial_ring(GF(29),:t); + +julia> ktfield = fraction_field(kt); + +julia> bk = [((17*t^4 + 23*t^3 + 18*t^2 + 2*t + 6, 8*t^5 + 2*t^4 + 6*t^3 + 25*t^2 + 24*t + 5 )), + ((17*t^6 + 3*t^5 + 16*t^4 + 4*t^3 + 13*t^2 + 6*t + 5)//(t^2 + 12*t + 7), (4*t^8 + 19*t^7 + 14*t^6 + 18*t^5 + 27*t^4 + 13*t^3 + 9*t^2 + 14*t + 12)//(t^3 + 18*t^2 + 21*t + 13) ), + ((17*t^6 + 10*t^5 + 24*t^4 + 15*t^3 + 22*t^2 + 27*t + 5)//(t^2 + 16*t + 6), (20*t^8 + 24*t^7 + 22*t^6 + 12*t^5 + 21*t^4 + 21*t^3 + 9*t^2 + 21*t + 12)//(t^3 + 24*t^2 + 18*t + 19) ), + ((17*t^8 + 21*t^7 + 20*t^5 + 24*t^4 + 21*t^3 + 4*t^2 + 9*t + 13)//(t^4 + 17*t^3 + 12*t^2 + 28*t + 28), (23*t^11 + 25*t^10 + 8*t^9 + 7*t^8 + 28*t^7 + 16*t^6 + 7*t^5 + 23*t^4 + 9*t^3 + 27*t^2 + 13*t + 13)//(t^6 + 11*t^5 + 14*t^4 + 13*t^3 + 6*t^2 + 18*t + 12) )]; + +julia> E = elliptic_curve(ktfield,[3*t^8+24*t^7+22*t^6+15*t^5+28*t^4+20*t^3+16*t^2+26*t+16, 24*t^12+27*t^11+28*t^10+8*t^9+6*t^8+16*t^7+2*t^6+10*t^5+3*t^4+22*t^3+27*t^2+10*t+3]); + +julia> bk = [E(collect(i)) for i in bk]; + +julia> Oscar._prop217(E,bk[2],2) +5-element Vector{Tuple{FqPolyRingElem, FqPolyRingElem}}: + (t^2 + 12*t + 7, 0) + (t^3 + 8*t + 3, 0) + (t^4 + 23*t + 2, 0) + (25*t + 22, 1) + (12*t + 28, t) + +julia> Oscar._prop217(E,bk[1],1) +2-element Vector{Tuple{FqPolyRingElem, FqPolyRingElem}}: + (1, 0) + (t, 0) +``` +""" +function _prop217(E::EllipticCurve, P::EllipticCurvePoint, k) + @req !iszero(P[3]) "P must not be torsion" # seems like we cannot check this + xn = numerator(P[1]) + xd = denominator(P[1]) + yn = numerator(P[2]) + yd = denominator(P[2]) + OP = divexact(max(degree(xd), degree(xn) - 4), 2) + dega = k + 2*OP + degb = k + 2*OP - 2 - divexact(degree(xd), 2) #? + base = base_field(E) + Bt = base_ring(base) + B = coefficient_ring(Bt) + + R,ab = polynomial_ring(base,vcat([Symbol(:a,i) for i in 0:dega],[Symbol(:b,i) for i in 0:degb]),cached=false) + Rt, t1 = polynomial_ring(R,:t) + a = reduce(+,(ab[i+1]*t1^i for i in 0:dega), init=zero(Rt)) + b = reduce(+,(ab[2+dega+j]*t1^j for j in 0:degb), init=zero(Rt)) + c = a*xn(t1) - b*yn(t1) + r = mod(c, xd(t1)) + # setup the linear equations for coefficients of r to vanish + # and for the degree of c to be bounded above by + # k + 2*OP + 4 + degree(xd) + eq1 = collect(coefficients(r)) + eq2 = [coeff(c,i) for i in (k + 2*OP + 4 + degree(xd) + 1):degree(c)] + eqns = vcat(eq1, eq2) + + # collect the equations as a matrix + cc = [[coeff(j, abi) for abi in ab] for j in eqns] + mat = reduce(vcat,cc, init=elem_type(base)[]) + @assert all(is_one(denominator(x)) for x in mat) + @assert all(is_constant(numerator(x)) for x in mat) + mat2 = [constant_coefficient(numerator(x)) for x in mat] + M = matrix(B, length(eqns), length(ab), mat2) + # @assert M == matrix(base, cc) # does not work if length(eqns)==0 + K = kernel(M; side = :right) + kerdim = ncols(K) + result = Tuple{elem_type(Bt),elem_type(Bt)}[] + t = gen(Bt) + for j in 1:kerdim + aa = reduce(+, (K[i+1,j]*t^i for i in 0:dega), init=zero(Bt)) + bb = reduce(+, (K[dega+i+2,j]*t^i for i in 0:degb), init=zero(Bt)) + push!(result, (aa, bb)) + end + # confirm the computation + @assert kerdim == 2*k + OP # prediced by Riemann-Roch + for (a,b) in result + @assert mod(a*xn - b*yn, xd) == 0 + @assert degree(a) <= k + 2*OP + @assert degree(b) <= k + 2*OP - 2 - 1//2*degree(xd) + @assert degree(a*xn - b*yn) <= k + 2*OP + 4 + degree(xd) + end + return result +end + +@doc raw""" + linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int) -> LinearSystem + +Compute the linear system ``|O + P + k F|`` on the elliptic surface ``X``. +Here ``F`` is the class of the fiber over ``[0:1]``, ``O`` the zero section +and ``P`` any section given as a point on the generic fiber. + +The linear system is represented in terms of the Weierstrass coordinates. +""" +function linear_system(X::EllipticSurface, P::EllipticCurvePoint, k::Int) + euler_characteristic(X) == 2 || error("linear system implemented only for elliptic K3s") + #FS = function_field(weierstrass_model(X)[1]) + FS = function_field(X) + U = weierstrass_chart_on_minimal_model(X) + (x,y,t) = ambient_coordinates(U) + + sections = elem_type(FS)[] + if iszero(P[3]) + append!(sections, [FS(t)^(i-k) for i in 0:k]) + append!(sections, [FS(t)^(i-k)*FS(x) for i in 0:k-4]) + else + xn = numerator(P[1]) + xd = denominator(P[1]) + yn = numerator(P[2]) + yd = denominator(P[2]) + + I = saturated_ideal(defining_ideal(U)) + IP = ideal([x*xd(t)-xn(t),y*yd(t)-yn(t)]) + @hassert :EllipticSurface 2 issubset(I, IP) || error("P does not define a point on the Weierstrasschart") + + @assert gcd(xn, xd)==1 + @assert gcd(yn, yd)==1 + ab = _prop217(generic_fiber(X), P, k) + d = divexact(yd, xd)(t) + den = t^k*(x*xd(t) - xn(t)) + for (a,b) in ab + c = divexact(b*yn - a*xn, xd) + num = a(t)*x+b(t)*d*y + c(t) + push!(sections, FS(num//den)) + end + end + return sections +end + +@doc raw""" + two_neighbor_step(X::EllipticSurface, F1::Vector{QQFieldElem}) + +Let ``F`` be the class of a fiber of the elliptic fibration on ``X``. +Given an isotropic nef divisor ``F_1`` with ``F_1.F = 2``, +compute the linear system ``|F_1|`` and return the corresponding generic fiber +as a double cover `C` of the projective line branched over four points. + +Input: +``F_1`` is represented as a vector in the `algebraic_lattice(X)` +``X`` must be a K3 surface + +Output: +A tuple `(C, (x1, y1, t1))` defined as follows. +- `C` is given by a polynomial ``y_1^2 - q(x_1)`` in ``k(t_1)[x_1,y_1]`` with ``q`` of degree ``3`` or ``4``. +- (x1,y1,t1) are expressed as rational functions in terms of the weierstrass coordinates `(x,y,t)`. +""" +function two_neighbor_step(X::EllipticSurface, F::Vector{QQFieldElem}) + E = generic_fiber(X) + basisNS, tors, NS = algebraic_lattice(X) + V = ambient_space(NS) + @req inner_product(V, F, F)==0 "not an isotropic divisor" + @req euler_characteristic(X) == 2 "not a K3 surface" + F0 = zeros(QQ,degree(NS)); F0[1]=1 + + @req inner_product(V, F, F0) == 2 "not a 2-neighbor" + + D1, D, P, l, c = horizontal_decomposition(X, F) + u = _elliptic_parameter(X, D1, D, P, l, c) + @assert scheme(parent(u)) === X + pr = weierstrass_contraction(X) + WX, _ = weierstrass_model(X) + # The following is a cheating version of the command u = pushforward(pr)(u) (the latter has now been deprecated!) + u = function_field(WX)(u[weierstrass_chart_on_minimal_model(X)]) + @assert scheme(parent(u)) === weierstrass_model(X)[1] + + # Helper function + my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) + + # transform to a quartic y'^2 = q(x) + if iszero(P[3]) # P = O + eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case1) + eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) +# function phi_func(x) +# y = phi1(x) +# n = numerator(y) +# d = denominator(y) +# return phi2(n)//phi2(d) +# end +# phi = MapFromFunc(domain(phi1), codomain(phi2), phi_func) +# # TODO: Verify that the construction below also works and replace by that, eventually. +# phi_alt = compose(phi1, extend_domain_to_fraction_field(phi2)) +# @assert phi.(gens(domain(phi))) == phi_alt.(gens(domain(phi))) + phi = compose(phi1, extend_domain_to_fraction_field(phi2)) + elseif iszero(2*P) # P is a 2-torsion section + eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case3) + #eqn1, phi1 = _conversion_case_3(X, u) + (x2, y2) = gens(parent(eqn1)) + + # Make sure the coefficient of y² is one (or a square) so that + # completing the square works. + c = my_const(coeff(eqn1, [x2, y2], [0, 2]))::AbstractAlgebra.Generic.FracFieldElem + eqn1 = inv(unit(factor(c)))*eqn1 + + eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) + phi = compose(phi1, extend_domain_to_fraction_field(phi2)) + else # P has infinite order + eqn1, phi1 = _elliptic_parameter_conversion(X, u, case=:case2) + #eqn1, phi1 = _conversion_case_2(X, u) + (x2, y2) = gens(parent(eqn1)) + + # Make sure the coefficient of y² is one (or a square) so that + # completing the square works. + c = my_const(coeff(eqn1, [x2, y2], [0, 2]))::AbstractAlgebra.Generic.FracFieldElem + eqn1 = inv(unit(factor(c)))*eqn1 + + eqn2, phi2 = _normalize_hyperelliptic_curve(eqn1) + phi = compose(phi1, extend_domain_to_fraction_field(phi2)) + end + + return eqn2, phi +end + +@doc raw""" + horizontal_decomposition(X::EllipticSurface, L::Vector{QQFieldElem}) -> AbsWeilDivisor, EllipticCurvePoint + +Given a divisor ``L`` as a vector in the `algebraic_lattice(X)` +find a linearly equivalent divisor ``(n-1) O + P + V = D ~ L`` where +``O`` is the zero section, ``P`` is any section and ``V`` is vertical. + +Returns a tuple `(D1, D, P, l, c)` where `D` and `P` are as above and +``D <= D1 = (n-1)O + P + n_1F_1 + ... n_k F_k`` with ``l = n_1 + ... n_k`` minimal +and the `F_i` are some other fibers. +The rational function `c=c(t)` has divisor of zeros and poles`` +(c) = lF - n_0F_1 + ... n_k F_k`` +""" +function horizontal_decomposition(X::EllipticSurface, F::Vector{QQFieldElem}) + E = generic_fiber(X) + basisNS, tors, NS = algebraic_lattice(X) + V = ambient_space(NS) + @req F in algebraic_lattice(X)[3] "not in the algebraic lattice" + @req inner_product(V, F, F)==0 "not an isotropic divisor" + @req euler_characteristic(X) == 2 "not a K3 surface" + # how to give an ample divisor automagically in general? + # @req is_nef(X, F) "F is not nef" + l = F[1] + rk_triv = nrows(trivial_lattice(X)[2]) + n = rank(NS) + @assert degree(NS) == rank(NS) + p, P = _vertical_part(X,F) + D = section(X, P) + F2 = F - p + @vprint :EllipticSurface 4 "F2 = $(F2)\n" + D = D + ZZ(F2[2])*zero_section(X) + D1 = D + F2 = ZZ.(F2); F2[2] = 0 + l = F2[1] # number of fibers that we need + # find the fiber components meeting O necessary + F3 = F2 + (_,_,t) = ambient_coordinates(weierstrass_chart_on_minimal_model(X)) + c = t^0 + for (pt, rt, fiber, comp, gram) in reducible_fibers(X) + Fib0 = comp[1] + f0 = zeros(QQFieldElem, length(basisNS)) + for i in 1:length(basisNS) + if !isone(components(Fib0)[1]+components(basisNS[i])[1]) + if length(comp)==2 && 2=0 for i in 1:length(basisNS)) + D = D + sum(ZZ(F4[i])*basisNS[i] for i in 1:length(basisNS)) + @assert D<=D1 + return D1, D, P, Int(l), c +end + +# internal method used for two neighbor steps +# in the horizontal_decomposition +function _vertical_part(X::EllipticSurface, v::QQMatrix) + @req nrows(v)==1 "not a row vector" + _,tors, NS = algebraic_lattice(X) + E = generic_fiber(X) + @req ncols(v)==degree(NS) "vector of wrong size $(ncols(v))" + @req v in NS "not an element of the lattice" + mwl_rank = length(X.MWL) + rk_triv = rank(NS)-mwl_rank + n = rank(NS) + P = sum([ZZ(v[1,i])*X.MWL[i-rk_triv] for i in (rk_triv+1):n], init = E([0,1,0])) + p = zero_matrix(QQ, 1, rank(NS)) # the section part + p[1,end-mwl_rank+1:end] = v[1,end-mwl_rank+1:end] + p[1,2] = 1 - sum(p) # assert p.F = 1 by adding a multiple of the zero section O + + # P meets exactly one fiber component per fiber + # and that one must be simple, it can be the one meeting O or not + # assert this by adding fiber components under the additional condition that p stays in the algebraic lattice + simples = [] + E = identity_matrix(QQ,rank(NS)) + z = zero_matrix(QQ,1, rank(NS)) + r = 2 + for fiber in _trivial_lattice(X)[3] + fiber_type = fiber[2] + fiber_rk = fiber_type[2] + h = highest_root(fiber_type...) + simple_indices = [r+i for i in 1:ncols(h) if isone(h[1,i])] + simple_or_zero = [E[i:i,:] for i in simple_indices] + push!(simple_or_zero, z) + push!(simples,simple_or_zero) + r += fiber_rk + end + G = gram_matrix(ambient_space(NS)) + pG = (p*G)[1:1,3:r] + T = Tuple(simples) + GF = G[3:r,3:r] + candidates = QQMatrix[] + for s in Base.Iterators.ProductIterator(T) + g = sum(s)[:,3:r] + y = (g - pG) + xx = solve(GF,y;side=:left) + x = zero_matrix(QQ,1, rank(NS)) + x[:,3:r] = xx + if x in NS + push!(candidates,p+x) + end + end + @assert length(candidates)>0 + # Select the candidate congruent to v modulo Triv + mwg = _mordell_weil_group(X) + vmwg = mwg(vec(collect(v))) + candidates2 = [mwg(vec(collect(x))) for x in candidates] + i = findfirst(==(vmwg), candidates2) + t = mwg(vec(collect(p - candidates[i]))) + mwl_tors_gens = [mwg(vec(collect(i[2]))) for i in tors] + ag = abelian_group(zeros(ZZ,length(tors))) + mwlAb = abelian_group(mwg) + phi = hom(ag, mwlAb, mwlAb.(mwl_tors_gens)) + a = preimage(phi, mwlAb(t)) + for i in 1:ngens(ag) + P += a[i]*rational_point(tors[i][1]) + end + + p = candidates[i] + k = (p*G*transpose(p))[1,1] + # assert p^2 = -2 + p[1,1] = -k/2-1 + V = ambient_space(NS) + @hassert :EllipticSurface 1 inner_product(V, p, p)[1,1]== -2 + @hassert :EllipticSurface 1 mwg(vec(collect(p))) == mwg(vec(collect(p))) + @hassert :EllipticSurface 3 basis_representation(X,section(X,P))==vec(collect(p)) + return p, P +end + +function _vertical_part(X::EllipticSurface, v::Vector{QQFieldElem}) + vv = matrix(QQ,1,length(v),v) + p, P = _vertical_part(X,vv) + pp = vec(collect(p)) + return pp, P +end + +@doc raw""" + elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) -> LinearSystem + +Return the elliptic parameter ``u`` of the divisor class `F`. + +The input `F` must be given with respect to the basis of +`algebraic_lattice(X)` and be an isotropic nef divisor. +This method assumes that $X$ is a K3 surface. +""" +function elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) + D1, D, P, l, c = horizontal_decomposition(X, F) + return _elliptic_parameter(X, D1, D, P, l, c) +end + +@doc raw""" + _elliptic_parameter(X::EllipticSurface, D::AbsWeilDivisor, l, c) + +Compute the linear system of ``D = (n-1) O + P + V``. +where V is vertical and `l` is the coefficient of the fiber class. +Assumes `D` nef and `D^2=0`. +Typically ``D`` is the output of `horizontal_decomposition`. +""" +function _elliptic_parameter(X::EllipticSurface, D1::AbsWeilDivisor, D::AbsWeilDivisor, P::EllipticCurvePoint, l::Int, c) + S, piS = weierstrass_model(X); + piX = weierstrass_contraction(X) + c = function_field(X)(c) + L = [i*c for i in linear_system(X, P, l)]; + LonX = linear_system(L, D1, check=false); + + LsubF, Tmat = subsystem(LonX, D); + LsubFonS = [sum(Tmat[i,j]*L[j] for j in 1:ncols(Tmat)) for i in 1:nrows(Tmat)] + + @assert length(LsubFonS)==2 + u2 = LsubFonS[2]//LsubFonS[1] + return u2 +end + + +######################################################################## +# Internal functionality for Weierstrass transformation +######################################################################## + +@doc raw""" + _normalize_hyperelliptic_curve(g::MPolyRingElem, parent=nothing) + +Transform ``a(x)y^2 + b(x)y - h(x)`` in ``K(t)[x,y]`` to ``y'^2 - h(x')`` +""" +function _normalize_hyperelliptic_curve(g::MPolyRingElem; parent::Union{MPolyRing, Nothing}=parent(g)) + R = Oscar.parent(g) + @assert ngens(R) == 2 "polynomial must be bivariate" + F = fraction_field(R) + kt = coefficient_ring(R) + (x, y) = gens(R) + + # Prepare the output ring + if parent===nothing + R1, (x1, y1) = R, gens(R) + else + R1 = parent + @assert coefficient_ring(R1) == coefficient_ring(R) "coefficient ring of output is incompatible with input" + (x1, y1) = gens(R1) + end + + # Get the coefficients of g as a univariate polynomial in y + ktx, X = polynomial_ring(kt, :X, cached=false) + ktxy, Y = polynomial_ring(ktx, :y, cached=false) + + # Maps to transform to univariate polynomials in y + split_map_R = hom(R, ktxy, [ktxy(X), Y]) + split_map_R1 = hom(R1, ktxy, [ktxy(X), Y]) + G = split_map_R(g) + @assert degree(G) == 2 "polynomial must be of degree 2 in its second variable" + + #complete the square + h, b, a = collect(coefficients(G)) + h = -h + u = unit(factor(a)) + a = inv(u)*a + b = inv(u)*b + success, sqa = is_square_with_sqrt(a) + @assert success "leading coefficient as univariate polynomial in the second variable must be a square" + + F1 = fraction_field(R1) + psi = hom(R1, F, F.([x, (2*evaluate(a, x)*y + evaluate(b, x))//(2*evaluate(sqa, x))])) + conv = MapFromFunc(ktx, R1, f->evaluate(f, x1)) + (a1, b1, sqa1) = conv.([a, b, sqa]) + phi = hom(R, F1, F1.([x1, (2*sqa1*y1-b1)//(2*a1)])) + phiF = MapFromFunc(F, F1, x-> phi(numerator(x))//phi(denominator(x))) + # the inverse map if wanted + # psiF = MapFromFunc(F1, F, x-> psi(numerator(x))//psi(denominator(x))) + # @assert all(phiF(psiF(F1(i)))==i for i in gens(R1)) + + # absorb squares into y1 + g1 = numerator(phi(g)) + G1 = split_map_R1(g1) + ff = factor(first(coefficients(G1))) + c = prod([p^div(i, 2) for (p, i) in ff], init=one(ktx)) + #d = sqrt(my_coeff(g1, y1, 2)) + d = last(coefficients(split_map_R1(g1))) + success, d = is_square_with_sqrt(d) + @assert success "leading coefficient must be a square" + + phi1 = hom(R1, F1, [F1(x1), F1(evaluate(c, x1), evaluate(d, x1))*y1]) + phiF1 = MapFromFunc(F1, F1, x-> phi1(numerator(x))//phi1(denominator(x))) + phi2 = compose(phi, phiF1) + g2 = numerator(phi1(g1)) + #c = my_coeff(g2, y1, 2) + c = last(coefficients(split_map_R1(g2))) + g2 = divexact(g2, evaluate(c, x1)) + @assert degree(g2, gen(parent, 1)) <= 4 "degree in the first variable is too high" + @assert degree(g2, gen(parent, 1)) >= 3 "degree in the first variable is too low" + return g2, phi2 +end + +@doc raw""" + transform_to_weierstrass(g::MPolyRingElem, x::MPolyRingElem, y::MPolyRingElem, P::Vector{<:RingElem}) + +Transform a bivariate polynomial `g` of the form `y^2 - Q(x)` with `Q(x)` of degree ``≤ 4`` +to Weierstrass form. This returns a pair `(f, trans)` where `trans` is an endomorphism of the +`fraction_field` of `parent(g)` and `f` is the transform. The input `P` must be a rational point +on the curve defined by `g`, i.e. `g(P) == 0`. +""" +function transform_to_weierstrass(g::MPolyRingElem, x::MPolyRingElem, y::MPolyRingElem, P::Vector{<:RingElem}) + R = parent(g) + F = fraction_field(R) + @assert ngens(R) == 2 "input polynomial must be bivariate" + @assert x in gens(R) "second argument must be a variable of the parent of the first" + @assert y in gens(R) "third argument must be a variable of the parent of the first" + # In case of variables in the wrong order, switch and transform the result. + if x == R[2] && y == R[1] + switch = hom(R, R, reverse(gens(R))) + g_trans, trans = transform_to_weierstrass(switch(g), y, x, reverse(P)) + new_trans = MapFromFunc(F, F, f->begin + switch_num = switch(numerator(f)) + switch_den = switch(denominator(f)) + interm_res = trans(F(switch_num))//trans(F(switch_den)) + num = numerator(interm_res) + den = denominator(interm_res) + switch(num)//switch(den) + end + ) + return switch(g_trans), new_trans + end + + g = inv(coeff(g,[0,2]))*g # normalise g + kk = coefficient_ring(R) + kkx, X = polynomial_ring(kk, :x, cached=false) + kkxy, Y = polynomial_ring(kkx, :y, cached=false) + + imgs = [kkxy(X), Y] + split_map = hom(R, kkxy, imgs) + + G = split_map(g) + @assert degree(G) == 2 "input polynomial must be of degree 2 in y" + @assert all(h->degree(h)<=4, coefficients(G)) "input polynomial must be of degree <= 4 in x" + @assert iszero(coefficients(G)[1]) "coefficient of linear term in y must be zero" + @assert isone(coefficients(G)[2]) "leading coefficient in y must be one" + + if length(P) == 3 && isone(P[3]) + P = P[1:2] + end + + + if length(P) == 2 + @assert iszero(evaluate(g, P)) "point does not lie on the hypersurface" + (px, py) = P + else + px = P[1] + end + # assert g.subs({x:px,y:py})==0 + gx = -evaluate(g, [X + px, zero(X)]) + coeff_gx = collect(coefficients(gx)) + A = coeff(gx, 4) + B = coeff(gx, 3) + C = coeff(gx, 2) + D = coeff(gx, 1) + E = coeff(gx, 0) + #E, D, C, B, A = coeff_gx + if length(P)==3 + @req all(h->degree(h)<=3, coefficients(G)) "infinity (0:1:0) is not a point of this hypersurface" + # y^2 = B*x^3+C*x^2+C*x+D + x1 = F(inv(B)*x) + y1 = F(inv(B)*y) + trans = MapFromFunc(F, F, f->evaluate(numerator(f), [x1, y1])//evaluate(denominator(f), [x1, y1])) + f_trans = B^2*trans(F(g)) + result = numerator(B^2*f_trans) + return result, trans + elseif !iszero(E) + b = py + a4, a3, a2, a1, a0 = A,B,C,D,E + A = b + B = a1//(2*b) + C = (4*a2*b^2-a1^2)//(8*b^3) + D = -2*b + + x1 = x//y + y1 = (A*y^2+B*x*y+C*x^2+D*x^3)//y^2 + x1 = x1+px + + # TODO: The following are needed for the inverse. To be added eventually. + # x2 = (y-(A+B*x+C*x^2))//(D*x^2) + # y2 = x2//x + # x2 = evaluate(x2, [x-px, y]) + # y2 = evaluate(y2, [x-px, y]) + + # @assert x == evaluate(x1, [x2, y2]) + # @assert y == evaluate(y1, [x2, y2]) + else + # TODO compute the inverse transformation (x2,y2) + x1 = 1//x + y1 = y//x^2 + g1 = numerator(evaluate(g, [x1, y1])) + c = coeff(g1, [x], [3]) + x1 = evaluate(x1, [-x//c, y//c]) + y1 = evaluate(y1, [-x//c, y//c]) + x1 = x1+px + #@assert x == evaluate(x1, [x2, y2]) + #@assert y == evaluate(y1, [x2, y2]) + end + @assert F === parent(x1) "something is wrong with caching of fraction fields" + # TODO: eventually add the inverse. + trans = MapFromFunc(F, F, f->evaluate(numerator(f), [x1, y1])//evaluate(denominator(f), [x1, y1])) + f_trans = trans(F(g)) + fac = [a[1] for a in factor(numerator(f_trans)) if isone(a[2]) && _is_in_weierstrass_form(a[1])] + isone(length(fac)) || error("transform to weierstrass form did not succeed") + + # normalize the output + result = first(fac) + result = inv(first(coefficients(coeff(result, gens(parent(result)), [3, 0]))))*result + + return result, trans +end + +function _is_in_weierstrass_form(f::MPolyRingElem) + R = parent(f) + @req ngens(R) == 2 "polynomial must be bivariate" + # Helper function + my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) + + (x, y) = gens(R) + f = -inv(my_const(coeff(f, [x, y], [0, 2]))) * f + isone(-coeff(f, [x, y], [0, 2])) || return false + isone(coeff(f, [x, y], [3, 0])) || return false + + a6 = coeff(f, [x,y], [0,0]) + a4 = coeff(f, [x,y], [1,0]) + a2 = coeff(f, [x,y], [2,0]) + a3 = -coeff(f, [x,y], [0,1]) + a1 = -coeff(f, [x,y], [1,1]) + a_invars = [my_const(i) for i in [a1,a2,a3,a4,a6]] + (a1,a2,a3,a4,a6) = a_invars + return f == (-(y^2 + a1*x*y + a3*y) + (x^3 + a2*x^2 + a4*x + a6)) +end + +######################################################################## +# The three conversions from Section 39.1 in +# A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" +# pp. 44--45. +######################################################################## + +function _elliptic_parameter_conversion(X::EllipticSurface, u::VarietyFunctionFieldElem; + case::Symbol=:case1, names=[:x, :y, :t] + ) + @req variety(parent(u)) === weierstrass_model(X)[1] "function field element must live on the weierstrass model of the first argument" + @req length(names) == 3 "need 3 variable names x, y, t" + U = weierstrass_chart(X) + R = ambient_coordinate_ring(U) + x, y, t = gens(R) + loc_eqn = first(gens(modulus(OO(U)))) + E = generic_fiber(X)::EllipticCurve + f = equation(E) + kk = base_ring(X) + kkt_frac_XY = parent(f)::MPolyRing + (xx, yy) = gens(kkt_frac_XY) + kkt_frac = coefficient_ring(kkt_frac_XY)::AbstractAlgebra.Generic.FracField + kkt = base_ring(kkt_frac)::PolyRing + T = first(gens(kkt)) + +# kk = base_ring(U) +# kkt, T = polynomial_ring(kk, :T, cached=false) +# kkt_frac = fraction_field(kkt) +# kkt_frac_XY, (xx, yy) = polynomial_ring(kkt_frac, [:X, :Y], cached=false) + R_to_kkt_frac_XY = hom(R, kkt_frac_XY, [xx, yy, kkt_frac_XY(T)]) + + f_loc = first(gens(modulus(OO(U)))) + @assert f == R_to_kkt_frac_XY(f_loc) && _is_in_weierstrass_form(f) "local equation is not in Weierstrass form" + a = a_invariants(E) + + u_loc = u[U]::AbstractAlgebra.Generic.FracFieldElem # the representative on the Weierstrass chart + + # Set up the ambient_coordinate_ring of the new Weierstrass-chart + kkt2, t2 = polynomial_ring(kk, names[3], cached=false) + kkt2_frac = fraction_field(kkt2) + S, (x2, y2) = polynomial_ring(kkt2_frac, names[1:2], cached=false) + FS = fraction_field(S) + + # Helper function + my_const(u::MPolyRingElem) = is_zero(u) ? zero(coefficient_ring(parent(u))) : first(coefficients(u)) + + # We verify the assumptions made on p. 44 of + # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" + # for the first case considered there. + @assert all(x->isone(denominator(x)), a) "local equation does not have the correct form" + a = numerator.(a) + @assert iszero(a[1]) "local equation does not have the correct form" + @assert degree(a[2]) <= 4 "local equation does not have the correct form" + @assert iszero(a[3]) "local equation does not have the correct form" + @assert degree(a[4]) <= 8 "local equation does not have the correct form" + @assert degree(a[5]) <= 12 "local equation does not have the correct form" # This is really a₆ in the notation of the paper, a₅ does not exist. + # reduce fraction + u_frac = R_to_kkt_frac_XY(numerator(u_loc))//R_to_kkt_frac_XY(denominator(u_loc)) + u_num = numerator(u_frac) + u_den = denominator(u_frac) + if case == :case1 + # D = 2O + u_poly = u_num*inv(u_den) # Will throw if the latter is not a unit + # Extract a(t) and b(t) as in the notation of the paper + a_t = my_const(coeff(u_poly, [xx, yy], [0, 0])) + b_t = my_const(coeff(u_poly, [xx, yy], [1, 0])) + + a_t = evaluate(a_t, x2) + b_t = evaluate(b_t, x2) + phi = hom(R, FS, FS.([(t2 - a_t)//b_t, y2, x2])) + f_trans = phi(f_loc) + return numerator(f_trans), phi + elseif case == :old + # D = O + P + @assert degree(u_num, 2) == 1 && degree(u_num, 1) <= 1 "numerator does not have the correct degree" + @assert degree(u_den, 1) == 1 && degree(u_den, 2) == 0 "denominator does not have the correct degree" + + # We expect a form as on p. 44, l. -4 + denom_unit = my_const(coeff(u_den, [xx, yy], [1, 0])) + x0 = -inv(denom_unit)*my_const(coeff(u_den, [xx, yy], [0, 0])) + b_t = inv(denom_unit)*my_const(coeff(u_num, [xx, yy], [0, 1])) + u_num = u_num - denom_unit * b_t * yy + a_t = inv(denom_unit)*my_const(coeff(u_num, [xx, yy], [1, 0])) + u_num = u_num - denom_unit * a_t * (xx - x0) + @assert is_constant(u_num) "numerator is not in the correct form" + y0 = my_const(coeff(u_num, [xx, yy], [0, 0])) * inv(denom_unit * b_t) + + @assert a_t + b_t*(yy + y0)//(xx - x0) == u_frac "decomposition failed" + # We have + # + # y ↦ (u - a_t) * (x - x₀) / b_t - y₀ = (t₂ - a_t(x₂)) * (y₂ - x₀(x₂)) / b_t(x₂) - y₀(x₂) + # x ↦ y₂ + # t ↦ x₂ + phi = hom(R, FS, FS.([y2, (t2 - evaluate(a_t, x2)) * (y2 - evaluate(x0, x2)) // evaluate(b_t, x2) - evaluate(y0, x2), x2])) + f_trans = phi(f_loc) + eqn1 = numerator(f_trans) + # According to + # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" + # p. 45, l. 1 we expect the following cancelation to be possible: + divisor_num = evaluate(numerator(x0), x2) + divisor_den = evaluate(denominator(x0), x2) + divisor = divisor_den * y2 - divisor_num + success, eqn1 = divides(eqn1, divisor) # This division must only be possible in the ring K(x2)[y2]. + # Hence, multiplying by the denominator `divisor_den` is + # merely an educated guess. + @assert success "division failed" + return eqn1, phi + elseif case == :case3 + # D = O + T + + @assert u_den == xx "elliptic parameter was not brought to the correct form" + @assert degree(u_num, 1) <= 1 && degree(u_num, 2) <= 1 "numerator does not have the correct degrees" + a_t = my_const(coeff(u_num, [xx, yy], [1, 0])) + b_t = my_const(coeff(u_num, [xx, yy], [0, 1])) + + # New Weierstrass equation is of the form + # + # x^2 = h(t, u) + # + # so y₂ = x, x₂ = t, and t₂ = u. + # + # We have u = a_t + b_t * y/x ⇒ y = (u - a_t) * x / b_t = (t₂ - a_t(x₂)) * y₂ / b_t(x₂) + phi = hom(R, FS, FS.([y2, (t2 - evaluate(a_t, x2)) * y2 // evaluate(b_t, x2), x2])) + f_trans = phi(f_loc) + eqn1 = numerator(f_trans) + # According to + # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" + # p. 45, l. 15 we expect the following cancelation to be possible: + success, eqn1 = divides(eqn1, y2) + @assert success "equation did not come out in the anticipated form" + return eqn1, phi + elseif case == :case2 + # D = O + P + @assert degree(u_num, 2) == 1 && degree(u_num, 1) <= 1 "numerator does not have the correct degree" + @assert degree(u_den, 1) == 1 && degree(u_den, 2) <= 1 "denominator does not have the correct degree" + + # u = (ax + by + c)/(a'x + b'y + c') + an = my_const(coeff(u_num, [xx, yy], [1, 0])) + bn = my_const(coeff(u_num, [xx, yy], [0, 1])) + cn = my_const(coeff(u_num, [xx, yy], [0, 0])) + + ad = my_const(coeff(u_den, [xx, yy], [1, 0])) + bd = my_const(coeff(u_den, [xx, yy], [0, 1])) + cd = my_const(coeff(u_den, [xx, yy], [0, 0])) + + @assert (an*xx+bn*yy+cn)//(ad*xx+bd*yy+cd) == u_frac "decomposition failed" + + + v = solve(matrix(parent(an), 2, 2, [-an, bn,-ad, bd]), matrix(parent(an), 2, 1, [cn, cd]); side=:right) + x0 = v[1,1] + y0 = v[2,1] + @assert evaluate(f_loc,[x0,y0,gen(parent(x0))])==0 + + ad = evaluate(ad,x2) + an = evaluate(an,x2) + bd = evaluate(bd,x2) + bn = evaluate(bn,x2) + cn = evaluate(cn,x2) + cd = evaluate(cd,x2) + #x0 = evaluate(x0,x2) + #y0 = evaluate(y0,x2) + + + imgy = -FS(((ad*t2 - an )*y2 + (cd*t2 -cn)) //(bd*t2 -bn)) + + # We have + # + # y ↦ -((ad u - an )x + (cd u -cn)) // (bd*u -bn) + # x ↦ y₂ + # t ↦ x₂ + phi = hom(R, FS, [y2, imgy, x2]) + f_trans = phi(f_loc) + eqn1 = numerator(f_trans) + # According to + # A. Kumar: "Elliptic Fibrations on a generic Jacobian Kummer surface" + # p. 45, l. 1 we expect the following cancellation to be possible: + divisor_num = evaluate(numerator(x0), x2) + divisor_den = evaluate(denominator(x0), x2) + divisor = divisor_den * y2 - divisor_num + success, eqn1 = divides(eqn1, divisor) # This division must only be possible in the ring K(x2)[y2]. + # Hence, multiplying by the denominator `divisor_den` is + # merely an educated guess. + @assert success "division failed" + return eqn1, phi + else + error("case not recognized") + end +end + + +# Given a bivariate polynomial over a univariate function field, +# normalize the associated elliptic curve so that the usual constructor +# for elliptic surfaces digests it, and then return it, together with the +# transformation on the algebraic side. +# +# The transformation is a morphism from the fraction field of the +# parent of g to the fraction field of the `ambient_coordinate_ring` +# of the `weierstrass_chart` of the resulting surface. +function _elliptic_surface_with_trafo(g::MPolyRingElem{<:AbstractAlgebra.Generic.FracFieldElem}; minimize::Bool=true) + x, y = gens(parent(g)) + E = elliptic_curve(g, x, y) + kkt = base_field(E) + kk = coefficient_ring(base_ring(kkt)) + + FFt, t = rational_function_field(kk, :t) + + # The following three commands won't work unless we convert to a rational_function_field + EE = base_change(x->evaluate(x, t), E) + if minimize + EE = tates_algorithm_global(EE) + EE, _ = short_weierstrass_model(EE) + EE, _ = integral_model(EE) + end + + # ...and back. + E2 = base_change(x->evaluate(x, gen(kkt)), EE) + + @assert is_isomorphic(E, E2) + a, b, _ = rational_maps(isomorphism(E2, E)) + + eq_E = equation(E) + eq_E2 = equation(E2) + + h = evaluate(eq_E, [a, b]) + @assert divides(h, eq_E2)[1] + + cod = parent(a)::MPolyRing + + #phi = hom(R, cod, cod.([a, b])) + #Phi = extend_domain_to_fraction_field(phi) + + result = elliptic_surface(E2, 2) + W = weierstrass_chart(result) + R = ambient_coordinate_ring(W) + FR = fraction_field(R) + + help_map = hom(cod, FR, t->evaluate(t, FR(R[3])), FR.([R[1], R[2]])) + A = help_map(a) + B = help_map(b) + + res_map = hom(parent(g), FR, t->evaluate(t, FR(R[3])), [A, B]) + return result, extend_domain_to_fraction_field(res_map) +end + + + +# TODO: Instead return an abelian group A and two maps. +# algebraic_lattice -> A +# A -> MWL= E(k(t)) +function _mordell_weil_group(X) + N = algebraic_lattice(X)[3] + V = ambient_space(N) + t = nrows(trivial_lattice(X)[2]) + Triv = lattice(V, identity_matrix(QQ,dim(V))[1:t,:]) + return torsion_quadratic_module(N, Triv;modulus=1, modulus_qf=1, check=false) +end + diff --git a/src/AlgebraicGeometry/Surfaces/EllipticSurface/Types.jl b/src/AlgebraicGeometry/Surfaces/EllipticSurface/Types.jl new file mode 100644 index 000000000000..07caee1d56b0 --- /dev/null +++ b/src/AlgebraicGeometry/Surfaces/EllipticSurface/Types.jl @@ -0,0 +1,97 @@ +@doc raw""" + EllipticSurface{BaseField<:Field, BaseCurveFieldType} <: AbsCoveredScheme{BaseField} + +A relatively minimal elliptic surface defined as follows. + +A genus ``1``-fibration is a proper map +```math +\pi \colon X \to C +``` +from a smooth projective surface ``X`` to a smooth projective curve ``C`` whose generic fiber is a curve of (arithmetic) genus ``1``. + +The fibration is relatively minimal if its fibers do not contain any ``(-1)``-curves. +We call the fibration elliptic if it is relatively minimal and comes equipped with a section ``\sigma_0\colon \mathbb{P}^1 \to X``. +This turns the generic fiber of ``\pi`` into an elliptic curve ``E/k(C)`` where +``k(C)`` is the function field of the curve ``C``. + +We further require that ``\pi`` has at least one singular fiber and that the base field ``k`` is perfect. + +For now functionality is restricted to ``C = \mathbb{P}^1``. + +This datatype stores a subgroup of the Mordell-Weil group ``E(k(C))``. +It is referred to as the Mordell-Weil subgroup of `X`. +""" +@attributes mutable struct EllipticSurface{BaseField<:Field, BaseCurveFieldType} <: AbsCoveredSurface{BaseField} + Y::CoveredScheme{BaseField} # the underlying_scheme + E::EllipticCurve{BaseCurveFieldType} + MWL::Vector{EllipticCurvePoint{BaseCurveFieldType}} # basis for the mordell weil group + MWLtors::Vector{EllipticCurvePoint{BaseCurveFieldType}} # torsion sections + Weierstrasschart::AbsAffineScheme + Weierstrassmodel::CoveredScheme + inc_Weierstrass::CoveredClosedEmbedding # inclusion of the weierstrass chart in its ambient projective bundle + inc_Y::CoveredClosedEmbedding # inclusion of Y in its ambient blown up projective bundle + euler_characteristic::Int + resolution_strategy::Symbol + # the following are temporary until we have a dedicated type for + # iterated blow ups + blowup::AbsCoveredSchemeMorphism + blowups::Vector{<:AbsCoveredSchemeMorphism} + # exceptionals not used for now + ambient_blowups::Vector{<:BlowupMorphism} + ambient_exceptionals::Vector{<:EffectiveCartierDivisor} + fibration::AbsCoveredSchemeMorphism # the projection to IP^1 + fibration_weierstrass_model::AbsCoveredSchemeMorphism # the projection from the Weierstrass model + + function EllipticSurface( + generic_fiber::EllipticCurve{F}, + euler_characteristic::Int, + mwl_basis::Vector{<:EllipticCurvePoint}; + resolution_strategy::Symbol=:iterative + ) where F + B = typeof(coefficient_ring(base_ring(base_field(generic_fiber)))) + S = new{B,F}() + S.E = generic_fiber + S.MWL = mwl_basis + S.euler_characteristic = euler_characteristic + set_attribute!(S, :is_irreducible=>true) + set_attribute!(S, :is_reduced=>true) + set_attribute!(S, :is_integral=>true) + set_attribute!(S, :is_equidimensional=>true) + S.resolution_strategy = resolution_strategy + return S + end +end + +@doc raw""" + EllipticSurfaceSection <: AbsWeilDivisor + +A section of an elliptic fibration represented as a Weil divisor. +""" +@attributes mutable struct EllipticSurfaceSection{ + CoveredSchemeType<:AbsCoveredScheme, + CoefficientRingType<:AbstractAlgebra.Ring, + CoefficientRingElemType<:AbstractAlgebra.RingElem + } <: AbsWeilDivisor{CoveredSchemeType, CoefficientRingType} + D::WeilDivisor{CoveredSchemeType, CoefficientRingType, CoefficientRingElemType} + P::EllipticCurvePoint + + function EllipticSurfaceSection(X::EllipticSurface, P::EllipticCurvePoint; coefficient_ring::Ring=ZZ) + @vprint :EllipticSurface 3 "Computing a section from a point on the generic fiber\n" + weierstrass_contraction(X) # trigger required computations + PX = _section_on_weierstrass_ambient_space(X, P) + for f in X.ambient_blowups + PX = strict_transform(f , PX) + end + PY = pullback(X.inc_Y, PX) + set_attribute!(PY, :name, string("section: (",P[1]," : ",P[2]," : ",P[3],")")) + set_attribute!(PY, :_self_intersection, -euler_characteristic(X)) + W = WeilDivisor(PY, check=false) + set_attribute!(W, :is_prime=>true) + I = first(components(W)) + set_attribute!(I, :is_prime=>true) + return new{typeof(X), typeof(coefficient_ring), elem_type(coefficient_ring)}(W, P) + end +end + +underlying_divisor(D::EllipticSurfaceSection) = D.D +rational_point(D::EllipticSurfaceSection) = D.P diff --git a/src/deprecations.jl b/src/deprecations.jl index 64e8188595e0..86c6106e18a2 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -144,6 +144,7 @@ Base.@deprecate_binding in_linear_system is_in_linear_system @deprecate scheme(W::CartierDivisor) ambient_scheme(W) @deprecate scheme(W::EffectiveCartierDivisor) ambient_scheme(W) +@deprecate mordell_weil_lattice(X::EllipticSurface) mordell_weil_sublattice(X) @deprecate minimal_generating_set(G::GAPGroup) minimal_size_generating_set(G) @deprecate has_minimal_generating_set(G::GAPGroup) has_minimal_size_generating_set(G) @deprecate set_minimal_generating_set(G::GAPGroup, v) set_minimal_size_generating_set(G, v) diff --git a/src/exports.jl b/src/exports.jl index 4f855fc39f9c..ba5bcb41ecad 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -49,6 +49,7 @@ export AlgebraicCycle export AutomorphismGroup export AutomorphismGroupElem export BettiTable +export BlowupMorphism export BorcherdsCtx export CartierDivisor export ClosedEmbedding @@ -66,6 +67,7 @@ export DirectSumSheaf export Directed export Edge export EffectiveCartierDivisor +export EllipticSurface export EmptyScheme export FPGroup export FPGroupElem @@ -233,6 +235,7 @@ export affine_space export alexander_dual export algebraic_cycle export algebraic_ideal +export algebraic_lattice export algebraic_matrix export algebraic_matroid export algebraic_pluecker_vector @@ -277,8 +280,8 @@ export as_dictionary export as_gset export ascending_compositions export associahedron -export associated_primes export associated_points +export associated_primes export atlas_description export atlas_group export atlas_irrationality @@ -529,6 +532,8 @@ export elementary_abelian_group export elementary_symmetric export elements export eliminate +export elliptic_parameter +export elliptic_surface export embedding export embedding_orthogonal_group export epimorphism_from_free_group @@ -538,6 +543,7 @@ export equidimensional_hull export equidimensional_hull_radical export euler_characteristic export euler_phi +export exceptional_divisor export expand export explicit_zonotope export exponent, has_exponent, set_exponent @@ -563,8 +569,10 @@ export fat_ideal export fat_scheme export feasible_region export fglm +export fiber_components export fiber_product export fibonacci +export fibration_type export filtrate export find_morphism export find_morphisms @@ -611,6 +619,7 @@ export generalized_jordan_block export generalized_jordan_form export generating_system export generator_matrix +export generic_fiber export generic_fraction export generic_fractions export generic_section @@ -719,7 +728,6 @@ export image_in_Oq export images export img_gens export immaculate_line_bundles -export is_in_linear_system export incidence_matrix export inclusion_morphism export independent_sets @@ -837,6 +845,7 @@ export is_groebner_basis export is_homogeneous export is_identity_map export is_immaculate +export is_in_linear_system export is_injective export is_inner_automorphism export is_integral @@ -1075,6 +1084,7 @@ export monomial_basis export monomial_ordering export monomials export monomials_of_degree +export mordell_weil_sublattice export mori_cone export morphism export morphism_from_cox_variety @@ -1335,6 +1345,7 @@ export reduce_with_quotients_and_unit export reduced_characteristic_polynomial export reduced_groebner_basis export reduced_scheme +export reducible_fibers export register_morphism! export regular_120_cell export regular_24_cell @@ -1403,6 +1414,7 @@ export schur_polynomial export secondary_cone export secondary_invariants export secondary_polytope +export section export sectional_genus export semi_invariants export semidirect_product @@ -1415,6 +1427,7 @@ export set_conjugate! export set_coordinate_names export set_coordinate_names_of_torus export set_grading +export set_mordell_weil_basis! export set_name! export set_ordering export set_power! @@ -1531,6 +1544,7 @@ export torusinvariant_weil_divisor_group export total_degree export total_space export transform +export transform_to_weierstrass export transitive_group export transitive_group_identification, has_transitive_group_identification export transitivity @@ -1539,6 +1553,7 @@ export transportation_polytope export trivial_character export trivial_divisor export trivial_divisor_class +export trivial_lattice export trivial_line_bundle export trivial_morphism export trivial_subgroup, has_trivial_subgroup, set_trivial_subgroup @@ -1602,6 +1617,10 @@ export wedge export wedge_generator_decompose_function export wedge_multiplication_map export wedge_pure_function +export weierstrass_chart +export weierstrass_chart_on_minimal_model +export weierstrass_contraction +export weierstrass_model export weight export weight_cone export weight_ordering @@ -1614,5 +1633,6 @@ export witt_index export wreath_product export write_as_full export young_tableau +export zero_section export zonotope export zonotope_vertices_fukuda_matrix diff --git a/test/AlgebraicGeometry/Schemes/elliptic_surface.jl b/test/AlgebraicGeometry/Schemes/EllipticSurface.jl similarity index 99% rename from test/AlgebraicGeometry/Schemes/elliptic_surface.jl rename to test/AlgebraicGeometry/Schemes/EllipticSurface.jl index 1d38d185653f..2860d531759e 100644 --- a/test/AlgebraicGeometry/Schemes/elliptic_surface.jl +++ b/test/AlgebraicGeometry/Schemes/EllipticSurface.jl @@ -50,7 +50,7 @@ @test length(Oscar.mordell_weil_torsion(X)) == 1 alg = algebraic_lattice(X) @test det(alg[3]) == -192 - @test det(mordell_weil_lattice(X)) == 3 + @test det(mordell_weil_sublattice(X)) == 3 X1 = elliptic_surface(short_weierstrass_model(E)[1],2) Oscar.isomorphism_from_generic_fibers(X,X1) diff --git a/test/runtests.jl b/test/runtests.jl index 6cfd97ea74c1..8e3fc850a0ae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -113,7 +113,7 @@ test_large = [ "experimental/GModule/test/runtests.jl", "experimental/LieAlgebras/test/LieAlgebraModule-test.jl", "test/Modules/ModulesGraded.jl", - "test/AlgebraicGeometry/Schemes/elliptic_surface.jl", + "test/AlgebraicGeometry/Schemes/EllipticSurface.jl", ] test_book = [ "test/book/test.jl", From fb767f2294d2f0781b5253e538426298e49c1efd Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 15 Nov 2024 14:07:00 +0100 Subject: [PATCH 69/84] bib: add a bunch of DOIs, minor corrections (#4317) DOIs added via script from . The DOIs were then checked with and in a few cases further adjustments were made to the bib data. --- docs/oscar_references.bib | 83 ++++++++++++++++--------- docs/src/Groups/tom.md | 2 +- src/Combinatorics/Matroids/ChowRings.jl | 4 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index d7c1881064a4..723a35327d53 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -34,7 +34,7 @@ @Article{AG10 number = {1}, pages = {3--24}, year = {2010}, - eprint = {0810.1148} + doi = {10.1070/SM2010v201n01ABEH004063} } @InProceedings{AGK96, @@ -155,6 +155,7 @@ @InProceedings{BDLP19 booktitle = {MEGA 2019 - International Conference on Effective Methods in Algebraic Geometry}, address = {Madrid, Spain}, year = {2019}, + doi = {10.1016/j.jsc.2020.07.007}, archiveprefix = {HAL}, eprint = {hal-02912148} } @@ -206,13 +207,17 @@ @InProceedings{BES-E-D21 location = {Virtual Event, Russian Federation} } -@Misc{BES19, +@Article{BES23, author = {Backman, Spencer and Eur, Christopher and Simpson, Connor}, title = {Simplicial generation of Chow rings of matroids}, - year = {2019}, - eprint = {1905.07114}, - archiveprefix = {arXiv}, - primaryclass = {math.CO} + journal = {JEMS}, + fjournal = {Journal of the European Mathematical Society}, + volume = {26}, + number = {11}, + pages = {4491--4535}, + year = {2023}, + month = jun, + doi = {10.4171/jems/1350} } @Book{BGV03, @@ -249,13 +254,15 @@ @Article{BH23 doi = {10.1017/fms.2023.50} } -@Misc{BHMPW20, +@Article{BHMPW22, author = {Braden, Tom and Huh, June and Matherne, Jacob P. and Proudfoot, Nicholas and Wang, Botong}, title = {A semi-small decomposition of the Chow ring of a matroid}, - year = {2020}, - eprint = {2002.03341}, - archiveprefix = {arXiv}, - primaryclass = {math.AG} + journal = {Advances in Mathematics}, + volume = {409}, + pages = {108646}, + year = {2022}, + month = nov, + doi = {10.1016/j.aim.2022.108646} } @Book{BHPV-D-V04, @@ -267,6 +274,7 @@ @Book{BHPV-D-V04 publisher = {Berlin: Springer}, edition = {2nd enlarged ed.}, year = {2004}, + doi = {10.1007/978-3-642-57739-0}, fseries = {Ergebnisse der Mathematik und ihrer Grenzgebiete. 3. Folge}, language = {English}, zbmath = {2008523} @@ -386,7 +394,8 @@ @Book{Ben93 volume = {190}, address = {Cambridge}, publisher = {Cambridge University Press}, - year = {1993} + year = {1993}, + doi = {10.1017/CBO9780511565809} } @MastersThesis{Bhm99, @@ -431,14 +440,15 @@ @Article{Bis96 doi = {10.1112/S0025579300011773} } -@Book{Bur55, +@Book{Bur11, author = {Burnside, W.}, title = {Theory of groups of finite order}, mrnumber = {69818}, note = {2d ed}, publisher = {Dover Publications, Inc., New York}, pages = {xxiv+512}, - year = {1955} + year = {1911}, + doi = {10.1017/CBO9781139237253} } @Book{C-MLS20, @@ -655,6 +665,7 @@ @InCollection{DE02 publisher = {Berlin: Springer}, pages = {215--249}, year = {2002}, + doi = {10.1007/978-3-662-04851-1_9}, language = {English}, zbmath = {1693054} } @@ -764,7 +775,8 @@ @Article{DK17 fjournal = {Journal of Algebra}, volume = {472}, pages = {546--572}, - year = {2017} + year = {2017}, + doi = {10.1016/j.jalgebra.2016.10.042} } @Book{DL06, @@ -825,6 +837,7 @@ @Book{DSS09 volume = {39}, publisher = {Basel: Birkhäuser}, year = {2009}, + doi = {10.1007/978-3-7643-8905-5}, fseries = {Oberwolfach Seminars}, language = {English}, zbmath = {5303649} @@ -1023,6 +1036,7 @@ @InProceedings{FLINT publisher = {Springer-Verlag}, pages = {88--91}, year = {2010}, + doi = {10.1007/978-3-642-15582-6_18}, location = {Kobe, Japan}, numpages = {4} } @@ -1072,7 +1086,8 @@ @Book{Ful97 note = {With applications to representation theory and geometry}, publisher = {Cambridge University Press, Cambridge}, pages = {x+260}, - year = {1997} + year = {1997}, + doi = {10.1017/CBO9780511626241} } @Book{Ful98, @@ -1109,7 +1124,8 @@ @InProceedings{GHJ16 address = {Cham}, publisher = {Springer International Publishing}, pages = {403--410}, - year = {2016} + year = {2016}, + doi = {10.1007/978-3-319-42432-3_50} } @Article{GIR96, @@ -1155,7 +1171,8 @@ @InProceedings{GK14 booktitle = {Proceedings of the fifteenth ACM conference on Economics and computation}, publisher = {Association for Computing Machinery, New York}, pages = {259--276}, - year = {2014} + year = {2014}, + doi = {10.1145/2600057.2602883} } @Book{GLS07, @@ -1440,7 +1457,8 @@ @Article{JKS22 number = {4}, publisher = {SIAM}, pages = {711--739}, - year = {2022} + year = {2022}, + doi = {10.1137/21M1441286} } @Article{JLLT22, @@ -1544,7 +1562,8 @@ @Book{Jos21 volume = {219}, address = {Providence, RI}, publisher = {American Mathematical Society}, - year = {2021} + year = {2021}, + doi = {10.1090/gsm/219} } @Article{Jow11, @@ -1638,7 +1657,8 @@ @InCollection{KS99 volume = {173}, publisher = {Birkhäuser, Basel}, pages = {267--285}, - year = {1999} + year = {1999}, + doi = {10.1007/978-3-0348-8716-8_17} } @Article{Kah10, @@ -1714,6 +1734,7 @@ @Book{Kol13 note = {With a collaboration of Sándor Kovács}, publisher = {Cambridge University Press}, year = {2013}, + doi = {10.1017/CBO9781139547895}, location = {Cambridge} } @@ -1796,7 +1817,8 @@ @Book{Loo84 volume = {77}, publisher = {Cambridge University Press, Cambridge}, pages = {xi+200}, - year = {1984} + year = {1984}, + doi = {10.1017/CBO9780511662720} } @Misc{MNP24, @@ -1856,7 +1878,8 @@ @Book{MS15 volume = {161}, publisher = {Providence, RI: American Mathematical Society (AMS)}, pages = {xii + 363}, - year = {2015} + year = {2015}, + doi = {10.1090/gsm/161} } @Book{MS21, @@ -2034,7 +2057,8 @@ @Article{Pos09 number = {6}, publisher = {OUP}, pages = {1026--1106}, - year = {2009} + year = {2009}, + doi = {10.1093/imrn/rnn153} } @Article{Pos18, @@ -2070,13 +2094,14 @@ @Article{RR10 doi = {10.1063/1.3501135} } -@Article{RSS03, +@InBook{RSS03, author = {Rote, Günter and Santos, Francisco and Streinu, Ileana}, title = {Expansive motions and the polytope of pointed pseudo-triangulations}, - journal = {Discrete and Computational Geometry: The Goodman-Pollack Festschrift}, + booktitle = {Discrete and Computational Geometry}, publisher = {Springer}, pages = {699--736}, - year = {2003} + year = {2003}, + doi = {10.1007/978-3-642-55566-4_33} } @Article{Rin13, @@ -2100,6 +2125,7 @@ @Book{SS03 volume = {24}, publisher = {Oxford University Press}, year = {2003}, + doi = {10.1093/oso/9780198509424.001.0001}, fseries = {Oxford Lecture Series in Mathematics and its Applications} } @@ -2226,7 +2252,8 @@ @Article{Sta79 volume = {1}, number = {3}, pages = {475--511}, - year = {1979} + year = {1979}, + doi = {10.1090/S0273-0979-1979-14597-X} } @Misc{Stacks, diff --git a/docs/src/Groups/tom.md b/docs/src/Groups/tom.md index fb04e7274c40..05f64e78bfe2 100644 --- a/docs/src/Groups/tom.md +++ b/docs/src/Groups/tom.md @@ -6,7 +6,7 @@ DocTestSetup = Oscar.doctestsetup() # Tables of Marks The concept of a *Table of Marks* was introduced by W. Burnside in his book -Theory of Groups of Finite Order [Bur55](@cite). +Theory of Groups of Finite Order [Bur11](@cite). Therefore a table of marks is sometimes called a *Burnside matrix*. The table of marks of a finite group ``G`` is a matrix whose rows and columns are labelled by the conjugacy classes of subgroups of ``G`` and where for diff --git a/src/Combinatorics/Matroids/ChowRings.jl b/src/Combinatorics/Matroids/ChowRings.jl index 6e98d0cec084..dbcefb19380c 100644 --- a/src/Combinatorics/Matroids/ChowRings.jl +++ b/src/Combinatorics/Matroids/ChowRings.jl @@ -3,7 +3,7 @@ Return the Chow ring of a matroid, optionally also with the simplicial generators and the polynomial ring. -See [AHK18](@cite) and [BES19](@cite). +See [AHK18](@cite) and [BES23](@cite). # Examples The following computes the Chow ring of the Fano matroid. @@ -141,7 +141,7 @@ end @doc raw""" augmented_chow_ring(M::Matroid) -Return an augmented Chow ring of a matroid. As described in [BHMPW20](@cite). +Return an augmented Chow ring of a matroid. As described in [BHMPW22](@cite). # Examples ```jldoctest From 56c8f1f42fae7aa22a85107689189d085c3198fd Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Fri, 15 Nov 2024 17:35:21 +0100 Subject: [PATCH 70/84] add missing doctest markers (#4321) --- experimental/SetPartitions/src/SetPartition.jl | 1 + src/Groups/pcgroup.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/experimental/SetPartitions/src/SetPartition.jl b/experimental/SetPartitions/src/SetPartition.jl index dab1433dc8d7..3bb6a44ec548 100644 --- a/experimental/SetPartitions/src/SetPartition.jl +++ b/experimental/SetPartitions/src/SetPartition.jl @@ -84,6 +84,7 @@ julia> lower_points(set_partition([2, 4], [4, 99])) 2-element Vector{Int64}: 2 3 +``` """ function lower_points(p::SetPartition) return p.lower_points diff --git a/src/Groups/pcgroup.jl b/src/Groups/pcgroup.jl index 56b231a7404b..db534e1f14e1 100644 --- a/src/Groups/pcgroup.jl +++ b/src/Groups/pcgroup.jl @@ -174,6 +174,7 @@ julia> get_relative_orders(c) 2-element Vector{ZZRingElem}: 2 0 +``` """ function get_relative_orders(c::Collector{T}) where T <: IntegerUnion return c.relorders From 14fc59cc0318018e76c5748d0fb2034481f20623 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Fri, 15 Nov 2024 19:52:37 +0100 Subject: [PATCH 71/84] make `isomorphism(PcGroup, A)` for infinite abelian `A` work (#4319) The documentation claimed that this is not supported, but in fact GAP's pcp groups can deal with this case. The code looks horrible, due to the fact that GAP supports the decomposition of elements in abelian groups w.r.t. independent generators only for special such generating sets. (This happens for abelian permutation groups.) Note that this is generic code. Perhaps we should add special methods for the case that we ask for a `FPGroup` or a `PcGroup`, then also the functions for computing (pre)images can be more efficient. --- src/Groups/homomorphisms.jl | 13 ++++++++----- test/Groups/homomorphisms.jl | 25 +++++++++++-------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Groups/homomorphisms.jl b/src/Groups/homomorphisms.jl index 6b1424491325..acac1f65be8a 100644 --- a/src/Groups/homomorphisms.jl +++ b/src/Groups/homomorphisms.jl @@ -711,7 +711,7 @@ end isomorphism(::Type{T}, A::FinGenAbGroup) where T <: Union{GAPGroup, FinGenAbGroup} Return an isomorphism from `A` to a group of type `T`. -An exception is thrown if no such isomorphism exists or if `A` is not finite. +An exception is thrown if no such isomorphism exists. """ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup # Known isomorphisms are cached in the attribute `:isomorphisms`. @@ -729,15 +729,16 @@ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup A_to_A2 = inv(A2_to_A) # Create an isomorphic GAP group whose `GAPWrap.GeneratorsOfGroup` # consists of independent elements of the orders in `exponents`. - # (We cannot guarantee that these generators form a pcgs in the case - # `T == PcGroup`, hence we cannot call `abelian_group(T, exponents)`.) if T == PcGroup + # We cannot guarantee that these generators form a pcgs in the case + # `T == PcGroup`, hence we cannot call `abelian_group(T, exponents)`. if 0 in exponents GapG = GAP.Globals.AbelianPcpGroup(length(exponents), GapObj(exponents; recursive = true)) + G = PcGroup(GapG) else GapG = GAP.Globals.AbelianGroup(GAP.Globals.IsPcGroup, GapObj(exponents; recursive = true)) + G = PcGroup(GAP.Globals.SubgroupNC(GapG, GAP.Globals.FamilyPcgs(GapG))) end - G = PcGroup(GAP.Globals.SubgroupNC(GapG, GAP.Globals.FamilyPcgs(GapG))) else G = abelian_group(T, exponents) GapG = GapObj(G) @@ -771,7 +772,9 @@ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup Ggens = newGgens end gensindep = GAP.Globals.IndependentGeneratorsOfAbelianGroup(GapG)::GapObj - Aindep = abelian_group(ZZRingElem[GAPWrap.Order(g) for g in gensindep]) + orders = [GAPWrap.Order(g) for g in gensindep] + exps = map(x -> x == GAP.Globals.infinity ? ZZRingElem(0) : ZZRingElem(x), orders) + Aindep = abelian_group(exps) imgs = [Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(GapG, a)) for a in Ggens] A2_to_Aindep = hom(A2, Aindep, elem_type(Aindep)[Aindep(e) for e in imgs]) Aindep_to_A = compose(inv(A2_to_Aindep), A2_to_A) diff --git a/test/Groups/homomorphisms.jl b/test/Groups/homomorphisms.jl index 1889233e1202..1b5e891d8694 100644 --- a/test/Groups/homomorphisms.jl +++ b/test/Groups/homomorphisms.jl @@ -265,14 +265,10 @@ end end @testset "Finite FinGenAbGroup to GAPGroup" begin -# @testset for Agens in [Int[], [2, 4, 8], [2, 3, 4], [2, 12], -#T problem with GAP's `AbelianGroup`; -#T see https://github.com/gap-system/gap/issues/5430 - @testset for Agens in [[2, 4, 8], [2, 3, 4], [2, 12], + @testset for Agens in [Int[], [2, 4, 8], [2, 3, 4], [2, 12], [1, 6], matrix(ZZ, 2, 2, [2, 3, 2, 6])] A = abelian_group(Agens) -# for T in [FPGroup, PcGroup, PermGroup] - for T in [FPGroup, SubPcGroup, PermGroup] + for T in [FPGroup, PcGroup, SubPcGroup, PermGroup] iso = @inferred isomorphism(T, A) for x in gens(A), y in gens(A) z = x+y @@ -284,14 +280,15 @@ end end @testset "Infinite FinGenAbGroup to GAPGroup" begin - Agens = matrix(ZZ, 2, 2, [2, 3, 0, 0]) - A = abelian_group(Agens) - for T in [FPGroup] - iso = @inferred isomorphism(T, A) - for x in gens(A), y in gens(A) - z = x+y - @test iso(x) * iso(y) == iso(z) - @test all(a -> preimage(iso, iso(a)) == a, [x, y, z]) + @testset for Agens in [matrix(ZZ, 2, 2, [2, 3, 0, 0]), [6, 0]] + A = abelian_group(Agens) + for T in [FPGroup, PcGroup] + iso = @inferred isomorphism(T, A) + for x in gens(A), y in gens(A) + z = x+y + @test iso(x) * iso(y) == iso(z) + @test all(a -> preimage(iso, iso(a)) == a, [x, y, z]) + end end end end From ed67b2fcfe6debd8eb8d95a2218f4b686f5277cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 15 Nov 2024 19:54:27 +0100 Subject: [PATCH 72/84] Fix casing in `show(::PBWAlgebra)` and friends (#4315) * Overhaul `show(::PBWAlgebra)`, fix casing issues * Overhaul `show(::PBWAlgQuo)`, fix casing issues * Overhaul `show(::PBWAlgOppositeMap)`, fix casing issues * Adapt doctests --- .../PBWAlgebras/ideals.md | 4 +- .../PBWAlgebras/quotients.md | 2 +- .../ExteriorAlgebra/src/ExteriorAlgebra.jl | 15 +++- src/Rings/PBWAlgebra.jl | 69 ++++++++++--------- src/Rings/PBWAlgebraQuo.jl | 39 ++++------- 5 files changed, 65 insertions(+), 64 deletions(-) diff --git a/docs/src/NoncommutativeAlgebra/PBWAlgebras/ideals.md b/docs/src/NoncommutativeAlgebra/PBWAlgebras/ideals.md index 6ac2f6e4d4a2..c4f67c8fba42 100644 --- a/docs/src/NoncommutativeAlgebra/PBWAlgebras/ideals.md +++ b/docs/src/NoncommutativeAlgebra/PBWAlgebras/ideals.md @@ -35,13 +35,13 @@ If `I` is an ideal of a PBW-algebra `A`, then ```jldoctest julia> D, (x, y, dx, dy) = weyl_algebra(QQ, ["x", "y"]) -(Weyl-algebra over Rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) +(Weyl-algebra over rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) julia> I = left_ideal(D, [x, dx]) left_ideal(x, dx) julia> base_ring(I) -Weyl-algebra over Rational field in variables (x, y) +Weyl-algebra over rational field in variables (x, y) julia> gens(I) 2-element Vector{PBWAlgElem{QQFieldElem, Singular.n_Q}}: diff --git a/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md b/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md index 9d59afdcaafe..46de0d53ece3 100644 --- a/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md +++ b/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md @@ -67,7 +67,7 @@ julia> I = two_sided_ideal(A, [x^2, y^2, z^2]); julia> Q, q = quo(A, I); julia> base_ring(Q) -PBW-algebra over Rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z +PBW-algebra over rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z julia> modulus(Q) two_sided_ideal(x^2, y^2, z^2) diff --git a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl index 17433aec1d35..d9e115feb27f 100644 --- a/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl +++ b/experimental/ExteriorAlgebra/src/ExteriorAlgebra.jl @@ -84,15 +84,26 @@ function exterior_algebra(K::Ring, varnames::Vector{Symbol}) ExtAlg_singular = Singular.create_ring_from_singular_ring(SINGULAR_PTR) # Create Quotient ring with special implementation: ExtAlg, _ = quo(PBW, I; special_impl=ExtAlg_singular) # 2nd result is a QuoMap, apparently not needed - return ExtAlg, gens(ExtAlg) + generators = gens(ExtAlg) else ExtAlg, QuoMap = quo(PBW, I) - return ExtAlg, QuoMap.(PBW_indets) + generators = QuoMap.(PBW_indets) end + set_attribute!(ExtAlg, :show, show_exterior_algebra) + return ExtAlg, generators end AbstractAlgebra.@varnames_interface exterior_algebra(K::Ring, varnames) macros = :no +function show_exterior_algebra(io::IO, E::PBWAlgQuo) + x = symbols(E) + io = pretty(io) + print(io, "Exterior algebra over ", Lowercase(), coefficient_ring(E)) + print(io, " in (") + join(io, x, ", ") + print(io, ")") +end + # # BUGS/DEFICIENCIES (2023-02-13): # # (1) Computations with elements DO NOT AUTOMATICALLY REDUCE # # modulo the squares of the generators. diff --git a/src/Rings/PBWAlgebra.jl b/src/Rings/PBWAlgebra.jl index 95456c792c24..b692649288a9 100644 --- a/src/Rings/PBWAlgebra.jl +++ b/src/Rings/PBWAlgebra.jl @@ -1,4 +1,3 @@ -# Use attribute :is_weyl_algebra to permit better printing (see expressify, below) @attributes mutable struct PBWAlgRing{T, S} <: NCRing sring::Singular.PluralRing{S} relations::Singular.smatrix{Singular.spoly{S}} @@ -85,28 +84,31 @@ end @enable_all_show_via_expressify PBWAlgElem -function expressify(a::PBWAlgRing; context = nothing) +function show(io::IO, a::PBWAlgRing) + @show_name(io, a) + @show_special(io, a) x = symbols(a) n = length(x) - # Next if stmt handles special printing for Weyl algebras - if get_attribute(a, :is_weyl_algebra) === :true - return Expr(:sequence, Expr(:text, "Weyl-algebra over "), - expressify(coefficient_ring(a); context=context), - Expr(:text, " in variables ("), - Expr(:series, first(x,div(n,2))...), - Expr(:text, ")")) - end - rel = [Expr(:call, :(==), Expr(:call, :*, x[j], x[i]), expressify(a.relations[i,j])) + io = pretty(io) + rel = [AbstractAlgebra.PrettyPrinting.canonicalize(Expr(:call, :(==), Expr(:call, :*, x[j], x[i]), expressify(a.relations[i,j]))) for i in 1:n-1 for j in i+1:n] - return Expr(:sequence, Expr(:text, "PBW-algebra over "), - expressify(coefficient_ring(a); context=context), - Expr(:text, " in "), - Expr(:series, x...), - Expr(:text, " with relations "), - Expr(:series, rel...)) + print(io, LowercaseOff(), "PBW-algebra over ", Lowercase(), coefficient_ring(a)) + print(io, " in ") + join(io, x, ", ") + print(io, " with relations ") + AbstractAlgebra.show_obj(io, MIME("text/plain"), Expr(:series, rel...)) end -@enable_all_show_via_expressify PBWAlgRing +# handles special printing for Weyl algebras +function show_weyl_algebra(io::IO, a::PBWAlgRing) + x = symbols(a) + n = length(x) + io = pretty(io) + print(io, LowercaseOff(), "Weyl-algebra over ", Lowercase(), coefficient_ring(a)) + print(io, " in variables (") + join(io, first(x, div(n, 2)), ", ") + print(io, ")") +end #### AA prefix here because these all use the ordering in the parent @@ -428,7 +430,7 @@ julia> L = [x*y, x*z, y*z + 1]; julia> REL = strictly_upper_triangular_matrix(L); julia> A, (x, y, z) = pbw_algebra(R, REL, deglex(gens(R))) -(PBW-algebra over Rational field in x, y, z with relations y*x = x*y, z*x = x*z, z*y = y*z + 1, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) +(PBW-algebra over rational field in x, y, z with relations y*x = x*y, z*x = x*z, z*y = y*z + 1, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) ``` """ function pbw_algebra(r::MPolyRing{T}, rel, ord::MonomialOrdering; check::Bool = true) where T @@ -482,7 +484,8 @@ function weyl_algebra(K::Ring, xs::Vector{Symbol}, dxs::Vector{Symbol}) r, v = polynomial_ring(K, vcat(xs, dxs); cached = false) rel = elem_type(r)[v[i]*v[j] + (j == i + n) for i in 1:2*n-1 for j in i+1:2*n] R,vars = pbw_algebra(r, strictly_upper_triangular_matrix(rel), default_ordering(r); check = false) - set_attribute!(R, :is_weyl_algebra, :true) # to activate special printing for Weyl algebras + set_attribute!(R, :is_weyl_algebra, :true) + set_attribute!(R, :show, show_weyl_algebra) # to activate special printing for Weyl algebras return (R,vars) end @@ -504,7 +507,7 @@ The generators of the returned algebra print according to the entries of `xs`. S # Examples ```jldoctest julia> D, (x, y, dx, dy) = weyl_algebra(QQ, [:x, :y]) -(Weyl-algebra over Rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) +(Weyl-algebra over rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) julia> dx*x x*dx + 1 @@ -519,13 +522,11 @@ end #### -function expressify(a::PBWAlgOppositeMap; context = nothing) - return Expr(:sequence, Expr(:text, "Map to opposite of "), - expressify(a.source; context=context)) +function Base.show(io::IO, a::PBWAlgOppositeMap) + io = pretty(io) + print(io, "Map to opposite of ", Lowercase(), a.source) end -@enable_all_show_via_expressify PBWAlgOppositeMap - function _opposite(a::PBWAlgRing{T, S}) where {T, S} if !isdefined(a, :opposite) ptr = Singular.libSingular.rOpposite(a.sring.ptr) @@ -553,15 +554,15 @@ Return the opposite algebra of `A`. # Examples ```jldoctest julia> D, (x, y, dx, dy) = weyl_algebra(QQ, [:x, :y]) -(Weyl-algebra over Rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) +(Weyl-algebra over rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) julia> Dop, opp = opposite_algebra(D); julia> Dop -PBW-algebra over Rational field in dy, dx, y, x with relations dx*dy = dy*dx, y*dy = dy*y + 1, x*dy = dy*x, y*dx = dx*y, x*dx = dx*x + 1, x*y = y*x +PBW-algebra over rational field in dy, dx, y, x with relations dx*dy = dy*dx, y*dy = dy*y + 1, x*dy = dy*x, y*dx = dx*y, x*dx = dx*x + 1, x*y = y*x julia> opp -Map to opposite of Weyl-algebra over Rational field in variables (x, y) +Map to opposite of Weyl-algebra over rational field in variables (x, y) julia> opp(dx*x) dx*x + 1 @@ -644,7 +645,7 @@ julia> L = [x*y, x*z, y*z + 1]; julia> REL = strictly_upper_triangular_matrix(L); julia> A, (x, y, z) = pbw_algebra(R, REL, deglex(gens(R))) -(PBW-algebra over Rational field in x, y, z with relations y*x = x*y, z*x = x*z, z*y = y*z + 1, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) +(PBW-algebra over rational field in x, y, z with relations y*x = x*y, z*x = x*z, z*y = y*z + 1, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) julia> I = left_ideal(A, [x^2*y^2, x*z+y*z]) left_ideal(x^2*y^2, x*z + y*z) @@ -748,7 +749,7 @@ Return `true` if `I` is the zero ideal, `false` otherwise. # Examples ```jldoctest julia> D, (x, y, dx, dy) = weyl_algebra(QQ, [:x, :y]) -(Weyl-algebra over Rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) +(Weyl-algebra over rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) julia> I = left_ideal(D, [x, dx]) left_ideal(x, dx) @@ -778,7 +779,7 @@ Return `true` if `I` is generated by `1`, `false` otherwise. # Examples ```jldoctest julia> D, (x, y, dx, dy) = weyl_algebra(QQ, [:x, :y]) -(Weyl-algebra over Rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) +(Weyl-algebra over rational field in variables (x, y), PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, dx, dy]) julia> I = left_ideal(D, [x, dx]) left_ideal(x, dx) @@ -1275,7 +1276,7 @@ julia> L = [x*y-z, x*z+2*x, x*a, y*z-2*y, y*a, z*a]; julia> REL = strictly_upper_triangular_matrix(L); julia> A, (x, y, z, a) = pbw_algebra(R, REL, deglex(gens(R))) -(PBW-algebra over Rational field in x, y, z, a with relations y*x = x*y - z, z*x = x*z + 2*x, a*x = x*a, z*y = y*z - 2*y, a*y = y*a, a*z = z*a, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z, a]) +(PBW-algebra over rational field in x, y, z, a with relations y*x = x*y - z, z*x = x*z + 2*x, a*x = x*a, z*y = y*z - 2*y, a*y = y*a, a*z = z*a, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z, a]) julia> f = 4*x*y+z^2-2*z-a; @@ -1300,7 +1301,7 @@ julia> L = [p*q+q^2]; julia> REL = strictly_upper_triangular_matrix(L); julia> A, (p, q) = pbw_algebra(R, REL, lex(gens(R))) -(PBW-algebra over Rational field in p, q with relations q*p = p*q + q^2, PBWAlgElem{QQFieldElem, Singular.n_Q}[p, q]) +(PBW-algebra over rational field in p, q with relations q*p = p*q + q^2, PBWAlgElem{QQFieldElem, Singular.n_Q}[p, q]) julia> I = left_ideal(A, [p, q]) left_ideal(p, q) diff --git a/src/Rings/PBWAlgebraQuo.jl b/src/Rings/PBWAlgebraQuo.jl index 60082c3a8369..8f2635c4b851 100644 --- a/src/Rings/PBWAlgebraQuo.jl +++ b/src/Rings/PBWAlgebraQuo.jl @@ -33,16 +33,20 @@ export PBWAlgQuo, PBWAlgQuoElem ###### @attributes ### DID NOT WORK -- alternative solution found (via has_special_impl, see ExteriorAlgebra.jl) -mutable struct PBWAlgQuo{T, S} <: NCRing +@attributes mutable struct PBWAlgQuo{T, S} <: NCRing I::PBWAlgIdeal{0, T, S} sring::Singular.PluralRing{S} # For ExtAlg this is the Singular impl; o/w same as I.basering.sring + + function PBWAlgQuo(I::PBWAlgIdeal{0, T, S}, sring::Singular.PluralRing{S}) where {T, S} + return new{T, S}(I, sring) + end end # For backward compatibility: ctor with 1 arg: # uses "default" arith impl -- namely that from basering! function PBWAlgQuo(I::PBWAlgIdeal{0, T, S}) where {T, S} - return PBWAlgQuo{T, S}(I, I.basering.sring) + return PBWAlgQuo(I, I.basering.sring) end @@ -93,27 +97,12 @@ end @enable_all_show_via_expressify PBWAlgQuoElem -function expressify(Q::PBWAlgQuo; context = nothing) # what about new sring data-field ??? - ## special printing if Q is an exterior algebra -###### if get_attribute(Q, :is_exterior_algebra) === :true - if has_special_impl(Q) - a = Q.I.basering - x = symbols(a) - n = length(x) - return Expr(:sequence, Expr(:text, "Exterior algebra over "), - expressify(coefficient_ring(a); context=context), - Expr(:text, " in ("), - Expr(:series, x...), - Expr(:text, ")")) - - end - # General case (not exterior algebra) - return Expr(:call, :/, expressify(Q.I.basering; context = nothing), - expressify(Q.I; context = nothing)) +function Base.show(io::IO, Q::PBWAlgQuo) + @show_name(io, Q) + @show_special(io, Q) + print(io, "(", base_ring(Q), ")/", modulus(Q)) end -@enable_all_show_via_expressify PBWAlgQuo - #### function number_of_generators(Q::PBWAlgQuo) @@ -216,7 +205,7 @@ julia> L = [-x*y, -x*z, -y*z]; julia> REL = strictly_upper_triangular_matrix(L); julia> A, (x, y, z) = pbw_algebra(R, REL, deglex(gens(R))) -(PBW-algebra over Rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) +(PBW-algebra over rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z, PBWAlgElem{QQFieldElem, Singular.n_Q}[x, y, z]) julia> I = two_sided_ideal(A, [x^2, y^2, z^2]) two_sided_ideal(x^2, y^2, z^2) @@ -224,12 +213,12 @@ two_sided_ideal(x^2, y^2, z^2) julia> Q, q = quo(A, I); julia> Q -(PBW-algebra over Rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z)/two_sided_ideal(x^2, y^2, z^2) +(PBW-algebra over rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z)/two_sided_ideal(x^2, y^2, z^2) julia> q Map defined by a julia-function with inverse - from pBW-algebra over Rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z - to (PBW-algebra over Rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z)/two_sided_ideal(x^2, y^2, z^2) + from PBW-algebra over rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z + to (PBW-algebra over rational field in x, y, z with relations y*x = -x*y, z*x = -x*z, z*y = -y*z)/two_sided_ideal(x^2, y^2, z^2) ``` !!! note From 99f0f7b3283231a27fcd0ce82110a348613d3996 Mon Sep 17 00:00:00 2001 From: KilianBruns <122679435+KilianBruns@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:08:10 +0100 Subject: [PATCH 73/84] Hasse-Schmidt derivatives 2.0 (#4272) * Hasse-Schmidt derivatives for MPolyRingElem (and related stuff in MPolyAnyRing) --- docs/oscar_references.bib | 49 ++++++ experimental/HasseSchmidt/src/HasseSchmidt.jl | 160 ++++++++++++++++++ experimental/HasseSchmidt/test/runtests.jl | 117 +++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 experimental/HasseSchmidt/src/HasseSchmidt.jl create mode 100644 experimental/HasseSchmidt/test/runtests.jl diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index 723a35327d53..9f4ea57cbe04 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -657,6 +657,18 @@ @Article{Cor21 doi = {10.1007/s00029-021-00679-6} } +@Book{Cut04, + author = {Cutkosky, Steven Dale}, + title = {Resolution of singularities}, + mrnumber = {2058431}, + series = {Graduate Studies in Mathematics}, + volume = {63}, + publisher = {American Mathematical Society, Providence, RI}, + pages = {viii+186}, + year = {2004}, + doi = {10.1090/gsm/063} +} + @InCollection{DE02, author = {Decker, Wolfram and Eisenbud, David}, title = {Sheaf algorithms using the exterior algebra}, @@ -1041,6 +1053,19 @@ @InProceedings{FLINT numpages = {4} } +@Article{FRS21, + author = {Frühbis-Krüger, Anne and Ristau, Lukas and Schober, Bernd}, + title = {Embedded desingularization for arithmetic surfaces---toward a parallel implementation}, + mrnumber = {4273121}, + journal = {Math. Comp.}, + fjournal = {Mathematics of Computation}, + volume = {90}, + number = {330}, + pages = {1957--1997}, + year = {2021}, + doi = {10.1090/mcom/3624} +} + @Article{FY04, author = {Feichtner, Eva Maria and Yuzvinsky, Sergey}, title = {Chow rings of toric varieties defined by atomic lattices}, @@ -1386,6 +1411,30 @@ @Book{Har77 doi = {10.1007/978-1-4757-3849-0} } +@Article{Has37, + author = {Hasse, H.}, + title = {Noch eine Begründung der Theorie der höheren Differentialquotienten in einem algebraischen + Funktionenkörper einer Unbestimmten. (Nach einer brieflichen Mitteilung von F. K. Schmidt in Jena)}, + mrnumber = {1581557}, + journal = {J. Reine Angew. Math.}, + fjournal = {Journal für die Reine und Angewandte Mathematik. [Crelle's Journal]}, + volume = {177}, + pages = {215--237}, + year = {1937}, + doi = {10.1515/crll.1937.177.215} +} + +@Article{Haz12, + author = {Hazewinkel, Michiel}, + title = {Hasse-Schmidt Derivations and the Hopf Algebra of Non-Commutative Symmetric Functions}, + journal = {Axioms}, + volume = {1}, + number = {2}, + pages = {149--154}, + year = {2012}, + doi = {10.3390/axioms1020149} +} + @Article{Hul22, author = {Hulpke, Alexander}, title = {The perfect groups of order up to two million}, diff --git a/experimental/HasseSchmidt/src/HasseSchmidt.jl b/experimental/HasseSchmidt/src/HasseSchmidt.jl new file mode 100644 index 000000000000..4ca17d4d37cb --- /dev/null +++ b/experimental/HasseSchmidt/src/HasseSchmidt.jl @@ -0,0 +1,160 @@ +export hasse_derivatives + +### We consider Hasse-Schmidt derivatives of polynomials as seen in +### +### [FRS21](@cite) Fruehbis-Krueger, Ristau, Schober: 'Embedded desingularization for arithmetic surfaces -- toward a parallel implementation' +### +### This is a special case of a more general definition of a Hasse-Schmidt derivative. These more general and rigorous definitions can be found in the following sources: +### +### [Cut04](@cite) Cutkosky: 'Resolution of Singularities' +### [Haz12](@cite) Michiel Hazewinkel: 'Hasse-Schmidt derivations and the Hopf algebra of noncommutative symmetric functions' +### + +################################################################################ +### HASSE-SCHMIDT derivatives for single polynomials + +@doc raw""" + hasse_derivatives(f::MPolyRingElem) + +Return a list of Hasse-Schmidt derivatives of `f`, each with a multiindex `[a_1, ..., a_n]`, where `a_i` describes the number of times `f` was derived w.r.t. the `i`-th variable. + +Hasse-Schmidt derivatives as seen in [FRS21](@cite). +For more general and rigorous definition see [Cut04](@cite) or [Haz12](@cite). + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(ZZ, ["x", "y"]); + +julia> f = 5*x^2 + 3*y^5; + +julia> hasse_derivatives(f) +8-element Vector{Vector{Any}}: + [[0, 5], 3] + [[0, 4], 15*y] + [[0, 3], 30*y^2] + [[2, 0], 5] + [[0, 2], 30*y^3] + [[1, 0], 10*x] + [[0, 1], 15*y^4] + [[0, 0], 5*x^2 + 3*y^5] +``` +""" +function hasse_derivatives(f::MPolyRingElem) + R = parent(f) + Rtemp, t = polynomial_ring(R, :t => 1:ngens(R)) + F = evaluate(f, gens(R) + t) + return [[degrees(monomial(term, 1)), coeff(term, 1)] for term in terms(F)] +end + +function hasse_derivatives(f::MPolyQuoRingElem) + error("Not implemented. For experts, however, there is an internal function called _hasse_derivatives, which works for elements of type MPolyQuoRingElem") +end + +function hasse_derivatives(f::Oscar.MPolyLocRingElem) + error("Not implemented. For experts, however, there is an internal function called _hasse_derivatives, which works for elements of type Oscar.MPolyLocRingElem") +end + +function hasse_derivatives(f::Oscar.MPolyQuoLocRingElem) + error("Not implemented. For experts, however, there is an internal function called _hasse_derivatives, which works for elements of type Oscar.MPolyQuoLocRingElem") +end + + + + +################################################################################ +### internal functions for expert use + +# MPolyQuoRingElem (internal, expert use only) +@doc raw""" + _hasse_derivatives(f::MPolyQuoRingElem) + +Return a list of Hasse-Schmidt derivatives of lift of `f`, each with a multiindex `[a_1, ..., a_n]`, where `a_i` describes the number of times `f` was derived w.r.t. the `i`-th variable. + +# Examples +```jldoctest +julia> R, (x, y) = polynomial_ring(ZZ, ["x", "y"]); + +julia> I = ideal(R, [x - 1]); + +julia> RQ, phi = quo(R, I); + +julia> f = phi(2*y^4); + +julia> Oscar._hasse_derivatives(f) +5-element Vector{Vector{Any}}: + [[0, 4], 2] + [[0, 3], 8*y] + [[0, 2], 12*y^2] + [[0, 1], 8*y^3] + [[0, 0], 2*y^4] +``` +""" +function _hasse_derivatives(f::MPolyQuoRingElem) + return hasse_derivatives(lift(f)) +end + +# Oscar.MPolyLocRingElem (internal, expert use only) +@doc raw""" + _hasse_derivatives(f::Oscar.MPolyLocRingElem) + +Return a list of Hasse-Schmidt derivatives of numerator of `f`, each with a multiindex `[a_1, ..., a_n]`, where `a_i` describes the number of times `f` was derived w.r.t. the `i`-th variable. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); + +julia> m = ideal(R, [x - 3, y - 2, z + 1]); + +julia> U = complement_of_prime_ideal(m); + +julia> Rloc, phi = localization(R, U); + +julia> f = phi(2*z^5); + +julia> Oscar._hasse_derivatives(f) +6-element Vector{Vector{Any}}: + [[0, 0, 5], 2] + [[0, 0, 4], 10*z] + [[0, 0, 3], 20*z^2] + [[0, 0, 2], 20*z^3] + [[0, 0, 1], 10*z^4] + [[0, 0, 0], 2*z^5] +``` +""" +function _hasse_derivatives(f::Oscar.MPolyLocRingElem) + return hasse_derivatives(numerator(f)) +end + +# Oscar.MPolyQuoLocRingElem (internal, expert use only) +@doc raw""" + _hasse_derivatives(f::Oscar.MPolyQuoLocRingElem) + +Return a list of Hasse-Schmidt derivatives of lifted numerator of `f`, each with a multiindex `[a_1, ..., a_n]`, where `a_i` describes the number of times `f` was derived w.r.t. the `i`-th variable. + +# Examples +```jldoctest +julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); + +julia> I = ideal(R, [x^3 - 1]); + +julia> RQ, phi = quo(R, I); + +julia> p = ideal(R, [z]); + +julia> U = complement_of_prime_ideal(p); + +julia> RQL, iota = localization(RQ, U); + +julia> f = iota(phi(4*y^3)); + +julia> Oscar._hasse_derivatives(f) +4-element Vector{Vector{Any}}: + [[0, 3, 0], 4] + [[0, 2, 0], 12*y] + [[0, 1, 0], 12*y^2] + [[0, 0, 0], 4*y^3] +``` +""" +function _hasse_derivatives(f::Oscar.MPolyQuoLocRingElem) + return hasse_derivatives(lifted_numerator(f)) +end diff --git a/experimental/HasseSchmidt/test/runtests.jl b/experimental/HasseSchmidt/test/runtests.jl new file mode 100644 index 000000000000..574a01ff5728 --- /dev/null +++ b/experimental/HasseSchmidt/test/runtests.jl @@ -0,0 +1,117 @@ +@testset "hasse_derivatives" begin + R, (x, y) = polynomial_ring(ZZ, ["x", "y"]); + + result_a1 = [ [[3, 0], 1], + [[2, 0], 3*x], + [[1, 0], 3*x^2], + [[0, 0], x^3]] + @test result_a1 == hasse_derivatives(x^3) + + result_a2 = [ [[0, 5], 3], + [[0, 4], 15*y], + [[0, 3], 30*y^2], + [[2, 0], 5], + [[0, 2], 30*y^3], + [[1, 0], 10*x], + [[0, 1], 15*y^4], + [[0, 0], 5*x^2 + 3*y^5]] + @test result_a2 == hasse_derivatives(5*x^2 + 3*y^5) + + result_a3 = [ [[2, 3], 1], + [[2, 2], 3*y], + [[1, 3], 2*x], + [[2, 1], 3*y^2], + [[1, 2], 6*x*y], + [[0, 3], x^2], + [[2, 0], y^3], + [[1, 1], 6*x*y^2], + [[0, 2], 3*x^2*y], + [[1, 0], 2*x*y^3], + [[0, 1], 3*x^2*y^2], + [[0, 0], x^2*y^3]] + @test result_a3 == hasse_derivatives(x^2*y^3) + + result_a4 = [ [[4, 0], 1], + [[3, 0], 4*x], + [[2, 0], 6*x^2], + [[0, 2], 1], + [[1, 0], 4*x^3], + [[0, 1], 2*y], + [[0, 0], x^4 + y^2]] + @test result_a4 == hasse_derivatives(x^4 + y^2) + + result_a5 = [ [[2, 1], 1], + [[1, 2], 1], + [[2, 0], y], + [[1, 1], 2*x + 2*y], + [[0, 2], x], + [[1, 0], 2*x*y + y^2], + [[0, 1], x^2 + 2*x*y], + [[0, 0], x^2*y + x*y^2]] + @test result_a5 == hasse_derivatives(x^2*y + x*y^2) +end + +@testset "hasse_derivatives finite fields" begin + R, (x, y, z) = polynomial_ring(GF(3), ["x", "y", "z"]); + + result_b1 = [ [[2, 0, 0], 1], + [[0, 2, 0], 1], + [[1, 0, 0], 2*x], + [[0, 1, 0], 2*y], + [[0, 0, 0], x^2 + y^2]] + @test result_b1 == hasse_derivatives(x^2 + y^2) + + result_b2 = [ [[0, 0, 6], 1], + [[2, 1, 0], 1], + [[0, 0, 3], 2*z^3], + [[2, 0, 0], y], + [[1, 1, 0], 2*x], + [[1, 0, 0], 2*x*y], + [[0, 1, 0], x^2], + [[0, 0, 0], x^2*y + z^6]] + @test result_b2 == hasse_derivatives(x^2*y + z^6) +end + +@testset "_hasse_derivatives MPolyQuoRingElem" begin + R, (x, y, z) = polynomial_ring(ZZ, ["x", "y", "z"]); + I = ideal(R, [x^2 - 1]); + RQ, _ = quo(R, I); + + result_c1 = [ [[0, 4, 0], 3], + [[0, 3, 0], 12*y], + [[0, 2, 0], 18*y^2], + [[0, 1, 0], 12*y^3], + [[0, 0, 0], 3*y^4]] + @test result_c1 == Oscar._hasse_derivatives(RQ(3y^4)) +end + +@testset "_hasse_derivatives Oscar.MPolyLocRingElem" begin + R, (x, y, z) = polynomial_ring(ZZ, ["x", "y", "z"]); + m = ideal(R, [x, y, z]); # max ideal + U = complement_of_prime_ideal(m); + RL, _ = localization(R, U); + + result_d1 = [ [[3, 0, 0], 5], + [[2, 0, 0], 15*x], + [[1, 0, 0], 15*x^2], + [[0, 0, 0], 5*x^3]] + @test result_d1 == Oscar._hasse_derivatives(RL(5x^3)) +end + +@testset "_hasse_derivatives Oscar.MPolyQuoLocRingElem" begin + R, (x, y, z) = polynomial_ring(ZZ, ["x", "y", "z"]); + I = ideal(R, [x^2 - 1]); + RQ, _ = quo(R, I); + m = ideal(R, [x, y, z]); # max ideal + U = complement_of_prime_ideal(m); + RQL, _ = localization(RQ, U); + + result_e1 = [ [[0, 0, 5], 2], + [[0, 0, 4], 10*z], + [[0, 0, 3], 20*z^2], + [[0, 0, 2], 20*z^3], + [[0, 0, 1], 10*z^4], + [[0, 0, 0], 2*z^5]] + @test result_e1 == Oscar._hasse_derivatives(RQL(2z^5)) +end + From e8f8ecc5039fe49de8b8d6bef111c39aed83a722 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Mon, 18 Nov 2024 11:44:10 +0100 Subject: [PATCH 74/84] CI: check for jldoctest end markers and unknown admonitions (#4325) * CI: check for jldoctest end markers and unknown admonitions * docs: fix two admonitions * add explanation * use !== nothing --- .github/workflows/Docstrings.yml | 29 +++++ etc/check_docstrings.jl | 107 ++++++++++++++++++ .../src/UserFunctions.jl | 4 +- 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/Docstrings.yml create mode 100644 etc/check_docstrings.jl diff --git a/.github/workflows/Docstrings.yml b/.github/workflows/Docstrings.yml new file mode 100644 index 000000000000..c3ae6a2f5780 --- /dev/null +++ b/.github/workflows/Docstrings.yml @@ -0,0 +1,29 @@ +name: docstring test + +on: + push: + branches: + - master + - 'release-*' + pull_request: + workflow_dispatch: + +concurrency: + # group by workflow and ref; the last slightly strange component ensures that for pull + # requests, we limit to 1 concurrent job, but for the master branch we don't + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref != 'refs/heads/master' || github.run_number }} + # Cancel intermediate builds, but only if it is a pull request build. + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + check-docstrings: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + - uses: julia-actions/cache@v2 + - name: Build package + uses: julia-actions/julia-buildpkg@v1 + - name: 'Check issues in docstrings' + run: | + julia --project=. -e 'using Oscar; include("etc/check_docstrings.jl")' diff --git a/etc/check_docstrings.jl b/etc/check_docstrings.jl new file mode 100644 index 000000000000..72caa4d49495 --- /dev/null +++ b/etc/check_docstrings.jl @@ -0,0 +1,107 @@ +# this can be run from the command line with: +# > julia --project=. -e 'using Oscar; include("etc/check_docstrings.jl")' +# +# it is also possible to test more packages at once (as long as Main.X exists) +# > julia --project=. -e 'using Oscar; include("etc/check_docstrings.jl")' Nemo Singular Hecke Polymake GAP Oscar +# +# note: the @main function requires julia 1.11 or newer + + +using Markdown + +admonition_types = string.((:note, :info, :tip, :danger, :warning, :compat, :todo, :details)) + +# this might not catch all broken jldoctests but seems to work for now +function has_broken_doctest(md::Markdown.MD) + todo = copy(md.content) + while !isempty(todo) + elem = popfirst!(todo) + # nested + if elem isa Markdown.MD + append!(todo, elem.content) + else + # proper docstrings are in a Markdown.Code block + if elem isa Markdown.Paragraph + for block in elem.content + # unterminated jldoctests seem to end up inside some string in a Paragraph block + if contains(string(block),"```jldoctest") + return "Unterminated jldoctest: $(string(block))" + end + end + elseif elem isa Markdown.Admonition + if !(elem.category in admonition_types) + return "Unknown admonition category: $(string(elem.category))" + end + end + end + end + return nothing +end + +function find_docstr_src(docs) + locs = [] + res = docs.meta[:results] + for i in 1:length(docs.content) + msg = has_broken_doctest(docs.content[i]) + if msg !== nothing + file = res[i].data[:path] + line = res[i].data[:linenumber] + mod = res[i].data[:module] + push!(locs, (mod, file, line, msg)) + end + end + return locs +end + +function get_broken_docstrings(m::Module) + allpairs = collect(zip(Iterators.repeated(m), names(m; all=true, imported=false))); + broken = [] + locs = [] + done = [m] + + while !isempty(allpairs) + (src, name) = popfirst!(allpairs) + memberobj = try + getfield(src,name) + catch + nothing + end + if memberobj isa Module + if !(memberobj in done) + push!(done, memberobj) + # we only want to consider one pkg + # but note that we still seem to pick up some reexported names + # even though imported=false and the check below + if (pkgdir(memberobj) == pkgdir(m)) + newnames = names(memberobj; all=true, imported=false) + filter!(x->!((memberobj, x) in allpairs || x in done), newnames) + append!(allpairs, collect(zip(Iterators.repeated(memberobj), newnames))) + end + end + elseif !isnothing(memberobj) + docs = Base.Docs.doc(memberobj) + if docs isa Markdown.MD + if has_broken_doctest(docs) !== nothing && !(memberobj in broken) + loc = find_docstr_src(docs) + push!(broken, memberobj) + # we could filter out docs from other packages via startswith(x, pkgdir(m)) + append!(locs, loc) + end + end + end + end + return locs +end + +function (@main)(args) + modnames = length(args) > 0 ? args : ["Oscar"] + mod = getfield.(Ref(Main), Symbol.(modnames)) + locs = reduce(vcat, get_broken_docstrings.(mod)) + isempty(locs) && exit(0) + for (mod, file, line, msg) in locs + dir = joinpath(pkgdir(mod),"") # add trailing / + relfile = replace(file, dir => "") + println("::error file=$relfile,line=$line,title=$(string(mod))::$msg") + end + exit(-1) +end diff --git a/experimental/BasisLieHighestWeight/src/UserFunctions.jl b/experimental/BasisLieHighestWeight/src/UserFunctions.jl index 8a69177f94dc..d9bb839a79ae 100644 --- a/experimental/BasisLieHighestWeight/src/UserFunctions.jl +++ b/experimental/BasisLieHighestWeight/src/UserFunctions.jl @@ -406,7 +406,7 @@ with highest weight `highest_weight` for a simple Lie algebra $L$ of type `type` Furthermore, for each degree, return the monomials that are not contained in the Minkowski sum of the bases of the lower degrees. -!!! warn +!!! warning Currently, this function expects $-w_0(\lambda)$ instead of $\lambda$ as the `highest_weight` input. This might change in a minor release. @@ -515,7 +515,7 @@ with highest weight `highest_weight` for a simple Lie algebra $L$ of type `type` Furthermore, for each degree, return the monomials that are not contained in the Minkowski sum of the bases of the lower degrees. -!!! warn +!!! warning Currently, this function expects $-w_0(\lambda)$ instead of $\lambda$ as the `highest_weight` input. This might change in a minor release. From bd570426aae70ae3faa0527d5525f8ff20e4ab4b Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Mon, 18 Nov 2024 14:58:09 +0100 Subject: [PATCH 75/84] add action on matrices in row reduced echelon form (#4311) This implements the action of a matrix group on subspaces of its natural module. --- docs/src/Groups/action.md | 3 +++ src/Groups/action.jl | 42 ++++++++++++++++++++++++++++++++++++++- src/exports.jl | 1 + test/Groups/action.jl | 4 ++-- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/src/Groups/action.md b/docs/src/Groups/action.md index 83304a9b5fa9..abf584b3a965 100644 --- a/docs/src/Groups/action.md +++ b/docs/src/Groups/action.md @@ -39,6 +39,9 @@ on_tuples on_sets permuted on_indeterminates +on_lines +on_echelon_form_mats +on_subgroups ``` diff --git a/src/Groups/action.jl b/src/Groups/action.jl index 4499d8ce5dbc..776c4469fc7c 100644 --- a/src/Groups/action.jl +++ b/src/Groups/action.jl @@ -19,7 +19,8 @@ objects, for example wrapped GAP matrices on GAP vectors: julia> g = GL(2,3); julia> m = g[1] -[ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ] +[2 0] +[0 1] julia> v = GapObj(m)[1] GAP: [ Z(3), 0*Z(3) ] @@ -453,6 +454,45 @@ end on_subgroups(x::T, g::GAPGroupElem) where T <: GAPGroup = T(on_subgroups(GapObj(x), g)) + +@doc raw""" + on_echelon_form_mats(m::MatElem{T}, x::MatrixGroupElem) where T <: FinFieldElem + +Return the image of `m` under `x`, +where the action is given by first computing the product `m * x` +and then normalizing the result by computing its reduced row echelon form +with `echelon_form`. + +Identifying `m` with the subspace of the natural module for the group of `x` +that is generated by the rows of `m`, +`on_echelon_form_mats` describes the action on subspaces of this natural module. +Note that for computing the orbit and stabilizer of `m` w.r.t. +`on_echelon_form_mats`, `m` must be in reduced row echelon form. + +# Examples +```jldoctest +julia> n = 3; q = 2; F = GF(q); V = free_module(F, n); + +julia> G = GL(n, F); + +julia> W, embW = sub(V, [gen(V,1), gen(V,3)]) +(Subspace over F with 2 generators and no relations, Hom: W -> V) + +julia> m = matrix(embW) +[1 0 0] +[0 0 1] + +julia> S, _ = stabilizer(G, m, on_echelon_form_mats); order(S) +24 + +julia> orb = orbit(G, on_echelon_form_mats, m); length(orb) +7 +``` +""" +function on_echelon_form_mats(m::MatElem{T}, x::MatrixGroupElem) where T <: FinFieldElem + return echelon_form(m * x) +end + @doc raw""" stabilizer(G::GAPGroup, pnt::Any[, actfun::Function]) diff --git a/src/exports.jl b/src/exports.jl index ba5bcb41ecad..b36c6c42271a 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -1182,6 +1182,7 @@ export objective_function export omega_group export on_indeterminates export on_lines +export on_echelon_form_mats export on_sets export on_sets_sets export on_simplicial_complex diff --git a/test/Groups/action.jl b/test/Groups/action.jl index 30a97cd832b4..a9e595f63a74 100644 --- a/test/Groups/action.jl +++ b/test/Groups/action.jl @@ -43,7 +43,7 @@ end n = 3 F = GF(2) G = general_linear_group(n, F) - V = AbstractAlgebra.Generic.FreeModule(F, n) + V = free_module(F, n) v = gen(V, 1) S = stabilizer(G, v) @test order(S[1]) == 24 @@ -132,7 +132,7 @@ end n = 3 F = GF(5) G = general_linear_group(n, F) - V = AbstractAlgebra.Generic.FreeModule(F, n) + V = free_module(F, n) v = gen(V, 1) v = on_lines(v, one(G)) # make sure that `v` is normalized @test on_lines(2*v, one(G)) == v From 5076ad2e3f17d2f469ba33488c17f35dd10cfd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 18 Nov 2024 19:30:04 +0100 Subject: [PATCH 76/84] BasisLieHighestWeight: Replace GAP uses by Oscar functionality (#4322) * Fix reference test to use `degrevlex` ordering Since this is the default in `basis_lie_highest_weight`. This has not been an issue in the cases that were compared previously. * Rename some vars to contain `_gap` * Change Lie algebra construction from GAP to OSCAR * Move iso calls around * Change cartan matrix from GAP to OSCAR * Change character from GAP to OSCAR * Change operator index calculations from GAP to OSCAR * Change `dim_of_simple_module` from GAP to OSCAR * Use specialized types for roots and weights instead of `Vector{ZZRingElem}`. Only minimal clean-up performed yet. * Use roots as operators instead of `GAP.Obj` * Some cleanup * Rewrite `compute_sub_weights` * Some optimization * Workaround show functions to not change output right now * Excise `LieAlgebraStructure` * Format * Remove AA workaround * Optimize character computations (slightly) * inline `add_known_monomials` * Re-parameterize weightspaces --- Project.toml | 2 +- .../src/BasisLieHighestWeight.jl | 15 +- .../src/BirationalSequence.jl | 53 ++- .../BasisLieHighestWeight/src/LieAlgebras.jl | 103 +----- .../src/MainAlgorithm.jl | 335 +++++++----------- .../src/MonomialBasis.jl | 73 +++- .../src/MonomialOrder.jl | 4 +- .../BasisLieHighestWeight/src/NewMonomial.jl | 9 +- .../src/RootConversion.jl | 9 - .../BasisLieHighestWeight/src/TensorModels.jl | 23 +- .../src/UserFunctions.jl | 64 ++-- .../BasisLieHighestWeight/src/WeylPolytope.jl | 103 ++---- .../BasisLieHighestWeight/test/MBOld.jl | 7 +- .../test/MainAlgorithm-test.jl | 62 ++-- .../test/NewMonomial-test.jl | 13 +- .../test/RootConversion-test.jl | 27 -- .../BasisLieHighestWeight/test/runtests.jl | 1 - experimental/LieAlgebras/src/GapWrapper.jl | 15 +- experimental/LieAlgebras/src/LieAlgebra.jl | 13 + .../LieAlgebras/src/LieAlgebraModule.jl | 41 +++ experimental/LieAlgebras/src/RootSystem.jl | 37 +- 21 files changed, 462 insertions(+), 547 deletions(-) delete mode 100644 experimental/BasisLieHighestWeight/src/RootConversion.jl delete mode 100644 experimental/BasisLieHighestWeight/test/RootConversion-test.jl diff --git a/Project.toml b/Project.toml index df7242405e92..c5b2f30b3504 100644 --- a/Project.toml +++ b/Project.toml @@ -25,7 +25,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" cohomCalg_jll = "5558cf25-a90e-53b0-b813-cadaa3ae7ade" [compat] -AbstractAlgebra = "0.43.7" +AbstractAlgebra = "0.43.11" AlgebraicSolving = "0.8.0" Distributed = "1.6" GAP = "0.12.0" diff --git a/experimental/BasisLieHighestWeight/src/BasisLieHighestWeight.jl b/experimental/BasisLieHighestWeight/src/BasisLieHighestWeight.jl index e43f319d2fe6..85105ae1e52d 100644 --- a/experimental/BasisLieHighestWeight/src/BasisLieHighestWeight.jl +++ b/experimental/BasisLieHighestWeight/src/BasisLieHighestWeight.jl @@ -1,32 +1,30 @@ module BasisLieHighestWeight using ..Oscar -using ..Oscar: GAPWrap using ..Oscar: IntegerUnion using ..Oscar: _is_weighted +using Oscar.LieAlgebras: + _character, + _root_system_type_string, + lie_algebra_simple_module_struct_consts_gap + using AbstractAlgebra.PrettyPrinting import Oscar: dim -import Oscar: dim_of_simple_module import Oscar: monomial_ordering import Oscar: monomials -import Oscar: number_of_positive_roots, n_positive_roots import Base: length # Long-term TODO's: -# - Use Oscar-Lie-Algebra type instead of LieAlgebraStructure # - Test if ZZx should be a graded_polynomial_ring with weights_w as weights # - Maybe export and docstring: -# - get_dim_weightspace -# - orbit_weylgroup # - get_lattice_points_of_weightspace # - convert_lattice_points_to_monomials # - convert_monomials_to_lattice_points # - action_matrices_of_operators # - weights_for_operators -# - Data-Type for weights of Lie-Algebras? Two types, in alpha_i and w_i, conversion is defined in RootConversion # - the list of Minkowski gens contains too many elements, only include those that give us something new include("LieAlgebras.jl") @@ -35,13 +33,13 @@ include("MonomialBasis.jl") include("NewMonomial.jl") include("TensorModels.jl") include("MonomialOrder.jl") -include("RootConversion.jl") include("WeylPolytope.jl") include("MainAlgorithm.jl") include("UserFunctions.jl") export MonomialBasis +export birational_sequence export basis_coordinate_ring_kodaira export basis_coordinate_ring_kodaira_ffl export basis_lie_highest_weight_operators @@ -59,6 +57,7 @@ export BasisLieHighestWeight export MonomialBasis +export birational_sequence export basis_coordinate_ring_kodaira export basis_coordinate_ring_kodaira_ffl export basis_lie_highest_weight_operators diff --git a/experimental/BasisLieHighestWeight/src/BirationalSequence.jl b/experimental/BasisLieHighestWeight/src/BirationalSequence.jl index 0d0de239c830..3a99f411d140 100644 --- a/experimental/BasisLieHighestWeight/src/BirationalSequence.jl +++ b/experimental/BasisLieHighestWeight/src/BirationalSequence.jl @@ -1,12 +1,51 @@ struct BirationalSequence - operators::Vector{GAP.Obj} - operators_vectors::Vector{Vector{Any}} - weights_w::Vector{Vector{ZZRingElem}} - weights_alpha::Vector{Vector{QQFieldElem}} + operator_roots::Vector{RootSpaceElem} + operator_weights::Vector{WeightLatticeElem} + + function BirationalSequence( + operator_roots::Vector{RootSpaceElem}, operator_weights::Vector{WeightLatticeElem} + ) + @req length(operator_roots) == length(operator_weights) "Different lengths" + return new(operator_roots, operator_weights) + end +end + +function birational_sequence( + operator_roots::Vector{RootSpaceElem}, operator_weights::Vector{WeightLatticeElem} +) + return BirationalSequence(operator_roots, operator_weights) +end + +function birational_sequence(operator_roots::Vector{RootSpaceElem}) + return birational_sequence(operator_roots, WeightLatticeElem.(operator_roots)) end -function Base.show(io::IO, birational_sequence::BirationalSequence) +function birational_sequence(operator_weights::Vector{WeightLatticeElem}) + return birational_sequence(RootSpaceElem.(operator_weights), operator_weights) +end + +function Base.show(io::IO, birational_seq::BirationalSequence) println(io, "BirationalSequence") - println(io, "Operators: ", birational_sequence.operators) - print(io, "Weights in alpha_i:", birational_sequence.weights_alpha) + println(io, "Operators: ", operators_as_roots(birational_seq)) + print(io, "Operators as weights:", operators_as_weights(birational_seq)) +end + +function length(birational_seq::BirationalSequence) + return length(birational_seq.operator_roots) +end + +function operators_as_roots(birational_seq::BirationalSequence) + return birational_seq.operator_roots +end + +function operator_as_root(birational_seq::BirationalSequence, i::Int) + return birational_seq.operator_roots[i] +end + +function operators_as_weights(birational_seq::BirationalSequence) + return birational_seq.operator_weights +end + +function operator_as_weight(birational_seq::BirationalSequence, i::Int) + return birational_seq.operator_weights[i] end diff --git a/experimental/BasisLieHighestWeight/src/LieAlgebras.jl b/experimental/BasisLieHighestWeight/src/LieAlgebras.jl index 2f4b5a35ac9c..d624b905c72c 100644 --- a/experimental/BasisLieHighestWeight/src/LieAlgebras.jl +++ b/experimental/BasisLieHighestWeight/src/LieAlgebras.jl @@ -1,93 +1,22 @@ -@attributes mutable struct LieAlgebraStructure - lie_type::Symbol - rank::Int - lie_algebra_gap::GAP.Obj - chevalley_basis::NTuple{3,Vector{GAP.Obj}} - - function LieAlgebraStructure(lie_type::Symbol, rank::Int) - lie_algebra_gap = GAP.Globals.SimpleLieAlgebra( - GAP.Obj(lie_type), rank, GAP.Globals.Rationals - ) - chevalley_basis = NTuple{3,Vector{GAP.Obj}}(GAP.Globals.ChevalleyBasis(lie_algebra_gap)) - return new(lie_type, rank, lie_algebra_gap, chevalley_basis) - end -end - -rank(L::LieAlgebraStructure) = L.rank - -@attr QQMatrix function cartan_matrix(L::LieAlgebraStructure) - R = GAPWrap.RootSystem(L.lie_algebra_gap) - C = matrix(QQ, GAP.Globals.CartanMatrix(R)) - return C -end - -@attr QQMatrix function inv_cartan_matrix(L::LieAlgebraStructure) - return inv(cartan_matrix(L)) -end - -function Base.show(io::IO, L::LieAlgebraStructure) - io = pretty(io) - print(io, LowercaseOff(), "Lie algebra of type $(L.lie_type)$(L.rank)") -end - -function lie_algebra(type::Symbol, rk::Int) - return LieAlgebraStructure(type, rk) -end - -function chevalley_basis_gap(L::LieAlgebraStructure) - return L.chevalley_basis -end - -function cartan_sub_basis(L::LieAlgebraStructure) - return L.chevalley_basis[3] -end - -function root_system_gap(L::LieAlgebraStructure) - return GAPWrap.RootSystem(L.lie_algebra_gap) -end - -function number_of_positive_roots(L::LieAlgebraStructure) - return length(GAP.Globals.PositiveRoots(root_system_gap(L))) -end - -function dim_of_simple_module(T::Type, L::LieAlgebraStructure, hw::Vector{<:IntegerUnion}) - hw_ = Int.(hw) - @req Oscar.LieAlgebras.is_dominant_weight(hw_) "Not a dominant weight." - return T(GAPWrap.DimensionOfHighestWeightModule(L.lie_algebra_gap, GAP.Obj(Int.(hw_)))) -end - -function dim_of_simple_module(L::LieAlgebraStructure, hw::Vector{<:IntegerUnion}) - return dim_of_simple_module(Int, L, hw) -end - -function matrices_of_operators_gap( - L::LieAlgebraStructure, highest_weight::Vector{ZZRingElem}, operators::Vector{GAP.Obj} +function matrices_of_operators( + L::LieAlgebra, highest_weight::WeightLatticeElem, operators::Vector{RootSpaceElem} ) # used in tensor_matrices_of_operators - M = GAP.Globals.HighestWeightModule(L.lie_algebra_gap, GAP.Obj(Int.(highest_weight))) - matrices_of_operators = [ - sparse_matrix(matrix(QQ, GAP.Globals.MatrixOfAction(GAPWrap.Basis(M), o))) for - o in operators + R = root_system(L) + struct_consts = lie_algebra_simple_module_struct_consts_gap(L, highest_weight) + dimV = size(struct_consts, 2) + transformation_matrices = [ + sparse_matrix(coefficient_ring(L), dimV, dimV) for _ in 1:number_of_positive_roots(R) ] - denominators = map(y -> denominator(y[2]), union(union(matrices_of_operators...)...)) - common_denominator = lcm(denominators)# // 1 - matrices_of_operators = - (A -> change_base_ring(ZZ, common_denominator * A)).(matrices_of_operators) - return matrices_of_operators -end + for i in 1:number_of_positive_roots(R), j in 1:dimV + transformation_matrices[i][j] = struct_consts[i + number_of_positive_roots(R), j] # take f_alpha for positive root alpha + end -@doc raw""" - weight(L::LieAlgebraStructure, operator::GAP.Obj) -> Vector{ZZRingElem} + matrices_of_operators = map(operators) do op + fl, i = is_positive_root_with_index(op) + @assert fl + change_base_ring(ZZ, transformation_matrices[i]) + end -Calculate the weight of `operator` w.r.t. the fundamental weights w_i. -""" -function weight(L::LieAlgebraStructure, operator::GAP.Obj) - @req !iszero(operator) "Operators should be non-zero" - basis = GAPWrap.Basis(L.lie_algebra_gap) - basis_ind = GAP.Globals.Position(basis, operator) - denom = GAPWrap.Coefficients(basis, operator)[basis_ind] - return [ - ZZ(GAPWrap.Coefficients(basis, h * operator)[basis_ind]//denom) for - h in cartan_sub_basis(L) - ] + return matrices_of_operators end diff --git a/experimental/BasisLieHighestWeight/src/MainAlgorithm.jl b/experimental/BasisLieHighestWeight/src/MainAlgorithm.jl index cf71d0954bdb..310c4629d834 100644 --- a/experimental/BasisLieHighestWeight/src/MainAlgorithm.jl +++ b/experimental/BasisLieHighestWeight/src/MainAlgorithm.jl @@ -1,7 +1,7 @@ function basis_lie_highest_weight_compute( - L::LieAlgebraStructure, + L::LieAlgebra, highest_weight::Vector{Int}, - operators::Vector{<:GAP.Obj}, # operators are represented by our monomials. x_i is connected to operators[i] + operators::Vector{RootSpaceElem}, # monomial x_i is corresponds to f_operators[i] monomial_ordering_symb::Symbol, ) # Pseudocode: @@ -23,45 +23,35 @@ function basis_lie_highest_weight_compute( # return set_mon # add_by_hand(highest_weight, set_mon) - # add_known_monomials(set_mon) + # add all monomials from set_mon to basis # go through all weightspaces that are not full # add_new_monomials(weightspace, set_mon) # return set_mon - # add_known_monomials(set_mon) - # add all monomials from set_mon to basis - # add_new_monomials(weightspace, set_mon) # calculate monomials with weight in weightspace # go through them one by one in monomial_ordering until basis is full # return set_mon - highest_weight = ZZ.(highest_weight) - # The function precomputes objects that are independent of the highest weight and that can be used in all recursion - # steps. Then it starts the recursion and returns the result. + R = root_system(L) + highest_weight = WeightLatticeElem(R, highest_weight) - weights_w = [weight(L, op) for op in operators] # weights of the operators - weights_alpha = [w_to_alpha(L, weight_w) for weight_w in weights_w] # other root system - - asVec(v) = GAP.gap_to_julia(GAPWrap.ExtRepOfObj(v)) # TODO - birational_sequence = BirationalSequence( - operators, [asVec(v) for v in operators], weights_w, weights_alpha - ) + birational_seq = birational_sequence(operators) ZZx, _ = polynomial_ring(ZZ, length(operators)) # for our monomials - monomial_ordering = get_monomial_ordering(monomial_ordering_symb, ZZx, weights_alpha) + monomial_ordering = get_monomial_ordering(monomial_ordering_symb, ZZx, operators) # save computations from recursions - calc_highest_weight = Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}( - [ZZ(0) for i in 1:rank(L)] => Set([ZZx(1)]) + calc_highest_weight = Dict{WeightLatticeElem,Set{ZZMPolyRingElem}}( + zero(WeightLatticeElem, R) => Set([ZZx(1)]) ) # save all highest weights, for which the Minkowski-sum did not suffice to gain all monomials - no_minkowski = Set{Vector{ZZRingElem}}() + no_minkowski = Set{WeightLatticeElem}() # start recursion over highest_weight monomials = compute_monomials( L, - birational_sequence, + birational_seq, ZZx, highest_weight, monomial_ordering, @@ -69,9 +59,12 @@ function basis_lie_highest_weight_compute( no_minkowski, ) # monomials = sort(collect(monomials); lt=((m1, m2) -> cmp(monomial_ordering, m1, m2) < 0)) - minkowski_gens = sort(collect(no_minkowski); by=(gen -> (sum(gen), reverse(gen)))) + minkowski_gens = sort( + collect(no_minkowski); + by=(gen -> (sum(coefficients(gen)), reverse(Oscar._vec(coefficients(gen))))), + ) # output - mb = MonomialBasis(L, highest_weight, birational_sequence, monomial_ordering, monomials) + mb = MonomialBasis(L, highest_weight, birational_seq, monomial_ordering, monomials) set_attribute!( mb, :algorithm => basis_lie_highest_weight_compute, :minkowski_gens => minkowski_gens ) @@ -79,10 +72,10 @@ function basis_lie_highest_weight_compute( end function basis_coordinate_ring_kodaira_compute( - L::LieAlgebraStructure, + L::LieAlgebra, highest_weight::Vector{Int}, degree::Int, - operators::Vector{<:GAP.Obj}, # operators are represented by our monomials. x_i is connected to operators[i] + operators::Vector{RootSpaceElem}, # monomial x_i is corresponds to f_operators[i] monomial_ordering_symb::Symbol, ) # Pseudocode: @@ -95,28 +88,21 @@ function basis_coordinate_ring_kodaira_compute( # of smaller multiples, the missing monomials @req degree > 0 "Degree must be positive" - highest_weight = ZZ.(highest_weight) - # The function precomputes objects that are independent of the highest weight and that can be used in all recursion - # steps. Then it starts the recursion and returns the result. + R = root_system(L) + highest_weight = WeightLatticeElem(R, highest_weight) - weights_w = [weight(L, op) for op in operators] # weights of the operators - weights_alpha = [w_to_alpha(L, weight_w) for weight_w in weights_w] # other root system - - asVec(v) = GAP.gap_to_julia(GAPWrap.ExtRepOfObj(v)) # TODO - birational_sequence = BirationalSequence( - operators, [asVec(v) for v in operators], weights_w, weights_alpha - ) + birational_seq = birational_sequence(operators) ZZx, _ = polynomial_ring(ZZ, length(operators)) # for our monomials - monomial_ordering = get_monomial_ordering(monomial_ordering_symb, ZZx, weights_alpha) + monomial_ordering = get_monomial_ordering(monomial_ordering_symb, ZZx, operators) # save computations from recursions - calc_highest_weight = Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}( - [ZZ(0) for i in 1:rank(L)] => Set([ZZx(1)]) + calc_highest_weight = Dict{WeightLatticeElem,Set{ZZMPolyRingElem}}( + zero(WeightLatticeElem, R) => Set([ZZx(1)]) ) # save all highest weights, for which the Minkowski-sum did not suffice to gain all monomials - no_minkowski = Set{Vector{ZZRingElem}}() + no_minkowski = Set{WeightLatticeElem}() monomials_k = Set{ZZMPolyRingElem}[] # monomial basis of the module k*highest_weight monomials_new_k = Vector{ZZMPolyRingElem}[] # store the monomials that are not products of basis monomials of smaller degree sizehint!(monomials_k, degree) @@ -143,7 +129,7 @@ function basis_coordinate_ring_kodaira_compute( @vprintln :BasisLieHighestWeight "for $(Int.(i * highest_weight)) we have $(length(monomials_minkowski_sum)) and need $(dim_i) monomials" monomials = compute_monomials( L, - birational_sequence, + birational_seq, ZZx, i * highest_weight, monomial_ordering, @@ -154,11 +140,14 @@ function basis_coordinate_ring_kodaira_compute( @vprintln :BasisLieHighestWeight "for $(Int.(i * highest_weight)) we added $(length(monomials_new)) monomials " # monomials = sort(collect(monomials); lt=((m1, m2) -> cmp(monomial_ordering, m1, m2) < 0)) - minkowski_gens = sort(collect(no_minkowski); by=(gen -> (sum(gen), reverse(gen)))) + minkowski_gens = sort( + collect(no_minkowski); + by=(gen -> (sum(coefficients(gen)), reverse(Oscar._vec(coefficients(gen))))), + ) end mb = MonomialBasis( - L, i * highest_weight, birational_sequence, monomial_ordering, monomials + L, i * highest_weight, birational_seq, monomial_ordering, monomials ) set_attribute!(mb, :algorithm => basis_coordinate_ring_kodaira_compute) monomials_new_sorted = sort( @@ -184,13 +173,13 @@ function basis_coordinate_ring_kodaira_compute( end function compute_monomials( - L::LieAlgebraStructure, - birational_sequence::BirationalSequence, + L::LieAlgebra, + birational_seq::BirationalSequence, ZZx::ZZMPolyRing, - highest_weight::Vector{ZZRingElem}, + highest_weight::WeightLatticeElem, monomial_ordering::MonomialOrdering, - calc_highest_weight::Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}, - no_minkowski::Set{Vector{ZZRingElem}}, + calc_highest_weight::Dict{WeightLatticeElem,Set{ZZMPolyRingElem}}, + no_minkowski::Set{WeightLatticeElem}, ) # This function calculates the monomial basis M_{highest_weight} recursively. The recursion saves all computed # results in calc_highest_weight and we first check, if we already encountered this highest weight in a prior step. @@ -206,41 +195,38 @@ function compute_monomials( # we already computed the highest_weight result in a prior recursion step if haskey(calc_highest_weight, highest_weight) return calc_highest_weight[highest_weight] - elseif highest_weight == [ZZ(0) for i in 1:(L.rank)] # we mathematically know the solution + elseif is_zero(highest_weight) # we mathematically know the solution return Set(ZZx(1)) end # calculation required - # gap_dim is number of monomials that we need to find, i.e. |M_{highest_weight}|. + # dim is number of monomials that we need to find, i.e. |M_{highest_weight}|. # if highest_weight is not a fundamental weight, partition into smaller summands is possible. This is the basecase of # the recursion. - gap_dim = dim_of_simple_module(L, highest_weight) - if is_fundamental(highest_weight) || sum(abs.(highest_weight)) == 0 + dim = dim_of_simple_module(L, highest_weight) + if is_zero(highest_weight) || is_fundamental(highest_weight) push!(no_minkowski, highest_weight) monomials = add_by_hand( - L, birational_sequence, ZZx, highest_weight, monomial_ordering, Set{ZZMPolyRingElem}() + L, birational_seq, ZZx, highest_weight, monomial_ordering, Set{ZZMPolyRingElem}() ) push!(calc_highest_weight, highest_weight => monomials) return monomials else # use Minkowski-Sum for recursion monomials = Set{ZZMPolyRingElem}() - i = 0 - sub_weights_w = compute_sub_weights(highest_weight) - l = length(sub_weights_w) - # go through all partitions lambda_1 + lambda_2 = highest_weight until we have enough monomials or used all - # partitions - while length(monomials) < gap_dim && i < l - i += 1 - lambda_1 = sub_weights_w[i] - lambda_2 = highest_weight .- lambda_1 - - if lambda_1 > lambda_2 - continue - end + sub_weights = sub_weights_proper(highest_weight) + sort!(sub_weights; by=x -> sum(coefficients(x) .^ 2)) + # go through all partitions lambda_1 + lambda_2 = highest_weight until we have enough monomials or used all partitions + for (ind_lambda_1, lambda_1) in enumerate(sub_weights) + length(monomials) >= dim && break + + lambda_2 = highest_weight - lambda_1 + ind_lambda_2 = findfirst(==(lambda_2), sub_weights)::Int + + ind_lambda_1 > ind_lambda_2 && continue mon_lambda_1 = compute_monomials( L, - birational_sequence, + birational_seq, ZZx, lambda_1, monomial_ordering, @@ -249,7 +235,7 @@ function compute_monomials( ) mon_lambda_2 = compute_monomials( L, - birational_sequence, + birational_seq, ZZx, lambda_2, monomial_ordering, @@ -258,15 +244,14 @@ function compute_monomials( ) # Minkowski-sum: M_{lambda_1} + M_{lambda_2} \subseteq M_{highest_weight}, if monomials get identified with # points in ZZ^n - monomials_minkowski_sum = Set([p * q for p in mon_lambda_1 for q in mon_lambda_2]) - union!(monomials, monomials_minkowski_sum) + union!(monomials, (p * q for p in mon_lambda_1 for q in mon_lambda_2)) end # check if we found enough monomials - if length(monomials) < gap_dim + if length(monomials) < dim push!(no_minkowski, highest_weight) monomials = add_by_hand( - L, birational_sequence, ZZx, highest_weight, monomial_ordering, monomials + L, birational_seq, ZZx, highest_weight, monomial_ordering, monomials ) end @@ -275,40 +260,18 @@ function compute_monomials( end end -function add_known_monomials!( - weight_w::Vector{ZZRingElem}, - monomials_in_weightspace::Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}, - matrices_of_operators::Vector{<:SMat{ZZRingElem}}, - space::Dict{Vector{ZZRingElem},<:SMat{QQFieldElem}}, - v0::SRow{ZZRingElem}, -) - # By using the Minkowski-sum, we know that all monomials in monomials_in_weightspace are in our basis. Since we want to - # extend the weightspace with missing monomials, we need to calculate and add the vector of each monomial to our - # basis. - - for mon in monomials_in_weightspace[weight_w] - # calculate the vector vec associated with mon - vec = calc_vec(v0, mon, matrices_of_operators) - - # check if vec extends the basis - if !haskey(space, weight_w) - space[weight_w] = sparse_matrix(QQ) - end - Hecke._add_row_to_rref!(space[weight_w], change_base_ring(QQ, vec)) - end -end - function add_new_monomials!( - L::LieAlgebraStructure, - birational_sequence::BirationalSequence, + L::LieAlgebra, + birational_seq::BirationalSequence, ZZx::ZZMPolyRing, matrices_of_operators::Vector{<:SMat{ZZRingElem}}, monomial_ordering::MonomialOrdering, - weightspaces::Dict{Vector{ZZRingElem},Int}, + weightspaces::Dict{WeightLatticeElem,Int}, dim_weightspace::Int, - weight_w::Vector{ZZRingElem}, - monomials_in_weightspace::Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}, - space::Dict{Vector{ZZRingElem},<:SMat{QQFieldElem}}, + weight_w::WeightLatticeElem, + highest_weight::WeightLatticeElem, + monomials_in_weightspace::Dict{WeightLatticeElem,Set{ZZMPolyRingElem}}, + space::Dict{WeightLatticeElem,<:SMat{QQFieldElem}}, v0::SRow{ZZRingElem}, basis::Set{ZZMPolyRingElem}, zero_coordinates::Vector{Int}, @@ -323,7 +286,8 @@ function add_new_monomials!( poss_mon_in_weightspace = convert_lattice_points_to_monomials( ZZx, get_lattice_points_of_weightspace( - birational_sequence.weights_alpha, w_to_alpha(L, weight_w), zero_coordinates + operators_as_roots(birational_seq), RootSpaceElem(highest_weight - weight_w), + zero_coordinates, ), ) isempty(poss_mon_in_weightspace) && error("The input seems to be invalid.") @@ -333,7 +297,7 @@ function add_new_monomials!( # check which monomials should get added to the basis i = 0 - if weight_w == 0 # check if [0 0 ... 0] already in basis + if highest_weight == weight_w # check if [0 0 ... 0] already in basis i += 1 end number_mon_in_weightspace = length(monomials_in_weightspace[weight_w]) @@ -350,9 +314,9 @@ function add_new_monomials!( for i in 1:(nvars(ZZx) - 1) if !haskey( weightspaces, - sum( + highest_weight - sum( exp * weight for (exp, weight) in - Iterators.drop(zip(degrees(mon), birational_sequence.weights_w), i) + Iterators.drop(zip(degrees(mon), operators_as_weights(birational_seq)), i) ), ) cancel = true @@ -382,10 +346,10 @@ function add_new_monomials!( end function add_by_hand( - L::LieAlgebraStructure, - birational_sequence::BirationalSequence, + L::LieAlgebra, + birational_seq::BirationalSequence, ZZx::ZZMPolyRing, - highest_weight::Vector{ZZRingElem}, + highest_weight::WeightLatticeElem, monomial_ordering::MonomialOrdering, basis::Set{ZZMPolyRingElem}, ) @@ -394,59 +358,63 @@ function add_by_hand( # initialization # matrices g_i for (g_1^a_1 * ... * g_k^a_k)*v + R = root_system(L) matrices_of_operators = tensor_matrices_of_operators( - L, highest_weight, birational_sequence.operators + L, highest_weight, operators_as_roots(birational_seq) ) - space = Dict(ZZ(0) * birational_sequence.weights_w[1] => sparse_matrix(QQ)) # span of basis vectors to keep track of the basis + space = Dict(zero(WeightLatticeElem, R) => sparse_matrix(QQ)) # span of basis vectors to keep track of the basis v0 = sparse_row(ZZ, [(1, 1)]) # starting vector v push!(basis, ZZx(1)) # required monomials of each weightspace - weightspaces = get_dim_weightspace(L, highest_weight) + weightspaces = _character(R, highest_weight) # sort the monomials from the minkowski-sum by their weightspaces - monomials_in_weightspace = Dict{Vector{ZZRingElem},Set{ZZMPolyRingElem}}() + monomials_in_weightspace = Dict{WeightLatticeElem,Set{ZZMPolyRingElem}}() for (weight_w, _) in weightspaces monomials_in_weightspace[weight_w] = Set{ZZMPolyRingElem}() end for mon in basis - weight_w = weight(mon, birational_sequence.weights_w) - push!(monomials_in_weightspace[weight_w], mon) + push!(monomials_in_weightspace[highest_weight - weight(mon, birational_seq)], mon) end # only inspect weightspaces with missing monomials - weights_with_non_full_weightspace = Set{Vector{ZZRingElem}}() + weights_with_non_full_weightspace = Set{WeightLatticeElem}() for (weight_w, dim_weightspace) in weightspaces if length(monomials_in_weightspace[weight_w]) != dim_weightspace push!(weights_with_non_full_weightspace, weight_w) end end - # The weightspaces could be calculated completely indepent (except for - # the caching). This is not implemented, since I used the package Distributed.jl for this, which is not in the - # Oscar dependendencies. But I plan to reimplement this. - # insert known monomials into basis - + # add all images from `monomials_in_weightspace` on `v0` to `space` for weight_w in weights_with_non_full_weightspace - add_known_monomials!( - weight_w, monomials_in_weightspace, matrices_of_operators, space, v0 - ) + for mon in monomials_in_weightspace[weight_w] + # calculate the vector vec associated with mon + vec = calc_vec(v0, mon, matrices_of_operators) + + # check if vec extends the basis + if !haskey(space, weight_w) + space[weight_w] = sparse_matrix(QQ) + end + Hecke._add_row_to_rref!(space[weight_w], change_base_ring(QQ, vec)) + end end # identify coordinates that are trivially zero because of the action on the generator - zero_coordinates = compute_zero_coordinates(birational_sequence, highest_weight) + zero_coordinates = compute_zero_coordinates(birational_seq, highest_weight) # calculate new monomials for weight_w in weights_with_non_full_weightspace dim_weightspace = weightspaces[weight_w] add_new_monomials!( L, - birational_sequence, + birational_seq, ZZx, matrices_of_operators, monomial_ordering, weightspaces, dim_weightspace, weight_w, + highest_weight, monomials_in_weightspace, space, v0, @@ -457,48 +425,33 @@ function add_by_hand( return basis end +function operators_asc_height(L::LieAlgebra) + return positive_roots(root_system(L)) +end + function operators_by_index( - L::LieAlgebraStructure, - chevalley_basis::NTuple{3,Vector{GAP.Obj}}, - birational_sequence::Vector{Int}, + L::LieAlgebra, + birational_seq::Vector{Int}, ) - @req all(i -> 1 <= i <= number_of_positive_roots(L), birational_sequence) "Entry of birational_sequence out of bounds" - - return [chevalley_basis[1][i] for i in birational_sequence] # TODO: change to [2] + return operators_asc_height(L)[birational_seq] end function operators_by_simple_roots( - L::LieAlgebraStructure, - chevalley_basis::NTuple{3,Vector{GAP.Obj}}, - birational_sequence::Vector{Vector{Int}}, + L::LieAlgebra, + birational_seq::Vector{Vector{Int}}, ) - rs = root_system_gap(L) - simple_roots = Vector{Vector{Int}}(GAP.Globals.SimpleSystem(rs)) - positive_roots = Vector{Vector{Int}}(GAP.Globals.PositiveRoots(rs)) - - root_inds = Int[] - for whgt_alpha in birational_sequence - @req length(whgt_alpha) == rank(L) "Length mismatch" - @req all(>=(0), whgt_alpha) "Only positive roots are allowed as input" - root = sum(whgt_alpha .* simple_roots) - root_ind = findfirst(==(root), positive_roots) - @req !isnothing(root_ind) "$whgt_alpha is not a positive root" - push!(root_inds, root_ind) + R = root_system(L) + operators = map(birational_seq) do whgt_alpha + root = RootSpaceElem(R, whgt_alpha) + fl = is_positive_root(root) + @req fl "Only positive roots are allowed as input" + root end - return operators_by_index(L, chevalley_basis, root_inds) -end - -function operators_lusztig( - L::LieAlgebraStructure, - chevalley_basis::NTuple{3,Vector{GAP.Obj}}, - reduced_expression::Vector{Int}, -) - root_inds = operators_lusztig_indices(L, reduced_expression) - return operators_by_index(L, chevalley_basis, root_inds) + return operators end -function operators_lusztig_indices(L::LieAlgebraStructure, word::Vector{Int}) +function operators_lusztig(L::LieAlgebra, reduced_expression::Vector{Int}) # Computes the operators for the lusztig polytopes for a longest weyl-word # reduced_expression. @@ -509,44 +462,21 @@ function operators_lusztig_indices(L::LieAlgebraStructure, word::Vector{Int}) # \beta_2 = \alpha_1 + \alpha_2 # \beta_3 = \alpha_2 - rs = root_system_gap(L) - - simple_roots = GAP.Globals.SimpleSystem(rs) - positive_roots = Vector{Vector{Int}}(GAP.Globals.PositiveRoots(rs)) - sparse_cartan_matrix = GAP.Globals.SparseCartanMatrix(GAPWrap.WeylGroup(rs)) - - root_inds = Int[] - - for k in 1:length(word) - # Calculate betas by applying simple reflections step-by-step. - root = copy(simple_roots[word[k]]) - for j in (k - 1):-1:1 - GAP.Globals.ApplySimpleReflection(sparse_cartan_matrix, word[j], root) - end - root_ind = findfirst(==(Vector{Int}(root)), positive_roots) - @req !isnothing(root_ind) "$root is not a positive root" - push!(root_inds, root_ind) + R = root_system(L) + W = weyl_group(R) + operators = map(1:length(reduced_expression)) do k + root = W(reduced_expression[1:(k - 1)]) * simple_root(R, reduced_expression[k]) + fl = is_positive_root(root) + @req fl "Only positive roots may occur here" + root end - return root_inds + return operators end -@doc """ - is_fundamental(highest_weight::Vector{IntegerUnion}) -> Bool - -Return if ``highest_weight`` is fundamental, i.e. [0, ..., 1, ..., 0]. - -# Examples -```jldoctest -julia> BasisLieHighestWeight.is_fundamental([0, 1, 0]) -true - -julia> BasisLieHighestWeight.is_fundamental([0, 1, 1]) -false -``` -""" -function is_fundamental(highest_weight::Vector{<:IntegerUnion}) +# TODO: upstream to LieAlgebras/RootSystem.jl +function is_fundamental(highest_weight::WeightLatticeElem) hasone = false - for i in highest_weight + for i in coefficients(highest_weight) if iszero(i) continue elseif isone(i) @@ -559,19 +489,16 @@ function is_fundamental(highest_weight::Vector{<:IntegerUnion}) return hasone end -function compute_sub_weights(highest_weight::Vector{ZZRingElem}) - # returns list of weights w != 0, highest_weight with 0 <= w <= highest_weight elementwise, ordered by l_2-norm - - sub_weights_w = [] - foreach(Iterators.product((0:x for x in highest_weight)...)) do i - push!(sub_weights_w, [i...]) - end - if isempty(sub_weights_w) || length(sub_weights_w) == 1 # case [] or [[0, ..., 0]] - return [] - else - popfirst!(sub_weights_w) # [0, ..., 0] - pop!(sub_weights_w) # highest_weight - sort!(sub_weights_w; by=x -> sum((x) .^ 2)) - return sub_weights_w +function sub_weights(w::WeightLatticeElem) + # returns list of weights v != 0, highest_weight with 0 <= v <= w elementwise + @req is_dominant(w) "The input must be a dominant weight" + R = root_system(w) + map(AbstractAlgebra.ProductIterator([0:w[i] for i in 1:rank(R)])) do coeffs + WeightLatticeElem(R, coeffs) end end + +function sub_weights_proper(w::WeightLatticeElem) + # returns list of weights v != 0, highest_weight with 0 <= v <= w elementwise, but neither 0 nor w + return filter(x -> !iszero(x) && x != w, sub_weights(w)) +end diff --git a/experimental/BasisLieHighestWeight/src/MonomialBasis.jl b/experimental/BasisLieHighestWeight/src/MonomialBasis.jl index 84d4e535f7a6..57d0b0d8047e 100644 --- a/experimental/BasisLieHighestWeight/src/MonomialBasis.jl +++ b/experimental/BasisLieHighestWeight/src/MonomialBasis.jl @@ -1,23 +1,23 @@ @attributes mutable struct MonomialBasis - lie_algebra::LieAlgebraStructure - highest_weight::Vector{Int} - birational_sequence::BirationalSequence + lie_algebra::AbstractLieAlgebra{QQFieldElem} + highest_weight::WeightLatticeElem + birational_seq::BirationalSequence monomial_ordering::MonomialOrdering dimension::Int monomials::Set{ZZMPolyRingElem} monomials_parent::ZZMPolyRing function MonomialBasis( - lie_algebra::LieAlgebraStructure, - highest_weight::Vector{<:IntegerUnion}, - birational_sequence::BirationalSequence, + lie_algebra::AbstractLieAlgebra{QQFieldElem}, + highest_weight::WeightLatticeElem, + birational_seq::BirationalSequence, monomial_ordering::MonomialOrdering, monomials::Set{ZZMPolyRingElem}, ) return new( lie_algebra, - Int.(highest_weight), - birational_sequence, + highest_weight, + birational_seq, monomial_ordering, length(monomials), monomials, @@ -37,15 +37,35 @@ monomials(basis::MonomialBasis) = basis.monomials monomial_ordering(basis::MonomialBasis) = basis.monomial_ordering -birational_sequence(basis::MonomialBasis) = basis.birational_sequence +birational_sequence(basis::MonomialBasis) = basis.birational_seq function Base.show(io::IO, ::MIME"text/plain", basis::MonomialBasis) io = pretty(io) print(io, "Monomial basis of a highest weight module") - print(io, Indent(), "\nof highest weight $(highest_weight(basis))", Dedent()) + print( + io, + Indent(), + "\nof highest weight $(Int.(Oscar._vec(coefficients(highest_weight(basis)))))", + Dedent(), + ) print(io, Indent(), "\nof dimension $(dim(basis))", Dedent()) print(io, Indent(), "\nwith monomial ordering $(monomial_ordering(basis))", Dedent()) - print(io, "\nover ", Lowercase(), base_lie_algebra(basis)) + # TODO: use the following line instead of printing workaround below + # print(io, "\nover ", Lowercase(), base_lie_algebra(basis)) + # begin of workaround + L = base_lie_algebra(basis) + print(io, "\nover Lie algebra") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + end + end + # end of workaround if get_attribute(basis, :algorithm, nothing) === basis_lie_highest_weight_compute print( io, @@ -53,8 +73,8 @@ function Base.show(io::IO, ::MIME"text/plain", basis::MonomialBasis) "\nwhere the used birational sequence consists of the following roots (given as coefficients w.r.t. alpha_i):", Indent(), ) - for weight in birational_sequence(basis).weights_alpha - print(io, '\n', Int.(weight)) + for root in operators_as_roots(birational_sequence(basis)) + print(io, '\n', Int.(Oscar._vec(coefficients(root)))) end print(io, Dedent(), Dedent()) print( @@ -64,7 +84,7 @@ function Base.show(io::IO, ::MIME"text/plain", basis::MonomialBasis) Indent(), ) for gen in get_attribute(basis, :minkowski_gens) - print(io, '\n', Int.(gen)) + print(io, '\n', Int.(Oscar._vec(coefficients(gen)))) end print(io, Dedent(), Dedent()) elseif get_attribute(basis, :algorithm, nothing) === basis_coordinate_ring_kodaira_compute @@ -74,8 +94,8 @@ function Base.show(io::IO, ::MIME"text/plain", basis::MonomialBasis) "\nwhere the used birational sequence consists of the following roots (given as coefficients w.r.t. alpha_i):", Indent(), ) - for weight in birational_sequence(basis).weights_alpha - print(io, '\n', Int.(weight)) + for root in operators_as_roots(birational_sequence(basis)) + print(io, '\n', Int.(Oscar._vec(coefficients(root)))) end print(io, Dedent(), Dedent()) print( @@ -85,7 +105,7 @@ function Base.show(io::IO, ::MIME"text/plain", basis::MonomialBasis) Indent(), ) for gen in get_attribute(basis, :minkowski_gens) - print(io, '\n', Int.(gen)) + print(io, '\n', Int.(Oscar._vec(coefficients(gen)))) end print(io, Dedent(), Dedent()) end @@ -98,8 +118,23 @@ function Base.show(io::IO, basis::MonomialBasis) io = pretty(io) print( io, - "Monomial basis of a highest weight module with highest weight $(highest_weight(basis)) over ", + "Monomial basis of a highest weight module with highest weight $(Int.(Oscar._vec(coefficients(highest_weight(basis))))) over ", ) - print(terse(io), Lowercase(), base_lie_algebra(basis)) + # TODO: use the following line instead of printing workaround below + # print(terse(io), Lowercase(), base_lie_algebra(basis)) + # begin of workaround + L = base_lie_algebra(basis) + print(io, "Lie algebra") + if has_root_system(L) + rs = root_system(L) + if has_root_system_type(rs) + type, ord = root_system_type_with_ordering(rs) + print(io, " of type ", _root_system_type_string(type)) + if !issorted(ord) + print(io, " (non-canonical ordering)") + end + end + end + # end of workaround end end diff --git a/experimental/BasisLieHighestWeight/src/MonomialOrder.jl b/experimental/BasisLieHighestWeight/src/MonomialOrder.jl index b1dbb134798d..43443e61d289 100644 --- a/experimental/BasisLieHighestWeight/src/MonomialOrder.jl +++ b/experimental/BasisLieHighestWeight/src/MonomialOrder.jl @@ -1,11 +1,11 @@ function get_monomial_ordering( ordering_input::Union{Symbol,Function}, ZZx::ZZMPolyRing, - weights_alpha::Vector{Vector{QQFieldElem}}, + operators::Vector{RootSpaceElem}, ) if _is_weighted(ordering_input) choosen_monomial_order = monomial_ordering( - ZZx, ordering_input, Int[Int(sum(w)) for w in weights_alpha] + ZZx, ordering_input, [Int(height(alpha)) for alpha in operators] ) else choosen_monomial_order = monomial_ordering(ZZx, ordering_input) diff --git a/experimental/BasisLieHighestWeight/src/NewMonomial.jl b/experimental/BasisLieHighestWeight/src/NewMonomial.jl index 3af1055a78c8..3f28c032e5b0 100644 --- a/experimental/BasisLieHighestWeight/src/NewMonomial.jl +++ b/experimental/BasisLieHighestWeight/src/NewMonomial.jl @@ -1,6 +1,9 @@ -function weight(mon::ZZMPolyRingElem, weights_w::Vector{Vector{ZZRingElem}}) - @assert length(weights_w) == length(degrees(mon)) - return sum(exp * weight for (exp, weight) in zip(degrees(mon), weights_w)) +function weight(mon::ZZMPolyRingElem, birational_seq::BirationalSequence) + @assert length(birational_seq) == nvars(parent(mon)) + return sum( + exp * weight for + (exp, weight) in zip(degrees(mon), operators_as_weights(birational_seq)) + ) end function calc_vec( diff --git a/experimental/BasisLieHighestWeight/src/RootConversion.jl b/experimental/BasisLieHighestWeight/src/RootConversion.jl deleted file mode 100644 index c1f83d9a0964..000000000000 --- a/experimental/BasisLieHighestWeight/src/RootConversion.jl +++ /dev/null @@ -1,9 +0,0 @@ -function w_to_alpha( - L::LieAlgebraStructure, weight_w::Union{Vector{ZZRingElem},Vector{QQFieldElem}} -) - return weight_w * inv_cartan_matrix(L) -end - -function alpha_to_w(L::LieAlgebraStructure, weight_alpha::Vector{QQFieldElem}) - return weight_alpha * cartan_matrix(L) -end diff --git a/experimental/BasisLieHighestWeight/src/TensorModels.jl b/experimental/BasisLieHighestWeight/src/TensorModels.jl index c154d006e890..a8eb645dccd8 100644 --- a/experimental/BasisLieHighestWeight/src/TensorModels.jl +++ b/experimental/BasisLieHighestWeight/src/TensorModels.jl @@ -14,7 +14,7 @@ function _tensor_power(A, k) end @doc raw""" - tensor_matrices_of_operators(L::LieAlgebraStructure, highest_weight::Vector{ZZRingElem}, operators::Vector{GAP.Obj}) -> Vector{SMat{ZZRingElem}} + tensor_matrices_of_operators(L::LieAlgebra, highest_weight::WeightLatticeElem, operators::Vector{RootSpaceElem}) -> Vector{SMat{ZZRingElem}} Calculates the action matrices of the operators in `operators` on the tensor product of multiples of the fundamental modules (with multiplicities in `highest_weight`). @@ -22,20 +22,19 @@ Note that the highest weight module with highest weight `highest_weight` is a su We use multiples of fundamentals to reduce the total dimension of the ambient space """ function tensor_matrices_of_operators( - L::LieAlgebraStructure, highest_weight::Vector{ZZRingElem}, operators::Vector{GAP.Obj} + L::LieAlgebra, highest_weight::WeightLatticeElem, operators::Vector{RootSpaceElem} ) - matrices_of_operators = [zero_matrix(SMat, ZZ, 1) for _ in operators] - for (i, highest_weight_i) in enumerate(Int.(highest_weight)) - if highest_weight_i <= 0 - continue - end - wi = ZZ.(1:length(highest_weight) .== i) # i-th fundamental weight - matrices_of_operators = [ + R = root_system(L) + mats = [zero_matrix(SMat, ZZ, 1) for _ in operators] + for i in 1:rank(R) + highest_weight_i = highest_weight[i] + iszero(highest_weight_i) && continue + mats = [ _tensor_product(mat_temp, mat_wi) for (mat_temp, mat_wi) in zip( - matrices_of_operators, - matrices_of_operators_gap(L, highest_weight_i * wi, operators), + mats, + matrices_of_operators(L, highest_weight_i * fundamental_weight(R, i), operators), ) ] end - return matrices_of_operators + return mats end diff --git a/experimental/BasisLieHighestWeight/src/UserFunctions.jl b/experimental/BasisLieHighestWeight/src/UserFunctions.jl index d9bb839a79ae..1e2db62f0eb3 100644 --- a/experimental/BasisLieHighestWeight/src/UserFunctions.jl +++ b/experimental/BasisLieHighestWeight/src/UserFunctions.jl @@ -17,11 +17,8 @@ julia> basis_lie_highest_weight_operators(:B, 2) ``` """ function basis_lie_highest_weight_operators(type::Symbol, rank::Int) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = chevalley_basis[1] # TODO: change to [2] - weights_alpha = [w_to_alpha(L, weight(L, op)) for op in operators] - return collect(enumerate(weights_alpha)) + R = root_system(type, rank) + return collect(enumerate(map(r -> Oscar._vec(coefficients(r)), positive_roots(R)))) # TODO: clean up end @doc raw""" @@ -112,8 +109,8 @@ over Lie algebra of type C3 [0, 0, 1] [1, 1, 0] [0, 1, 1] - [1, 1, 1] [0, 2, 1] + [1, 1, 1] [1, 2, 1] [2, 2, 1] and the basis was generated by Minkowski sums of the bases of the following highest weight modules: @@ -127,9 +124,8 @@ over Lie algebra of type C3 function basis_lie_highest_weight( type::Symbol, rank::Int, highest_weight::Vector{Int}; monomial_ordering::Symbol=:degrevlex ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = chevalley_basis[1] # TODO: change to [2] + L = lie_algebra(QQ, type, rank) + operators = operators_asc_height(L) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -140,9 +136,8 @@ function basis_lie_highest_weight( birational_sequence::Vector{Int}; monomial_ordering::Symbol=:degrevlex, ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_index(L, chevalley_basis, birational_sequence) + L = lie_algebra(QQ, type, rank) + operators = operators_by_index(L, birational_sequence) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -153,9 +148,8 @@ function basis_lie_highest_weight( birational_sequence::Vector{Vector{Int}}; monomial_ordering::Symbol=:degrevlex, ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_simple_roots(L, chevalley_basis, birational_sequence) + L = lie_algebra(QQ, type, rank) + operators = operators_by_simple_roots(L, birational_sequence) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -205,9 +199,8 @@ function basis_lie_highest_weight_lusztig( type::Symbol, rank::Int, highest_weight::Vector{Int}, reduced_expression::Vector{Int} ) monomial_ordering = :wdegrevlex - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_lusztig(L, chevalley_basis, reduced_expression) + L = lie_algebra(QQ, type, rank) + operators = operators_lusztig(L, reduced_expression) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -276,9 +269,8 @@ function basis_lie_highest_weight_string( type::Symbol, rank::Int, highest_weight::Vector{Int}, reduced_expression::Vector{Int} ) monomial_ordering = :neglex - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_index(L, chevalley_basis, reduced_expression) + L = lie_algebra(QQ, type, rank) + operators = operators_by_index(L, reduced_expression) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -316,9 +308,8 @@ over Lie algebra of type A3 """ function basis_lie_highest_weight_ffl(type::Symbol, rank::Int, highest_weight::Vector{Int}) monomial_ordering = :degrevlex - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = reverse(chevalley_basis[1]) # TODO: change to [2] + L = lie_algebra(QQ, type, rank) + operators = reverse(operators_asc_height(L)) # we reverse the order here to have simple roots at the right end, this is then a good ordering. # simple roots at the right end speed up the program very much return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) @@ -389,9 +380,8 @@ function basis_lie_highest_weight_nz( type::Symbol, rank::Int, highest_weight::Vector{Int}, reduced_expression::Vector{Int} ) monomial_ordering = :degrevlex - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_index(L, chevalley_basis, reduced_expression) + L = lie_algebra(QQ, type, rank) + operators = operators_by_index(L, reduced_expression) return basis_lie_highest_weight_compute(L, highest_weight, operators, monomial_ordering) end @@ -466,9 +456,8 @@ function basis_coordinate_ring_kodaira( degree::Int; monomial_ordering::Symbol=:degrevlex, ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = chevalley_basis[1] # TODO: change to [2] + L = lie_algebra(QQ, type, rank) + operators = operators_asc_height(L) return basis_coordinate_ring_kodaira_compute( L, highest_weight, degree, operators, monomial_ordering ) @@ -482,9 +471,8 @@ function basis_coordinate_ring_kodaira( birational_sequence::Vector{Int}; monomial_ordering::Symbol=:degrevlex, ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_index(L, chevalley_basis, birational_sequence) + L = lie_algebra(QQ, type, rank) + operators = operators_by_index(L, birational_sequence) return basis_coordinate_ring_kodaira_compute( L, highest_weight, degree, operators, monomial_ordering ) @@ -498,9 +486,8 @@ function basis_coordinate_ring_kodaira( birational_sequence::Vector{Vector{Int}}; monomial_ordering::Symbol=:degrevlex, ) - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = operators_by_simple_roots(L, chevalley_basis, birational_sequence) + L = lie_algebra(QQ, type, rank) + operators = operators_by_simple_roots(L, birational_sequence) return basis_coordinate_ring_kodaira_compute( L, highest_weight, degree, operators, monomial_ordering ) @@ -564,9 +551,8 @@ function basis_coordinate_ring_kodaira_ffl( type::Symbol, rank::Int, highest_weight::Vector{Int}, degree::Int ) monomial_ordering = :degrevlex - L = lie_algebra(type, rank) - chevalley_basis = chevalley_basis_gap(L) - operators = reverse(chevalley_basis[1]) # TODO: change to [2] + L = lie_algebra(QQ, type, rank) + operators = reverse(operators_asc_height(L)) # we reverse the order here to have simple roots at the right end, this is then a good ordering. # simple roots at the right end speed up the program very much return basis_coordinate_ring_kodaira_compute( diff --git a/experimental/BasisLieHighestWeight/src/WeylPolytope.jl b/experimental/BasisLieHighestWeight/src/WeylPolytope.jl index 72cbeec859c2..066d60c262a6 100644 --- a/experimental/BasisLieHighestWeight/src/WeylPolytope.jl +++ b/experimental/BasisLieHighestWeight/src/WeylPolytope.jl @@ -1,51 +1,3 @@ - -@doc raw""" - orbit_weylgroup(L::LieAlgebraStructure, weight_w::Vector{ZZRingElem}) -> Vector{Vector{ZZRingElem}} - -Computes the orbit of the weight `weight_w` given as coefficients to the fundamental weights -$\omega_i$ under the action of the Weyl group of the Lie algebra `L`. -""" -function orbit_weylgroup(L::LieAlgebraStructure, weight_w::Vector{ZZRingElem}) - # initialization - weyl_group = GAPWrap.WeylGroup(GAPWrap.RootSystem(L.lie_algebra_gap)) - orbit_iterator = GAPWrap.WeylOrbitIterator(weyl_group, GAP.Obj(Int.(weight_w))) - vertices = Vector{ZZRingElem}[] - - # operate with the weylgroup on weight_vector - while !(GAPWrap.IsDoneIterator(orbit_iterator)) - w = GAPWrap.NextIterator(orbit_iterator) - push!(vertices, Vector{ZZRingElem}(w)) - end - - return vertices -end - -@doc raw""" - get_dim_weightspace(L::LieAlgebraStructure, highest_weight::Vector{ZZRingElem}) -> Dict{Vector{ZZRingElem},Int} - -Computes the dimension of the weight spaces of the Lie algebra `L` module with highest weight `highest_weight`. -For all dominant weights, the dimension is computed with GAP. For the remaining weights, the dimension is -calculated by checking which dominant weight lies in the orbit of the weight under the action of the Weyl group. - -The weights are given as coefficients to the fundamental weights $\omega_i$. -""" -function get_dim_weightspace(L::LieAlgebraStructure, highest_weight::Vector{ZZRingElem}) - # calculate dimension for dominant weights with GAP - root_system = root_system_gap(L) - dominant_char = GAP.Globals.DominantCharacter(root_system, GAP.Obj(Int.(highest_weight))) - dominant_weights = map(weight -> ZZ.(weight), dominant_char[1]) - dominant_weights_dim = Int.(dominant_char[2]) - weightspaces = Dict{Vector{ZZRingElem},Int}() - - # calculate dimension for the rest by checking which dominant weight lies in the orbit - for (dominant_weight, dim) in zip(dominant_weights, dominant_weights_dim) - for weight in orbit_weylgroup(L, dominant_weight) - weightspaces[highest_weight - weight] = dim - end - end - return weightspaces -end - function convert_lattice_points_to_monomials( ZZx::ZZMPolyRing, lattice_points_weightspace::Vector{Vector{ZZRingElem}} ) @@ -62,62 +14,59 @@ where the coordinates in `zero_coordinates` are set to zero. All weights are given as coefficients to the simple roots $\alpha_i$. """ function get_lattice_points_of_weightspace( - root_weights::Vector{Vector{QQFieldElem}}, - weight::Vector{QQFieldElem}, + root_weights::Vector{RootSpaceElem}, + weight::RootSpaceElem, zero_coordinates::Vector{Int}, ) # calculate all integer solutions to the following linear program: - # [ | | ] [ x ] + # [ | | ] [ x ] # [root_weights[1]...root_weights[k]] * [ | ] = weight - # [ | | ] [ res ] - # [ | | ] [ | ] + # [ | | ] [ res ] + # [ | | ] [ | ] # where res[i] >= 0 for all i n = length(root_weights) - m = length(weight) - - A = zero_matrix(QQ, 2m + n + length(zero_coordinates), n) - b = [zero(QQ) for _ in 1:(2m + n + length(zero_coordinates))] + m = rank(root_system(weight)) # equalities + A_eq = zero_matrix(QQ, m + length(zero_coordinates), n) + b_eq = [zero(QQ) for _ in 1:(m + length(zero_coordinates))] for i in 1:n - w = matrix(QQ, m, 1, root_weights[i]) - A[1:m, i] = w - A[(m + 1):(2m), i] = -w - end - - b[1:m] = weight - b[(m + 1):(2m)] = -weight - # non-negativity - for i in 1:n - A[2m + i, i] = -1 + A_eq[1:m, i] = transpose(coefficients(root_weights[i])) end + b_eq[1:m] = view(coefficients(weight), 1, :) for (j, i) in enumerate(zero_coordinates) - A[2m + n + j, i] = 1 - b[2m + n + j] = 0 + A_eq[m + j, i] = 1 end - sol = Vector{ZZRingElem}.(lattice_points(polyhedron(A, b))) + + # non-negativity + A_ineq = -identity_matrix(QQ, n) + b_ineq = [zero(QQ) for _ in 1:n] + + sol = Vector{ZZRingElem}.(lattice_points(polyhedron((A_ineq, b_ineq), (A_eq, b_eq)))) return sol end @doc raw""" - compute_zero_coordinates(bir_sequence::BirationalSequence, highest_weight::Vector{ZZRingElem}) + compute_zero_coordinates(bir_sequence::BirationalSequence, highest_weight::WeightLatticeElem) This function returns all indices into the birational sequence `bir_sequence` that correspond to root vectors that are in the annihilator of the submodule generated by everything on their right on the highest weight vector. """ function compute_zero_coordinates( - bir_sequence::BirationalSequence, highest_weight::Vector{ZZRingElem} + bir_sequence::BirationalSequence, highest_weight::WeightLatticeElem ) - n = length(bir_sequence.weights_alpha) - m = length(highest_weight) - non_zeros = Set{Int}(findall(!iszero, highest_weight)) + n = length(bir_sequence) + m = rank(root_system(highest_weight)) + non_zeros = Set(findall(!iszero, coefficients(highest_weight))) zero_coordinates = Int[] for c in n:-1:1 length(non_zeros) == m && break - if !isdisjoint(non_zeros, findall(!iszero, bir_sequence.weights_alpha[c])) - union!(non_zeros, findall(<(0), bir_sequence.weights_w[c])) + if !isdisjoint( + non_zeros, findall(!iszero, coefficients(operator_as_root(bir_sequence, c))) + ) + union!(non_zeros, findall(<(0), coefficients(operator_as_weight(bir_sequence, c)))) else push!(zero_coordinates, c) end diff --git a/experimental/BasisLieHighestWeight/test/MBOld.jl b/experimental/BasisLieHighestWeight/test/MBOld.jl index fe9c9760c27b..fc6f0eab8ec9 100644 --- a/experimental/BasisLieHighestWeight/test/MBOld.jl +++ b/experimental/BasisLieHighestWeight/test/MBOld.jl @@ -71,7 +71,7 @@ end #### Lie algebras function lieAlgebra(t::String, n::Int) - L = GAP.Globals.SimpleLieAlgebra(GAP.Obj(t), n, GAP.Globals.Rationals) + L = codomain(Oscar.iso_oscar_gap(Oscar.lie_algebra(QQ, Symbol(t), n))) return L, NTuple{3,Vector{GAP.Obj}}(GAP.Globals.ChevalleyBasis(L)) end @@ -214,9 +214,10 @@ function compute(v0, mats, wts::Vector{Vector{Int}}) newPos = length(monomials) deg = deg + 1 newMons(deg) - for i in 1:m, di in deg:-1:1 + # iteration in degrevlex ordering + for i in m:-1:1, di in deg:-1:1 for p in startPos:newPos - if !all(monomials[p][1:(i - 1)] .== 0) + if !all(monomials[p][(i + 1):m] .== 0) continue end diff --git a/experimental/BasisLieHighestWeight/test/MainAlgorithm-test.jl b/experimental/BasisLieHighestWeight/test/MainAlgorithm-test.jl index 4b9e06bb02e8..88eae9d1ddaa 100644 --- a/experimental/BasisLieHighestWeight/test/MainAlgorithm-test.jl +++ b/experimental/BasisLieHighestWeight/test/MainAlgorithm-test.jl @@ -31,43 +31,53 @@ function check_dimension( @test gap_dim == dim(basis) == length(monomials(basis)) # check if dimension is correct end -@testset "Test BasisLieHighestWeight" begin - @testset "is_fundamental" begin - @test BasisLieHighestWeight.is_fundamental([ZZ(0), ZZ(1), ZZ(0)]) - @test !BasisLieHighestWeight.is_fundamental([ZZ(0), ZZ(1), ZZ(1)]) - end +@testset "is_fundamental" begin + R = root_system(:B, 3) + @test BasisLieHighestWeight.is_fundamental(WeightLatticeElem(R, [ZZ(0), ZZ(1), ZZ(0)])) + @test !BasisLieHighestWeight.is_fundamental(WeightLatticeElem(R, [ZZ(0), ZZ(1), ZZ(1)])) +end - @testset "compute_sub_weights" begin - @test isequal(BasisLieHighestWeight.compute_sub_weights([ZZ(0), ZZ(0), ZZ(0)]), []) - sub_weights = Vector{Vector{ZZRingElem}}([ - [1, 0, 0], - [0, 1, 0], +@testset "sub_weights(_proper)" begin + sub_weights = BasisLieHighestWeight.sub_weights + sub_weights_proper = BasisLieHighestWeight.sub_weights_proper + R = root_system(:B, 3) + + w_zero = zero(WeightLatticeElem, R) + @test issetequal(sub_weights(w_zero), [w_zero]) + @test isempty(sub_weights_proper(w_zero)) + + w_231 = WeightLatticeElem(R, [2, 3, 1]) + sub_weights_proper_231 = [ + WeightLatticeElem(R, coeffs) for coeffs in [ [0, 0, 1], - [1, 1, 0], - [1, 0, 1], + [0, 1, 0], [0, 1, 1], - [1, 1, 1], - [2, 0, 0], [0, 2, 0], - [2, 1, 0], + [0, 2, 1], + [0, 3, 0], + [0, 3, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1], [1, 2, 0], + [1, 2, 1], + [1, 3, 0], + [1, 3, 1], + [2, 0, 0], [2, 0, 1], - [0, 2, 1], + [2, 1, 0], [2, 1, 1], - [1, 2, 1], [2, 2, 0], - [0, 3, 0], [2, 2, 1], - [1, 3, 0], - [0, 3, 1], - [1, 3, 1], [2, 3, 0], - ]) - @test isequal( - BasisLieHighestWeight.compute_sub_weights([ZZ(2), ZZ(3), ZZ(1)]), sub_weights - ) - end + ] + ] + @test issetequal(sub_weights(w_231), [w_zero, w_231, sub_weights_proper_231...]) + @test issetequal(sub_weights_proper(w_231), sub_weights_proper_231) +end +@testset "Test BasisLieHighestWeight" begin @testset "Known examples basis_lie_highest_weight" begin base = basis_lie_highest_weight(:A, 2, [1, 0]) mons = monomials(base) diff --git a/experimental/BasisLieHighestWeight/test/NewMonomial-test.jl b/experimental/BasisLieHighestWeight/test/NewMonomial-test.jl index a2f78f125a10..966b99ad68e4 100644 --- a/experimental/BasisLieHighestWeight/test/NewMonomial-test.jl +++ b/experimental/BasisLieHighestWeight/test/NewMonomial-test.jl @@ -2,11 +2,14 @@ weight = BasisLieHighestWeight.weight calc_vec = BasisLieHighestWeight.calc_vec + R = root_system(:A, 2) ZZx, _ = polynomial_ring(ZZ, 2) x = gens(ZZx) mon1 = ZZx(1) mon2 = x[1]^2 * x[2] - weights = [[ZZ(1), ZZ(1)], [ZZ(2), ZZ(1)]] + birational_seq = birational_sequence([ + WeightLatticeElem(R, [ZZ(1), ZZ(1)]), WeightLatticeElem(R, [ZZ(2), ZZ(1)]) + ]) A = sparse_matrix(ZZ, 2, 2) # [0, 2; 1, 1] setindex!(A, sparse_row(ZZ, [2], [ZZ(2)]), 1) setindex!(A, sparse_row(ZZ, [1, 2], [ZZ(1), ZZ(1)]), 2) @@ -19,12 +22,12 @@ mon2_vec = sparse_row(ZZ, [1, 2], [2, 2]) @testset "weight" begin - @test isequal(weight(mon1, weights), [ZZ(0), ZZ(0)]) - @test isequal(weight(mon2, weights), [ZZ(4), ZZ(3)]) + @test weight(mon1, birational_seq) == WeightLatticeElem(R, [ZZ(0), ZZ(0)]) + @test weight(mon2, birational_seq) == WeightLatticeElem(R, [ZZ(4), ZZ(3)]) end @testset "calc_vec" begin - @test isequal(calc_vec(v0, mon1, matrices_of_operators), v0) - @test isequal(calc_vec(v0, mon2, matrices_of_operators), mon2_vec) + @test calc_vec(v0, mon1, matrices_of_operators) == v0 + @test calc_vec(v0, mon2, matrices_of_operators) == mon2_vec end end diff --git a/experimental/BasisLieHighestWeight/test/RootConversion-test.jl b/experimental/BasisLieHighestWeight/test/RootConversion-test.jl deleted file mode 100644 index 558d0039f698..000000000000 --- a/experimental/BasisLieHighestWeight/test/RootConversion-test.jl +++ /dev/null @@ -1,27 +0,0 @@ - -@testset "Test RootConversion" begin - w_to_alpha = BasisLieHighestWeight.w_to_alpha - alpha_to_w = BasisLieHighestWeight.alpha_to_w - - function test_inverse_alpha_w(L, weight) - @test w_to_alpha(L, alpha_to_w(L, weight)) == weight # alpha -> w -> alpha - @test alpha_to_w(L, w_to_alpha(L, weight)) == weight # w -> alpha -> w - end - - @testset "Dynkin type $dynkin" for dynkin in (:A, :B, :C, :D, :E, :F, :G) - @testset "n = $n" for n in 1:10 - if ( - !(dynkin == :B && n < 2) && - !(dynkin == :C && n < 2) && - !(dynkin == :D && n < 4) && - !(dynkin == :E && !(n == 6 || n == 7 || n == 8)) && - !(dynkin == :F && n != 4) && - !(dynkin == :G && (n != 2)) - ) - weight = [rand(QQ, -10:10) for _ in 1:n] - L = BasisLieHighestWeight.lie_algebra(dynkin, n) - test_inverse_alpha_w(L, weight) - end - end - end -end diff --git a/experimental/BasisLieHighestWeight/test/runtests.jl b/experimental/BasisLieHighestWeight/test/runtests.jl index b07a9d40abbe..4d7a059cc49b 100644 --- a/experimental/BasisLieHighestWeight/test/runtests.jl +++ b/experimental/BasisLieHighestWeight/test/runtests.jl @@ -1,3 +1,2 @@ include("NewMonomial-test.jl") -include("RootConversion-test.jl") include("MainAlgorithm-test.jl") diff --git a/experimental/LieAlgebras/src/GapWrapper.jl b/experimental/LieAlgebras/src/GapWrapper.jl index 0945275eceb1..e943a594f00a 100644 --- a/experimental/LieAlgebras/src/GapWrapper.jl +++ b/experimental/LieAlgebras/src/GapWrapper.jl @@ -4,7 +4,18 @@ # ################################################################################ -function lie_algebra_simple_module_struct_consts_gap(L::LieAlgebra, weight::Vector{Int}) +function lie_algebra_simple_module_struct_consts_gap( + L::LieAlgebra, weight::WeightLatticeElem +) + @req root_system(weight) == root_system(L) "Incompatible root systems" + return lie_algebra_simple_module_struct_consts_gap( + L, Int.(Oscar._vec(coefficients(weight))) + ) +end + +function lie_algebra_simple_module_struct_consts_gap( + L::LieAlgebra{C}, weight::Vector{Int} +) where {C<:FieldElem} R = coefficient_ring(L) isoR = Oscar.iso_oscar_gap(R) @@ -16,7 +27,7 @@ function lie_algebra_simple_module_struct_consts_gap(L::LieAlgebra, weight::Vect dimV = GAPWrap.Dimension(gapV) basisV = GAPWrap.Basis(gapV) - struct_consts = Matrix{sparse_row_type(R)}(undef, dimL, dimV) + struct_consts = Matrix{sparse_row_type(C)}(undef, dimL, dimV) for i in 1:dimL, j in 1:dimV struct_consts[i, j] = sparse_row( R, diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index a38a308d2ee2..93ec1080c25d 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -776,6 +776,19 @@ function chevalley_basis(L::LieAlgebra) # to be implemented by subtypes throw(Hecke.NotImplemented()) end +@doc raw""" + cartan_matrix(L::LieAlgebra) -> ZZMatrix + +Return the Cartan matrix of the root system of `L`. +""" +function cartan_matrix(L::LieAlgebra{C}) where {C<:FieldElem} + return cartan_matrix(root_system(L)) +end + +function cartan_matrix_inv(L::LieAlgebra{C}) where {C<:FieldElem} + return cartan_matrix_inv(root_system(L)) +end + @doc raw""" cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} -> LieSubalgebra{C,elem_type(L)} diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index f50773d2fe67..a66743167284 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -1385,6 +1385,7 @@ end # TODO: add semisimplicity check once that is available +# TODO: move to RootSystem.jl function is_dominant_weight(hw::Vector{<:IntegerUnion}) return all(>=(0), hw) end @@ -1405,6 +1406,7 @@ end @doc raw""" dim_of_simple_module([T = Int], L::LieAlgebra{C}, hw::Vector{<:IntegerUnion}) -> T + dim_of_simple_module([T = Int], L::LieAlgebra{C}, hw::WeightLatticeElem) -> T Compute the dimension of the simple module of the Lie algebra `L` with highest weight `hw` using Weyl's dimension formula. @@ -1427,8 +1429,18 @@ function dim_of_simple_module(L::LieAlgebra, hw::Vector{<:IntegerUnion}) return dim_of_simple_module(Int, L, hw) end +function dim_of_simple_module(T::Type, L::LieAlgebra, hw::WeightLatticeElem) + R = root_system(L) + return dim_of_simple_module(T, R, hw) +end + +function dim_of_simple_module(L::LieAlgebra, hw::WeightLatticeElem) + return dim_of_simple_module(Int, L, hw) +end + @doc raw""" dominant_weights([T,] L::LieAlgebra{C}, hw::Vector{<:IntegerUnion}) -> Vector{T} + dominant_weights([T,] L::LieAlgebra{C}, hw::WeightLatticeElem) -> Vector{T} Computes the dominant weights occurring in the simple module of the Lie algebra `L` with highest weight `hw`, sorted ascendingly by the total height of roots needed to reach them from `hw`. @@ -1461,8 +1473,18 @@ function dominant_weights(L::LieAlgebra, hw::Vector{<:IntegerUnion}) return dominant_weights(Vector{Int}, L, hw) end +function dominant_weights(T::Type, L::LieAlgebra, hw::WeightLatticeElem) + R = root_system(L) + return dominant_weights(T, R, hw) +end + +function dominant_weights(L::LieAlgebra, hw::WeightLatticeElem) + return dominant_weights(Vector{Int}, L, hw) +end + @doc raw""" dominant_character(L::LieAlgebra{C}, hw::Vector{<:IntegerUnion}) -> Dict{Vector{Int}, Int} + dominant_character(L::LieAlgebra{C}, hw::WeightLatticeElem) -> Dict{Vector{Int}, Int} Computes the dominant weights occurring in the simple module of the Lie algebra `L` with highest weight `hw`, together with their multiplicities. @@ -1488,8 +1510,14 @@ function dominant_character(L::LieAlgebra, hw::Vector{<:IntegerUnion}) return dominant_character(R, hw) end +function dominant_character(L::LieAlgebra, hw::WeightLatticeElem) + R = root_system(L) + return dominant_character(R, hw) +end + @doc raw""" character(L::LieAlgebra{C}, hw::Vector{<:IntegerUnion}) -> Dict{Vector{Int}, Int} + character(L::LieAlgebra{C}, hw::WeightLatticeElem) -> Dict{Vector{Int}, Int} Computes all weights occurring in the simple module of the Lie algebra `L` with highest weight `hw`, together with their multiplicities. @@ -1520,8 +1548,14 @@ function character(L::LieAlgebra, hw::Vector{<:IntegerUnion}) return character(R, hw) end +function character(L::LieAlgebra, hw::WeightLatticeElem) + R = root_system(L) + return character(R, hw) +end + @doc raw""" tensor_product_decomposition(L::LieAlgebra, hw1::Vector{<:IntegerUnion}, hw2::Vector{<:IntegerUnion}) -> MSet{Vector{Int}} + tensor_product_decomposition(L::LieAlgebra, hw1::WeightLatticeElem, hw2::WeightLatticeElem) -> MSet{Vector{Int}} Computes the decomposition of the tensor product of the simple modules of the Lie algebra `L` with highest weights `hw1` and `hw2` into simple modules with their multiplicities. @@ -1553,3 +1587,10 @@ function tensor_product_decomposition( R = root_system(L) return tensor_product_decomposition(R, hw1, hw2) end + +function tensor_product_decomposition( + L::LieAlgebra, hw1::WeightLatticeElem, hw2::WeightLatticeElem +) + R = root_system(L) + return tensor_product_decomposition(R, hw1, hw2) +end diff --git a/experimental/LieAlgebras/src/RootSystem.jl b/experimental/LieAlgebras/src/RootSystem.jl index f67ce0f90eb4..720777c74d38 100644 --- a/experimental/LieAlgebras/src/RootSystem.jl +++ b/experimental/LieAlgebras/src/RootSystem.jl @@ -1433,6 +1433,15 @@ Dict{Vector{Int64}, Int64} with 4 entries: ``` """ function dominant_character(R::RootSystem, hw::WeightLatticeElem) + char = _dominant_character(R, hw) + return Dict(Int.(_vec(coefficients(w))) => m for (w, m) in char) +end + +function dominant_character(R::RootSystem, hw::Vector{<:IntegerUnion}) + return dominant_character(R, WeightLatticeElem(R, hw)) +end + +function _dominant_character(R::RootSystem, hw::WeightLatticeElem) T = Int @req root_system(hw) === R "parent root system mismatch" @req is_dominant(hw) "not a dominant weight" @@ -1490,12 +1499,7 @@ function dominant_character(R::RootSystem, hw::WeightLatticeElem) end end end - # return char - return Dict(Int.(_vec(coefficients(w))) => m for (w, m) in char) -end - -function dominant_character(R::RootSystem, hw::Vector{<:IntegerUnion}) - return dominant_character(R, WeightLatticeElem(R, hw)) + return char end @doc raw""" @@ -1525,25 +1529,28 @@ Dict{Vector{Int64}, Int64} with 8 entries: ``` """ function character(R::RootSystem, hw::WeightLatticeElem) + char = _character(R, hw) + return Dict(Int.(_vec(coefficients(w))) => m for (w, m) in char) +end + +function character(R::RootSystem, hw::Vector{<:IntegerUnion}) + return character(R, WeightLatticeElem(R, hw)) +end + +function _character(R::RootSystem, hw::WeightLatticeElem) T = Int @req root_system(hw) === R "parent root system mismatch" @req is_dominant(hw) "not a dominant weight" - dom_char = dominant_character(R, hw) + dom_char = _dominant_character(R, hw) char = Dict{WeightLatticeElem,T}() - for (w_, m) in dom_char - w = WeightLatticeElem(R, w_) + for (w, m) in dom_char for w_conj in weyl_orbit(w) push!(char, w_conj => m) end end - # return char - return Dict(Int.(_vec(coefficients(w))) => m for (w, m) in char) -end - -function character(R::RootSystem, hw::Vector{<:IntegerUnion}) - return character(R, WeightLatticeElem(R, hw)) + return char end @doc raw""" From aa41156e487a0dea7586827d9c40d7a24e4746d7 Mon Sep 17 00:00:00 2001 From: Thomas Breuer Date: Mon, 18 Nov 2024 22:44:20 +0100 Subject: [PATCH 77/84] improvements for cosets (#4302) * improvements for cosets - access defining data via functions not fields - rename `acting_domain` to `acting_group` - improve documentation - `in` for left/right cosets now delegates to a membership test in a group - iterator for left/right cosets now uses a group iterator * address a comment * change parameterization of the `GroupCoset` type Add the type of the *subgroup* as a parameter, then we can prescribe a better `Base.IteratorSize(::Type{<:GroupCoset})`, as proposed in #4289. Note that in principle, we could omit the type of the *big group* from the parameters since it is the `parent_type` of the element type parameter. (The design of the `GroupCoset` type dates back to the times when we thought that a group has the same type as its subgroups. At the time when this idea was given up, I should have changed `GroupCoset` to take the *subgroup* type as a parameter.) * make `@inferred` happy This was tricky: The error messages were misleading, the problem occurred on a lower level. --- docs/src/Groups/subgroups.md | 23 +- docs/src/Groups/tom.md | 4 +- src/Groups/cosets.jl | 268 ++++++++++++++++---- src/Groups/gsets.jl | 10 +- src/deprecations.jl | 4 + src/exports.jl | 1 - test/Groups/gsets.jl | 2 +- test/Groups/matrixgroups.jl | 2 +- test/Groups/subgroups_and_cosets.jl | 13 +- test/book/cornerstones/groups/actions.jlcon | 2 +- 10 files changed, 258 insertions(+), 71 deletions(-) diff --git a/docs/src/Groups/subgroups.md b/docs/src/Groups/subgroups.md index fccfea3e6bc8..811a95dcd917 100644 --- a/docs/src/Groups/subgroups.md +++ b/docs/src/Groups/subgroups.md @@ -112,24 +112,31 @@ subgroup_classes(G::GAPGroup) ```@docs GroupCoset +group(C::GroupCoset) +acting_group(C::GroupCoset) +representative(C::GroupCoset) right_coset(H::GAPGroup, g::GAPGroupElem) left_coset(H::GAPGroup, g::GAPGroupElem) -is_right(c::GroupCoset) -is_left(c::GroupCoset) -is_bicoset(C::GroupCoset) -acting_domain(C::GroupCoset) -representative(C::GroupCoset) +is_right(C::GroupCoset) +is_left(C::GroupCoset) right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) left_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) right_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup left_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup +is_bicoset(C::GroupCoset) +``` + +```@docs GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem} -double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup) -double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup +group(C::GroupDoubleCoset) left_acting_group(C::GroupDoubleCoset) right_acting_group(C::GroupDoubleCoset) representative(C::GroupDoubleCoset) +double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup) +double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup +``` + +```@docs order(C::Union{GroupCoset,GroupDoubleCoset}) Base.rand(C::Union{GroupCoset,GroupDoubleCoset}) -intersect(V::AbstractVector{Union{<: GAPGroup, GroupCoset, GroupDoubleCoset}}) ``` diff --git a/docs/src/Groups/tom.md b/docs/src/Groups/tom.md index 05f64e78bfe2..c871d647c8bd 100644 --- a/docs/src/Groups/tom.md +++ b/docs/src/Groups/tom.md @@ -11,8 +11,8 @@ Therefore a table of marks is sometimes called a *Burnside matrix*. The table of marks of a finite group ``G`` is a matrix whose rows and columns are labelled by the conjugacy classes of subgroups of ``G`` and where for two subgroups ``H`` and ``K`` the ``(H, K)``-entry is the number of -fixed points of ``K`` in the transitive action of ``G`` on the cosets of ``H`` -in ``G``. +fixed points of ``K`` in the transitive action of ``G`` on the right cosets +of ``H`` in ``G``. So the table of marks characterizes the set of all permutation representations of ``G``. Moreover, the table of marks gives a compact description of the diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index e637a543d01f..66032af7386b 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -1,20 +1,35 @@ # T=type of the group, S=type of the element @doc raw""" - GroupCoset{T<: Group, S <: GAPGroupElem} + GroupCoset{TG <: GAPGroup, TH <: GAPGroup, S <: GAPGroupElem} -Type of group cosets. -Two cosets are equal if, and only if, they are both left (resp. right) +Type of right and left cosets of subgroups in groups. + +For an element $g$ in a group $G$, and a subgroup $H$ of $G$, +the set $Hg = \{ hg; h \in H \}$ is a right coset of $H$ in $G$, +and the set $gH = \{ gh; h \in H \}$ is a left coset of $H$ in $G$. + +- [`group(C::GroupCoset)`](@ref) returns $G$. + +- [`acting_group(C::GroupCoset)`](@ref) returns $H$. + +- [`representative(C::GroupCoset)`](@ref) returns an element + (the same element for each call) of `C`. + +- [`is_right(C::GroupCoset)`](@ref) and [`is_left(C::GroupCoset)`](@ref) + return whether `C` is a right or left coset, respectively. + +Two cosets are equal if and only if they are both left or right, respectively, and they contain the same elements. """ -struct GroupCoset{T<: GAPGroup, S <: GAPGroupElem} - G::T # big group containing the subgroup and the element - H::GAPGroup # subgroup (may have a different type) +struct GroupCoset{TG <: GAPGroup, TH <: GAPGroup, S <: GAPGroupElem} + G::TG # big group containing the subgroup and the element + H::TH # subgroup (may have a different type) repr::S # element side::Symbol # says if the coset is left or right X::Ref{GapObj} # GapObj(H*repr) - function GroupCoset(G::T, H::GAPGroup, representative::S, side::Symbol) where {T<: GAPGroup, S<:GAPGroupElem} - return new{T, S}(G, H, representative, side, Ref{GapObj}()) + function GroupCoset(G::TG, H::TH, representative::S, side::Symbol) where {TG <: GAPGroup, TH <: GAPGroup, S <:GAPGroupElem} + return new{TG, TH, S}(G, H, representative, side, Ref{GapObj}()) end end @@ -27,11 +42,11 @@ GAP.@install function GapObj(obj::GroupCoset) obj.X[] = GAPWrap.RightCoset(GAPWrap.ConjugateSubgroup(GapObj(obj.H), GAPWrap.Inverse(g)), g) end end - return obj.X[] + return obj.X[]::GapObj end Base.hash(x::GroupCoset, h::UInt) = h # FIXME -Base.eltype(::Type{GroupCoset{T,S}}) where {T,S} = S +Base.eltype(::Type{GroupCoset{TG, TH, S}}) where {TG, TH, S} = S function ==(C1::GroupCoset, C2::GroupCoset) H = C1.H @@ -167,37 +182,64 @@ function Base.:*(c::GroupCoset, d::GroupCoset) return double_coset(c.H, representative(c)*representative(d), d.H) end + """ - acting_domain(C::GroupCoset) + group(C::GroupCoset) -If `C` = `Hx` or `xH`, return `H`. +Return the group `G` that is the parent of all elements in `C`. +That is, `C` is a left or right coset of a subgroup of `G` in `G`. # Examples ```jldoctest julia> G = symmetric_group(5) Sym(5) -julia> g = perm(G,[3,4,1,5,2]) -(1,3)(2,4,5) +julia> H = sylow_subgroup(G, 2)[1] +Permutation group of degree 5 and order 8 + +julia> C = right_coset(H, gen(G, 1)) +Right coset of permutation group of degree 5 and order 8 + with representative (1,2,3,4,5) + in Sym(5) + +julia> group(C) == G +true +``` +""" +group(C::GroupCoset) = C.G + + +""" + acting_group(C::GroupCoset) + +Return the group `H` such that `C` is `Hx` (if `C` is a right coset) +or `xH` (if `C` is a left coset), for an element `x` in `C`. + +# Examples +```jldoctest +julia> G = symmetric_group(5) +Sym(5) julia> H = symmetric_group(3) Sym(3) -julia> gH = left_coset(H,g) -Left coset of Sym(3) - with representative (1,3)(2,4,5) +julia> C = right_coset(H, gen(G, 1)) +Right coset of Sym(3) + with representative (1,2,3,4,5) in Sym(5) -julia> acting_domain(gH) -Sym(3) +julia> acting_group(C) == H +true ``` """ -acting_domain(C::GroupCoset) = C.H +acting_group(C::GroupCoset) = C.H """ representative(C::GroupCoset) -If `C` = `Hx` or `xH`, return `x`. +Return an element `x` in `group(C)` such that +`C` = `Hx` (if `C` is a right coset) +or `xH` (if `C` is a left coset). # Examples ```jldoctest @@ -210,12 +252,12 @@ julia> g = perm(G,[3,4,1,5,2]) julia> H = symmetric_group(3) Sym(3) -julia> gH = left_coset(H, g) -Left coset of Sym(3) +julia> Hg = right_coset(H, g) +Right coset of Sym(3) with representative (1,3)(2,4,5) in Sym(5) -julia> representative(gH) +julia> representative(Hg) (1,3)(2,4,5) ``` """ @@ -225,8 +267,10 @@ representative(C::GroupCoset) = C.repr """ is_bicoset(C::GroupCoset) -Return whether `C` is simultaneously a right coset and a left coset for the same subgroup `H`. This -is the case if and only if the coset representative normalizes the acting domain subgroup. +Return whether `C` is simultaneously a right coset and a left coset +for the same subgroup `H`. +This is the case if and only if the coset representative normalizes +`acting_group(C)`. # Examples ```jldoctest @@ -268,6 +312,8 @@ Return the G-set that describes the right cosets of `H` in `G`. If `check == false`, do not check whether `H` is a subgroup of `G`. +Use [`right_transversal`](@ref) to compute the vector of coset representatives. + # Examples ```jldoctest julia> G = symmetric_group(4) @@ -282,7 +328,7 @@ Right cosets of Sym(4) julia> collect(rc) -4-element Vector{GroupCoset{PermGroup, PermGroupElem}}: +4-element Vector{GroupCoset{PermGroup, PermGroup, PermGroupElem}}: Right coset of H with representative () Right coset of H with representative (1,4) Right coset of H with representative (1,4,2) @@ -290,7 +336,6 @@ julia> collect(rc) ``` """ function right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) -#T _check_compatible(G, H) ? return GSetBySubgroupTransversal(G, H, :right, check = check) end @@ -301,6 +346,8 @@ Return the G-set that describes the left cosets of `H` in `G`. If `check == false`, do not check whether `H` is a subgroup of `G`. +Use [`left_transversal`](@ref) to compute the vector of coset representatives. + # Examples ```jldoctest julia> G = symmetric_group(4) @@ -400,6 +447,8 @@ they are created anew with each access to the transversal. If `check == false`, do not check whether `H` is a subgroup of `G`. +Use [`right_cosets`](@ref) to compute the G-set of right cosets. + # Examples ```jldoctest julia> G = symmetric_group(4) @@ -440,6 +489,8 @@ they are created anew with each access to the transversal. If `check == false`, do not check whether `H` is a subgroup of `G`. +Use [`left_cosets`](@ref) to compute the G-set of left cosets. + # Examples ```jldoctest julia> G = symmetric_group(4) @@ -470,22 +521,25 @@ function left_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup w GAPWrap.RightTransversal(GapObj(G), GapObj(H))) end -Base.IteratorSize(::Type{<:GroupCoset}) = Base.SizeUnknown() -Base.iterate(G::GroupCoset) = iterate(G, GAPWrap.Iterator(GapObj(G))) -function Base.iterate(G::GroupCoset, state) - GAPWrap.IsDoneIterator(state) && return nothing - i = GAPWrap.NextIterator(state)::GapObj - return group_element(G.G, i), state -end +@doc raw""" + GroupDoubleCoset{T<: Group, S <: GAPGroupElem} +Type of double cosets of subgroups in groups. +For an element $g$ in a group $G$, and two subgroups $H$, $K$ of $G$, +the set $HgK = \{ hgk; h \in H, k \in K \}$ is a $H-K$-double coset in $G$. -@doc raw""" - GroupDoubleCoset{T<: Group, S <: GAPGroupElem} +- [`group(C::GroupDoubleCoset)`](@ref) returns $G$. + +- [`left_acting_group(C::GroupDoubleCoset)`](@ref) returns $H$. -Group double coset. -Two double cosets are equal if, and only if, they contain the same elements. +- [`right_acting_group(C::GroupDoubleCoset)`](@ref) returns $H$. + +- [`representative(C::GroupDoubleCoset)`](@ref) returns an element + (the same element for each call) of `C`. + +Two double cosets are equal if and only if they contain the same elements. """ struct GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem} # T=type of the group, S=type of the element @@ -495,17 +549,17 @@ struct GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem} repr::S X::Ref{GapObj} size::Ref{ZZRingElem} - + function GroupDoubleCoset(G::T, H::GAPGroup, K::GAPGroup, representative::S) where {T<: GAPGroup, S<:GAPGroupElem} return new{T, S}(G, H, K, representative, Ref{GapObj}(), Ref{ZZRingElem}()) - end + end end GAP.@install function GapObj(C::GroupDoubleCoset) if !isassigned(C.X) C.X[] = GAPWrap.DoubleCoset(GapObj(C.H), GapObj(representative(C)), GapObj(C.K)) end - return C.X[] + return C.X[]::GapObj end Base.hash(x::GroupDoubleCoset, h::UInt) = h # FIXME @@ -613,7 +667,7 @@ function double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T C.size[] = ZZRingElem(n) res[i] = C end - return res + return res end """ @@ -632,10 +686,9 @@ function order(::Type{T}, C::GroupDoubleCoset) where T <: IntegerUnion if !isassigned(C.size) C.size[] = ZZRingElem(GAPWrap.Size(GapObj(C))) end - return T(C.size[]) -end + return T(C.size[])::T +end -Base.length(C::Union{GroupCoset,GroupDoubleCoset}) = order(C) """ rand(rng::Random.AbstractRNG = Random.GLOBAL_RNG, C::Union{GroupCoset,GroupDoubleCoset}) @@ -650,28 +703,145 @@ function Base.rand(rng::Random.AbstractRNG, C::Union{GroupCoset,GroupDoubleCoset return group_element(C.G, s) end + +""" + group(C::GroupDoubleCoset) + +Return the group `G` that is the parent of all elements in `C`. +That is, `C` is a double coset of two subgroups of `G` in `G`. + +# Examples +```jldoctest +julia> G = symmetric_group(5) +Sym(5) + +julia> H = symmetric_group(3); K = symmetric_group(2); + +julia> HgK = double_coset(H, gen(G, 1), K) +Double coset of Sym(3) + and Sym(2) + with representative (1,2,3,4,5) + in Sym(5) + +julia> group(HgK) == G +true +``` +""" +group(C::GroupDoubleCoset) = C.G + """ representative(C::GroupDoubleCoset) -Return a representative `x` of the double coset `C` = `HxK`. +Return an element `x` of the double coset `C` = `HxK`. + +# Examples +```jldoctest +julia> G = symmetric_group(5) +Sym(5) + +julia> H = symmetric_group(3); K = symmetric_group(2); + +julia> HgK = double_coset(H, gen(G, 1), K) +Double coset of Sym(3) + and Sym(2) + with representative (1,2,3,4,5) + in Sym(5) + +julia> representative(HgK) +(1,2,3,4,5) +``` """ representative(C::GroupDoubleCoset) = C.repr """ left_acting_group(C::GroupDoubleCoset) -Given a double coset `C` = `HxK`, return `H`. +Return `H` if `C` = `HxK`. + +# Examples +```jldoctest +julia> G = symmetric_group(5) +Sym(5) + +julia> H = symmetric_group(3); K = symmetric_group(2); + +julia> HgK = double_coset(H, gen(G, 1), K) +Double coset of Sym(3) + and Sym(2) + with representative (1,2,3,4,5) + in Sym(5) + +julia> left_acting_group(HgK) == H +true +``` """ left_acting_group(C::GroupDoubleCoset) = C.H """ right_acting_group(C::GroupDoubleCoset) -Given a double coset `C` = `HxK`, return `K`. +Return `K` if `C` = `HxK`. + +# Examples +```jldoctest +julia> G = symmetric_group(5) +Sym(5) + +julia> H = symmetric_group(3); K = symmetric_group(2); + +julia> HgK = double_coset(H, gen(G, 1), K) +Double coset of Sym(3) + and Sym(2) + with representative (1,2,3,4,5) + in Sym(5) + +julia> right_acting_group(HgK) == K +true +``` """ right_acting_group(C::GroupDoubleCoset) = C.K + +############################################################################ +# +# iteration over cosets +# +function Base.in(g::GAPGroupElem, C::GroupCoset) + if is_right(C) + return g / representative(C) in acting_group(C) + else + return g \ representative(C) in acting_group(C) + end +end + +function Base.in(g::GAPGroupElem, C::GroupDoubleCoset) + return GapObj(g) in GapObj(C) +#TODO: avoid delegation to GAP? +# (GAP uses `RepresentativesContainedRightCosets`, `CanonicalRightCosetElement`) +end + +Base.IteratorSize(::Type{<:GroupCoset{TG, TH, S}}) where {TG, TH, S} = Base.IteratorSize(TH) + +# need this function just for the iterator +Base.length(C::Union{GroupCoset,GroupDoubleCoset}) = order(Int, C) + +function Base.iterate(C::GroupCoset) + return iterate(C, iterate(acting_group(C))) +end + +function Base.iterate(C::GroupCoset, state) + state === nothing && return nothing + G = group(C) + if is_right(C) + res = G(state[1]) * representative(C) + else + res = representative(C) * G(state[1]) + end + return res, iterate(acting_group(C), state[2]) +end + Base.IteratorSize(::Type{<:GroupDoubleCoset}) = Base.SizeUnknown() +Base.IteratorSize(::Type{GroupDoubleCoset{PermGroup, PermGroupElem}}) = Base.HasLength() Base.iterate(G::GroupDoubleCoset) = iterate(G, GAPWrap.Iterator(GapObj(G))) diff --git a/src/Groups/gsets.jl b/src/Groups/gsets.jl index a22df1cd98de..0c94b49427fe 100644 --- a/src/Groups/gsets.jl +++ b/src/Groups/gsets.jl @@ -598,7 +598,7 @@ The fields are - the (left or right) transversal, of type `SubgroupTransversal{T, S, E}`, - the dictionary used to store attributes (orbits, elements, ...). """ -@attributes mutable struct GSetBySubgroupTransversal{T, S, E} <: GSet{T,GroupCoset{T, E}} +@attributes mutable struct GSetBySubgroupTransversal{T, S, E} <: GSet{T,GroupCoset{T, S, E}} group::T subgroup::S side::Symbol @@ -672,7 +672,7 @@ function Base.iterate(Omega::GSetBySubgroupTransversal, state = 1) end end -Base.eltype(::Type{GSetBySubgroupTransversal{T, S, E}}) where {S, T, E} = GroupCoset{T, E} +Base.eltype(::Type{GSetBySubgroupTransversal{T, S, E}}) where {S, T, E} = GroupCoset{T, S, E} function Base.getindex(Omega::GSetBySubgroupTransversal, i::Int) if Omega.side == :right @@ -684,7 +684,7 @@ end is_transitive(Omega::GSetBySubgroupTransversal) = true -function orbit(G::T, omega::GroupCoset{T, S}) where T <: GAPGroup where S +function orbit(G::T, omega::GroupCoset{T, TH, S}) where {T <: GAPGroup, TH <: GAPGroup, S} @req G == omega.G "omega must be a left or right coset in G" return GSetBySubgroupTransversal(G, omega.H, omega.side, check = false) end @@ -692,7 +692,7 @@ end # One problem would be that `omega` would not be a point in the orbit, # according to the definition of equality for cosets. -function orbit(Omega::GSetBySubgroupTransversal{T, S, E}, omega::GroupCoset{T, E}) where T <: GAPGroup where S <: GAPGroup where E +function orbit(Omega::GSetBySubgroupTransversal{T, S, E}, omega::GroupCoset{T, S, E}) where {T <: GAPGroup, S <: GAPGroup, E} @req (Omega.group == omega.G && Omega.subgroup == omega.H && Omega.side == omega.side) "omega is not in Omega" return Omega end @@ -874,8 +874,6 @@ end ############################################################################ -acting_domain(Omega::GSet) = acting_group(Omega) - Base.length(Omega::GSetByElements) = length(elements(Omega)) Base.length(::Type{T}, Omega::GSetByElements) where T <: IntegerUnion = T(length(elements(Omega))) diff --git a/src/deprecations.jl b/src/deprecations.jl index 86c6106e18a2..fee339161138 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -148,3 +148,7 @@ Base.@deprecate_binding in_linear_system is_in_linear_system @deprecate minimal_generating_set(G::GAPGroup) minimal_size_generating_set(G) @deprecate has_minimal_generating_set(G::GAPGroup) has_minimal_size_generating_set(G) @deprecate set_minimal_generating_set(G::GAPGroup, v) set_minimal_size_generating_set(G, v) + +# deprecated for 1.3 +@deprecate acting_domain(C::GroupCoset) acting_group(C) +@deprecate acting_domain(Omega::GSet) acting_group(Omega) diff --git a/src/exports.jl b/src/exports.jl index b36c6c42271a..88d5d00374e9 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -201,7 +201,6 @@ export abelian_group export abelian_invariants export abelian_invariants_schur_multiplier export absolute_primary_decomposition -export acting_domain export acting_group export acting_subgroup export action diff --git a/test/Groups/gsets.jl b/test/Groups/gsets.jl index a252160ae5c6..86377eef8bdd 100644 --- a/test/Groups/gsets.jl +++ b/test/Groups/gsets.jl @@ -103,7 +103,7 @@ G = symmetric_group(6) Omega = gset(G, [Set([1, 2])]) @test representative(Omega) in Omega - @test acting_domain(Omega) == G + @test acting_group(Omega) == G # wrapped elements of G-sets G = symmetric_group(4) diff --git a/test/Groups/matrixgroups.jl b/test/Groups/matrixgroups.jl index 61ca115ce1c7..bbc14add47e5 100644 --- a/test/Groups/matrixgroups.jl +++ b/test/Groups/matrixgroups.jl @@ -622,7 +622,7 @@ end lc = x*H @test order(lc)==order(H) @test representative(lc)==x - @test acting_domain(lc)==H + @test acting_group(lc)==H @test x in lc C = centralizer(G,x)[1] @test order(C)==64 diff --git a/test/Groups/subgroups_and_cosets.jl b/test/Groups/subgroups_and_cosets.jl index 298fc50958c0..fb51e3a1f881 100644 --- a/test/Groups/subgroups_and_cosets.jl +++ b/test/Groups/subgroups_and_cosets.jl @@ -167,6 +167,10 @@ end @test index(G, H) == 4 C = right_coset(H, G[1]) + @test group(C) == G + @test acting_group(C) == H + @test representative(C) in C + @test all(x -> x/G[1] in H, C) @test is_right(C) @test order(C) == length(collect(C)) @test order(C) isa ZZRingElem @@ -197,8 +201,8 @@ end @test rc==H*x @test lc==x*H @test dc==H*x*K - @test acting_domain(rc) == H - @test acting_domain(lc) == H + @test acting_group(rc) == H + @test acting_group(lc) == H @test left_acting_group(dc) == H @test right_acting_group(dc) == K @test representative(rc) == x @@ -219,6 +223,10 @@ end @test issubset(left_coset(K,x),dc) @test !is_bicoset(rc) + @inferred GapObj(rc) + @inferred GapObj(lc) + @inferred GapObj(dc) + @test rc == H*x @test lc == x*H @test dc == H*x*K @@ -271,6 +279,7 @@ end x = G([2,3,4,5,1]) dc = double_coset(H,x,K) dc1 = double_coset(H, H[1]*x, K) + @test group(dc) == G @test representative(dc) != representative(dc1) @test dc == dc1 L = double_cosets(G,H,K) diff --git a/test/book/cornerstones/groups/actions.jlcon b/test/book/cornerstones/groups/actions.jlcon index b76d6835384b..8afa776b146e 100644 --- a/test/book/cornerstones/groups/actions.jlcon +++ b/test/book/cornerstones/groups/actions.jlcon @@ -17,7 +17,7 @@ julia> acting_group(r) Pc group of order 6 julia> collect(r) -3-element Vector{GroupCoset{PcGroup, PcGroupElem}}: +3-element Vector{GroupCoset{PcGroup, SubPcGroup, PcGroupElem}}: Right coset of U with representative of ... Right coset of U with representative f2 Right coset of U with representative f2^2 From 23716cc4c3d5c4d4fb3e313153233dad24b6b0bb Mon Sep 17 00:00:00 2001 From: Erik Paemurru Date: Wed, 20 Nov 2024 18:18:32 +0100 Subject: [PATCH 78/84] Fix zero-dimensional cone in `cones` --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 13 +++++++++++-- test/PolyhedralGeometry/polyhedral_fan.jl | 9 ++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index 2b157f65c563..c7827957395a 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -212,8 +212,17 @@ julia> cones(PF, 2) """ function cones(PF::_FanLikeType, cone_dim::Int) l = cone_dim - length(lineality_space(PF)) - l < 1 && return nothing - return SubObjectIterator{Cone{_get_scalar_type(PF)}}( + t = Cone{_get_scalar_type(PF)} + l < 0 && return _empty_subobjectiterator(t, PF) + l == 0 && length(lineality_space(PF)) == 0 && return SubObjectIterator{t}( + PF, (_, _, _) -> cone(zeros(Int, ambient_dim(PF))), 1, NamedTuple() + ) + + # The function `lineality_space` returns a ray even in the case where + # the lineality space is actually a line. + l == 0 && length(lineality_space(PF)) > 0 && return error("Not implemented.") + + return SubObjectIterator{t}( PF, _cone_of_dim, size(Polymake.fan.cones_of_dim(pm_object(PF), l), 1), (c_dim=l,) ) end diff --git a/test/PolyhedralGeometry/polyhedral_fan.jl b/test/PolyhedralGeometry/polyhedral_fan.jl index 133ba92b6a59..30aa437e5bb6 100644 --- a/test/PolyhedralGeometry/polyhedral_fan.jl +++ b/test/PolyhedralGeometry/polyhedral_fan.jl @@ -58,11 +58,18 @@ @test size(cones(F2, 2)) == (2,) @test lineality_space(cones(F2, 2)[1]) == [[0, 1, 0]] @test rays.(cones(F2, 2)) == [[], []] - @test isnothing(cones(F2, 1)) @test _check_im_perm_rows(ray_indices(cones(F1, 2)), incidence1) @test _check_im_perm_rows(incidence_matrix(cones(F1, 2)), incidence1) @test _check_im_perm_rows(cones(IncidenceMatrix, F1, 2), incidence1) + A3 = affine_space(NormalToricVariety, 3) + @test length(cones(A3, 1)) == 3 + @test length(cones(A3, 0)) == 1 + @test length(cones(A3, -1)) == 0 + @test cones(A3, 1) isa SubObjectIterator{Cone{QQFieldElem}} + @test cones(A3, 0) isa SubObjectIterator{Cone{QQFieldElem}} + @test cones(A3, -1) isa SubObjectIterator{Cone{QQFieldElem}} + II = ray_indices(maximal_cones(NFsquare)) NF0 = polyhedral_fan(II, rays(NFsquare)) @test n_rays(NF0) == 4 From 0bdde4526aac67772f63346ea00fb01b2e562928 Mon Sep 17 00:00:00 2001 From: Erik Paemurru Date: Wed, 20 Nov 2024 18:37:27 +0100 Subject: [PATCH 79/84] Fix formatting --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index c7827957395a..05270b317412 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -214,9 +214,14 @@ function cones(PF::_FanLikeType, cone_dim::Int) l = cone_dim - length(lineality_space(PF)) t = Cone{_get_scalar_type(PF)} l < 0 && return _empty_subobjectiterator(t, PF) - l == 0 && length(lineality_space(PF)) == 0 && return SubObjectIterator{t}( - PF, (_, _, _) -> cone(zeros(Int, ambient_dim(PF))), 1, NamedTuple() - ) + + if l == 0 + if length(lineality_space(PF)) == 0 + return SubObjectIterator{t}( + PF, (_, _, _) -> cone(zeros(Int, ambient_dim(PF))), 1, NamedTuple() + ) + end + end # The function `lineality_space` returns a ray even in the case where # the lineality space is actually a line. From 6045366c3f319551c1a8257d814e02735de1df6c Mon Sep 17 00:00:00 2001 From: Erik Paemurru <143521159+paemurru@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:02:57 +0100 Subject: [PATCH 80/84] Update src/PolyhedralGeometry/PolyhedralFan/properties.jl Co-authored-by: Benjamin Lorenz --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index 05270b317412..dd4b48bf41b2 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -213,7 +213,7 @@ julia> cones(PF, 2) function cones(PF::_FanLikeType, cone_dim::Int) l = cone_dim - length(lineality_space(PF)) t = Cone{_get_scalar_type(PF)} - l < 0 && return _empty_subobjectiterator(t, PF) + (l < 0 || dim(PF) == -1) && return _empty_subobjectiterator(t, PF) if l == 0 if length(lineality_space(PF)) == 0 From 4c2f9099f363400a6ca9023ed8b2b807ce8bef65 Mon Sep 17 00:00:00 2001 From: Erik Paemurru <143521159+paemurru@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:05:28 +0100 Subject: [PATCH 81/84] Update src/PolyhedralGeometry/PolyhedralFan/properties.jl Co-authored-by: Benjamin Lorenz --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index dd4b48bf41b2..98b90fc77cb8 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -216,11 +216,9 @@ function cones(PF::_FanLikeType, cone_dim::Int) (l < 0 || dim(PF) == -1) && return _empty_subobjectiterator(t, PF) if l == 0 - if length(lineality_space(PF)) == 0 - return SubObjectIterator{t}( - PF, (_, _, _) -> cone(zeros(Int, ambient_dim(PF))), 1, NamedTuple() - ) - end + return SubObjectIterator{t}( + PF, (_, _, _) -> positive_hull(coefficient_field(PF), zeros(Int, ambient_dim(PF)), lineality_space(PF)), 1, NamedTuple() + ) end # The function `lineality_space` returns a ray even in the case where From 5955eaa8ea0b98cf092673cd37a96ecfee925534 Mon Sep 17 00:00:00 2001 From: Erik Paemurru <143521159+paemurru@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:05:38 +0100 Subject: [PATCH 82/84] Update src/PolyhedralGeometry/PolyhedralFan/properties.jl Co-authored-by: Benjamin Lorenz --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index 98b90fc77cb8..f6663b3299d2 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -221,9 +221,6 @@ function cones(PF::_FanLikeType, cone_dim::Int) ) end - # The function `lineality_space` returns a ray even in the case where - # the lineality space is actually a line. - l == 0 && length(lineality_space(PF)) > 0 && return error("Not implemented.") return SubObjectIterator{t}( PF, _cone_of_dim, size(Polymake.fan.cones_of_dim(pm_object(PF), l), 1), (c_dim=l,) From 5a737af78b4358675bbea8313acab7ab30dd9dc8 Mon Sep 17 00:00:00 2001 From: antonydellavecchia Date: Thu, 21 Nov 2024 11:45:31 +0100 Subject: [PATCH 83/84] fixes regression and adds test for file format paper (#4335) * fixes regression and adds test for file format paper * fixes version numbers for release and includes test * fix upgrade script * fixes for tests * upgrade for 1.3.0 * adds line to loading test * fixes for upgrade recurssion * reset state after loading field --- src/Serialization/Fields.jl | 4 +- src/Serialization/Upgrades/1.3.0.jl | 24 +++++ src/Serialization/Upgrades/main.jl | 1 + test/Serialization/loading.jl | 38 ++++--- test/Serialization/polynomial-example.mrdi | 36 +++++++ test/Serialization/upgrades/GF_2.json | 11 +++ test/Serialization/upgrades/GF_2_2.json | 45 +++++++++ test/Serialization/upgrades/poly1.0.5.json | 110 +++++++++++++++++++++ test/Serialization/upgrades/runtests.jl | 7 ++ 9 files changed, 259 insertions(+), 17 deletions(-) create mode 100644 src/Serialization/Upgrades/1.3.0.jl create mode 100644 test/Serialization/polynomial-example.mrdi create mode 100644 test/Serialization/upgrades/GF_2.json create mode 100644 test/Serialization/upgrades/GF_2_2.json create mode 100644 test/Serialization/upgrades/poly1.0.5.json diff --git a/src/Serialization/Fields.jl b/src/Serialization/Fields.jl index 4176250d0e58..043ff7c23184 100644 --- a/src/Serialization/Fields.jl +++ b/src/Serialization/Fields.jl @@ -191,7 +191,7 @@ function save_object(s::SerializerState, K::FqField) save_object(s, order(K)) else save_data_dict(s) do - save_typed_object(s, defining_polynomial(K)) + save_typed_object(s, defining_polynomial(K), :def_pol) end end end @@ -202,7 +202,7 @@ function load_object(s::DeserializerState, ::Type{<: FqField}) order = ZZRingElem(node) return finite_field(order)[1] else - def_pol = load_typed_object(s) + def_pol = load_typed_object(s, :def_pol) return finite_field(def_pol, cached=false)[1] end end diff --git a/src/Serialization/Upgrades/1.3.0.jl b/src/Serialization/Upgrades/1.3.0.jl new file mode 100644 index 000000000000..1c721bf8f489 --- /dev/null +++ b/src/Serialization/Upgrades/1.3.0.jl @@ -0,0 +1,24 @@ +push!(upgrade_scripts_set, UpgradeScript( + v"1.3.0", + function upgrade_1_3_0(s::UpgradeState, dict::Dict) + upgraded_dict = dict + if haskey(dict, :_type) && dict[:_type] == "FqField" + if dict[:data] isa Dict + if !(haskey(dict[:data], :def_pol)) + upgraded_dict[:data][:def_pol] = copy(dict[:data]) + end + end + elseif haskey(dict, :data) && dict[:data] isa Dict + upgraded_dict[:data] = upgrade_1_3_0(s, dict[:data]) + end + if haskey(dict, :_refs) + upgraded_refs = Dict() + for (k, v) in dict[:_refs] + upgraded_refs[k] = upgrade_1_3_0(s, v) + end + upgraded_dict[:_refs] = upgraded_refs + end + + return upgraded_dict + end +)) diff --git a/src/Serialization/Upgrades/main.jl b/src/Serialization/Upgrades/main.jl index fa297ff5e03f..f6c35f46589f 100644 --- a/src/Serialization/Upgrades/main.jl +++ b/src/Serialization/Upgrades/main.jl @@ -141,6 +141,7 @@ include("0.13.0.jl") include("0.15.0.jl") include("1.1.0.jl") include("1.2.0.jl") +include("1.3.0.jl") const upgrade_scripts = collect(upgrade_scripts_set) sort!(upgrade_scripts; by=version) diff --git a/test/Serialization/loading.jl b/test/Serialization/loading.jl index 299688d5f1b3..346bcd4b4245 100644 --- a/test/Serialization/loading.jl +++ b/test/Serialization/loading.jl @@ -1,17 +1,25 @@ @testset "loading" begin - - @testset "loading Vector{LinearProgram}" begin - c = cube(3) - LP0 = linear_program(c, [2,2,-3]) - LP1 = linear_program(c, [2,2,4]) - v = [LP0, LP1] - loaded = load(joinpath(@__DIR__,"vlp.json")) - @test length(v) == length(loaded) - @test feasible_region(loaded[1]) == feasible_region(loaded[2]) - @test feasible_region(loaded[1]) == feasible_region(LP0) - @test objective_function(loaded[1]) == objective_function(v[1]) - @test objective_function(loaded[2]) == objective_function(v[2]) - @test optimal_value(loaded[1]) == optimal_value(v[1]) - @test optimal_value(loaded[2]) == optimal_value(v[2]) - end + @testset "loading file format paper example" begin + F = GF(7, 2) + o = gen(F) + Fyz, (y, z) = F[:x, :y] + load(joinpath(@__DIR__,"polynomial-example.mrdi");) + loaded = load(joinpath(@__DIR__,"polynomial-example.mrdi"); params=Fyz) + @test loaded == 2*y^3*z^4 + 5*o*y + (o + 3)*z^2 + 1 + end + + @testset "loading Vector{LinearProgram}" begin + c = cube(3) + LP0 = linear_program(c, [2,2,-3]) + LP1 = linear_program(c, [2,2,4]) + v = [LP0, LP1] + loaded = load(joinpath(@__DIR__,"vlp.json")) + @test length(v) == length(loaded) + @test feasible_region(loaded[1]) == feasible_region(loaded[2]) + @test feasible_region(loaded[1]) == feasible_region(LP0) + @test objective_function(loaded[1]) == objective_function(v[1]) + @test objective_function(loaded[2]) == objective_function(v[2]) + @test optimal_value(loaded[1]) == optimal_value(v[1]) + @test optimal_value(loaded[2]) == optimal_value(v[2]) + end end diff --git a/test/Serialization/polynomial-example.mrdi b/test/Serialization/polynomial-example.mrdi new file mode 100644 index 000000000000..0ad82300e7b4 --- /dev/null +++ b/test/Serialization/polynomial-example.mrdi @@ -0,0 +1,36 @@ +{ + "_ns": { "Oscar": [ "https://github.com/oscar-system/Oscar.jl", "1.0.0" ] }, + "_type": { + "name": "MPolyRingElem", + "params": "869a359a-43d3-43f4-9821-0af9346be019" + }, + "data": [[["3", "4"], [["0", "2"]]], + [["0", "2"], [["0", "3"], ["1", "1"]]], + [["1", "0"], [["1", "5"]]], + [["0", "0"], [["0", "1"]]]], + "_refs": { + "152ac7bd-e85a-4b36-acc2-743ade2cad4f": { + "data": { "base_ring": { "data": "7", "_type": "FqField"}, + "symbols": ["x"] }, + "_type": "PolyRing" + }, + "869a359a-43d3-43f4-9821-0af9346be019": { + "data": { + "base_ring": "a8309b96-caec-443c-bedb-e23bb0634c14", + "symbols": [ "y", "z" ] + }, + "_type": "MPolyRing" }, + "a8309b96-caec-443c-bedb-e23bb0634c14": { + "data": { + "def_pol": { + "data": [["0", "1"], ["2", "1"]], + "_type": { + "name": "PolyRingElem", + "params": "152ac7bd-e85a-4b36-acc2-743ade2cad4f" + } + } + }, + "_type": "FqField" + } + } +} diff --git a/test/Serialization/upgrades/GF_2.json b/test/Serialization/upgrades/GF_2.json new file mode 100644 index 000000000000..812d8d1a467c --- /dev/null +++ b/test/Serialization/upgrades/GF_2.json @@ -0,0 +1,11 @@ +{ + "_ns": { + "Oscar": [ + "https://github.com/oscar-system/Oscar.jl", + "1.2.0" + ] + }, + "_type": "FqField", + "data": "2", + "id": "4e298c21-11cb-45f1-bac0-62e2df5e6454" +} diff --git a/test/Serialization/upgrades/GF_2_2.json b/test/Serialization/upgrades/GF_2_2.json new file mode 100644 index 000000000000..51d9a4e6b4da --- /dev/null +++ b/test/Serialization/upgrades/GF_2_2.json @@ -0,0 +1,45 @@ +{ + "_ns": { + "Oscar": [ + "https://github.com/oscar-system/Oscar.jl", + "1.2.0" + ] + }, + "_refs": { + "15ec4d5e-409d-4e80-a7a2-aa1b22b0db9d": { + "_type": "PolyRing", + "data": { + "base_ring": "4e298c21-11cb-45f1-bac0-62e2df5e6454", + "symbols": [ + "x" + ] + } + }, + "4e298c21-11cb-45f1-bac0-62e2df5e6454": { + "_type": "FqField", + "data": "2" + } + }, + "_type": "FqField", + "data": { + "_type": { + "name": "PolyRingElem", + "params": "15ec4d5e-409d-4e80-a7a2-aa1b22b0db9d" + }, + "data": [ + [ + "0", + "1" + ], + [ + "1", + "1" + ], + [ + "2", + "1" + ] + ] + }, + "id": "b2e7bf06-d89a-4fea-8bf4-215a61c16ca2" +} diff --git a/test/Serialization/upgrades/poly1.0.5.json b/test/Serialization/upgrades/poly1.0.5.json new file mode 100644 index 000000000000..b26acd897b02 --- /dev/null +++ b/test/Serialization/upgrades/poly1.0.5.json @@ -0,0 +1,110 @@ +{ + "_ns": { + "Oscar": [ + "https://github.com/oscar-system/Oscar.jl", + "1.0.5" + ] + }, + "_type": { + "name": "MPolyRingElem", + "params": "f0885e3e-71ff-4813-88fd-7ee05eeb3657" + }, + "data": [ + [ + [ + "3", + "4" + ], + [ + [ + "0", + "2" + ] + ] + ], + [ + [ + "1", + "0" + ], + [ + [ + "1", + "5" + ] + ] + ], + [ + [ + "0", + "2" + ], + [ + [ + "0", + "3" + ], + [ + "1", + "1" + ] + ] + ], + [ + [ + "0", + "0" + ], + [ + [ + "0", + "1" + ] + ] + ] + ], + "_refs": { + "f0885e3e-71ff-4813-88fd-7ee05eeb3657": { + "_type": "MPolyRing", + "data": { + "base_ring": "e06a6ac9-954b-4fb7-89c6-f2a25489440e", + "symbols": [ + "y", + "z" + ] + } + }, + "e06a6ac9-954b-4fb7-89c6-f2a25489440e": { + "_type": "FqField", + "data": { + "_type": { + "name": "PolyRingElem", + "params": "3a66dcbd-bd73-4bb7-8b99-42d9f5177893" + }, + "data": [ + [ + "0", + "1" + ], + [ + "2", + "1" + ] + ] + } + }, + "3a66dcbd-bd73-4bb7-8b99-42d9f5177893": { + "_type": "PolyRing", + "data": { + "base_ring": "221dfa92-69df-4b5a-8f30-166aafddfaa9", + "symbols": [ + "x" + ] + } + }, + "221dfa92-69df-4b5a-8f30-166aafddfaa9": { + "_type": "FqField", + "data": "7" + } + } +} diff --git a/test/Serialization/upgrades/runtests.jl b/test/Serialization/upgrades/runtests.jl index 222da8093629..851a121babe8 100644 --- a/test/Serialization/upgrades/runtests.jl +++ b/test/Serialization/upgrades/runtests.jl @@ -27,4 +27,11 @@ loaded = load(joinpath(@__DIR__, "file_version_less_than_1.2.0.json")); @test loaded isa Dict end + + @testset "< 1.3.0 Upgrade" begin + load(joinpath(@__DIR__, "GF_2_2.json")); + load(joinpath(@__DIR__, "GF_2.json")); + Oscar.reset_global_serializer_state() + load(joinpath(@__DIR__, "poly1.0.5.json")); + end end From 53ad85978a24fabb297cf1482533549be502f19e Mon Sep 17 00:00:00 2001 From: Erik Paemurru Date: Thu, 21 Nov 2024 12:47:27 +0100 Subject: [PATCH 84/84] Fix formatting --- src/PolyhedralGeometry/PolyhedralFan/properties.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/PolyhedralGeometry/PolyhedralFan/properties.jl b/src/PolyhedralGeometry/PolyhedralFan/properties.jl index f6663b3299d2..9a319a4b2f27 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/properties.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/properties.jl @@ -217,11 +217,15 @@ function cones(PF::_FanLikeType, cone_dim::Int) if l == 0 return SubObjectIterator{t}( - PF, (_, _, _) -> positive_hull(coefficient_field(PF), zeros(Int, ambient_dim(PF)), lineality_space(PF)), 1, NamedTuple() + PF, + (_, _, _) -> positive_hull( + coefficient_field(PF), zeros(Int, ambient_dim(PF)), lineality_space(PF) + ), + 1, + NamedTuple(), ) end - return SubObjectIterator{t}( PF, _cone_of_dim, size(Polymake.fan.cones_of_dim(pm_object(PF), l), 1), (c_dim=l,) )