diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 556a2710f3a6..7f5e5aec090a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -76,6 +76,7 @@ jobs: with: version: ${{ matrix.julia-version }} - uses: julia-actions/cache@v1 + if: runner.environment != 'self-hosted' with: cache-name: julia-cache;workflow=${{ github.workflow }};julia=${{ matrix.julia-version }};arch=${{ runner.arch }} include-matrix: false @@ -143,6 +144,7 @@ jobs: with: version: ${{ matrix.julia-version }} - uses: julia-actions/cache@v1 + if: runner.environment != 'self-hosted' with: cache-name: julia-cache;workflow=${{ github.workflow }};julia=${{ matrix.julia-version }};arch=${{ runner.arch }} include-matrix: false diff --git a/Artifacts.toml b/Artifacts.toml index 6c71301f1b22..6d7efc9b9c99 100644 --- a/Artifacts.toml +++ b/Artifacts.toml @@ -1,3 +1,11 @@ +[QSMDB] +git-tree-sha1 = "5c82e5ff3aa99f2aa660c9ffb3caa57ee535cc21" +lazy = true + + [[QSMDB.download]] + sha256 = "3f69aae0fca02d74764f709c622388c564e6e144a8b11347dadef5725a65b0a2" + url = "https://github.com/oscar-system/Oscar.jl/releases/download/archive-tag-1/qsmdb.tar.gz" + [gap_extraperfect] git-tree-sha1 = "084fa12573e5089ceb3299f9d341f244b415da52" lazy = true diff --git a/CITATION.cff b/CITATION.cff index 721eb7efbef4..ae3fec3e3f8e 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -32,6 +32,19 @@ preferred-citation: - family-names: "Joswig" given-names: "Michael" title: "The Computer Algebra System OSCAR: Algorithms and Examples" + collection-type: "Series" + collection-title: "Algorithms and Computation in Mathematics" + abstract: >- + The book is an invitation to use OSCAR. With discussions of + theoretical and algorithmic aspects included, it offers a + multitude of explicit code snippets. These are valuable for + interested researchers from graduate students through established + experts. year: 2024 + month: 8 + edition: 1 + issn: "1431-1550" + url: "https://link.springer.com/book/9783031621260" + volume: 32 publisher: name: "Springer" diff --git a/Project.toml b/Project.toml index c5175419f303..d3b395874337 100644 --- a/Project.toml +++ b/Project.toml @@ -29,7 +29,7 @@ AbstractAlgebra = "0.41.3" AlgebraicSolving = "0.4.15" Distributed = "1.6" GAP = "0.11.0" -Hecke = "0.31.5" +Hecke = "0.32.0" JSON = "^0.20, ^0.21" JSON3 = "1.13.2" LazyArtifacts = "1.6" diff --git a/README.md b/README.md index b538554620cb..08b868b19bdf 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,11 @@ If you are using BibTeX, you can use the following BibTeX entries: year = {2024}, publisher = {Springer}, series = {Algorithms and {C}omputation in {M}athematics}, + volume = {32}, + edition = {1}, + url = {https://link.springer.com/book/9783031621260}, + month = {8}, + issn = {1431-1550}, } ## Funding @@ -158,7 +163,7 @@ Forschungsgemeinschaft DFG within the [docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg [docs-stable-url]: https://docs.oscar-system.org/stable/ -[ga-img]: https://github.com/oscar-system/Oscar.jl/workflows/Run%20tests/badge.svg +[ga-img]: https://github.com/oscar-system/Oscar.jl/actions/workflows/CI.yml/badge.svg?branch=master&event=push [ga-url]: https://github.com/oscar-system/Oscar.jl/actions?query=workflow%3A%22Run+tests%22 [codecov-img]: https://codecov.io/gh/oscar-system/Oscar.jl/branch/master/graph/badge.svg?branch=master diff --git a/docs/doc.main b/docs/doc.main index e5aee7cb5914..cb3979b8ef46 100644 --- a/docs/doc.main +++ b/docs/doc.main @@ -163,15 +163,13 @@ "AlgebraicGeometry/Schemes/GeneralSchemes.md", "AlgebraicGeometry/Schemes/AffineSchemes.md", "AlgebraicGeometry/Schemes/MorphismsOfAffineSchemes.md", - "AlgebraicGeometry/Schemes/ArchitectureOfAffineSchemes.md", + "AlgebraicGeometry/Schemes/RationalPointsAffine.md", "AlgebraicGeometry/Schemes/CoveredSchemes.md", "AlgebraicGeometry/Schemes/CoveringsAndGluings.md", "AlgebraicGeometry/Schemes/CoveredSchemeMorphisms.md", "AlgebraicGeometry/Schemes/ProjectiveSchemes.md", "AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md", - ], - "Sheaf Cohomology" => [ - "AlgebraicGeometry/SheafCohomology/sheaf_cohomology.md", + "AlgebraicGeometry/Schemes/RationalPointsProjective.md", ], "Algebraic Sets" => [ "AlgebraicGeometry/AlgebraicSets/AffineAlgebraicSet.md", @@ -181,9 +179,20 @@ "AlgebraicGeometry/AlgebraicVarieties/AffineVariety.md", "AlgebraicGeometry/AlgebraicVarieties/ProjectiveVariety.md", ], - "Rational Points" => [ - "AlgebraicGeometry/RationalPoints/Affine.md", - "AlgebraicGeometry/RationalPoints/Projective.md", + "Curves" => [ + "AlgebraicGeometry/Curves/AffinePlaneCurves.md", + "AlgebraicGeometry/Curves/ProjectiveCurves.md", + "AlgebraicGeometry/Curves/ProjectivePlaneCurves.md", + "AlgebraicGeometry/Curves/ParametrizationPlaneCurves.md", + ], + "Surfaces" => [ + "AlgebraicGeometry/Surfaces/K3Surfaces.md", + "AlgebraicGeometry/Surfaces/AdjunctionProcess.md", + "AlgebraicGeometry/Surfaces/ParametrizationSurfaces.md", + "AlgebraicGeometry/Surfaces/SurfacesP4.md", + ], + "Sheaf Cohomology" => [ + "AlgebraicGeometry/SheafCohomology/sheaf_cohomology.md", ], "Toric Varieties" => [ "AlgebraicGeometry/ToricVarieties/intro.md", @@ -201,20 +210,12 @@ "AlgebraicGeometry/ToricVarieties/ToricIdealSheaves.md", "AlgebraicGeometry/ToricVarieties/BlowdownMorphisms.md", ], - "Curves" => [ - "AlgebraicGeometry/Curves/AffinePlaneCurves.md", - "AlgebraicGeometry/Curves/ProjectiveCurves.md", - "AlgebraicGeometry/Curves/ProjectivePlaneCurves.md", - ], - "Surfaces" => [ - "AlgebraicGeometry/Surfaces/K3Surfaces.md", - "AlgebraicGeometry/Surfaces/AdjunctionProcess.md", - "AlgebraicGeometry/Surfaces/ParametrizationSurfaces.md", - "AlgebraicGeometry/Surfaces/SurfacesP4.md", - ], "Miscellaneous" => [ "AlgebraicGeometry/Miscellaneous/miscellaneous.md", - ] + ], + "Frameworks for Developers" => [ + "AlgebraicGeometry/FrameWorks/ArchitectureOfAffineSchemes.md", + ], ], "Tropical Geometry" => [ diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index c7529ae38e0e..9f41157264b9 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -207,6 +207,20 @@ @Article{BHMPW20 primaryclass = {math.AG} } +@Book{BHPV-D-V04, + author = {Barth, Wolf P. and Hulek, Klaus and Peters, Chris A. M. and Van de Ven, Antonius}, + title = {Compact complex surfaces}, + zbl = {1036.14016}, + series = {Ergeb. Math. Grenzgeb., 3. Folge}, + volume = {4}, + publisher = {Berlin: Springer}, + edition = {2nd enlarged ed.}, + year = {2004}, + fseries = {Ergebnisse der Mathematik und ihrer Grenzgebiete. 3. Folge}, + language = {English}, + zbmath = {2008523} +} + @Article{BJRR10, author = {Blumenhagen, Ralph and Jurke, Benjamin and Rahn, Thorsten and Roschy, Helmut}, title = {Cohomology of line bundles: A computational algorithm}, diff --git a/docs/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.md b/docs/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.md new file mode 100644 index 000000000000..fd8b87ecea3c --- /dev/null +++ b/docs/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.md @@ -0,0 +1,80 @@ +```@meta +CurrentModule = Oscar +``` + +# Rational Parametrizations of Rational Plane Curves + +!!! note + In this section, $C$ will denote a complex projective plane curve, defined by an absolutely irreducible, + homogeneous polynomial in three variables, with coefficients in $\mathbb Q$. Moreover, we will write $n = \deg C$. + +Recall that the curve $C$ is *rational* if it is birationally equivalent to the projective line $\mathbb P^1(\mathbb C)$. +In other words, there exists a *rational parametrization* of $C$, that is, a birational map $\mathbb P^1(\mathbb C)\dashrightarrow C$. +Note that such a parametrization is given by three homogeneous polynomials of the same degree in the homogeneous coordinates on +$\mathbb P^1(\mathbb C)$. + +!!! note + The curve $C$ is rational iff its geometric genus is zero. + +Based on work of Max Noether on adjoint curves, Hilbert und Hurwitz showed that if +$C$ is rational, then there is a birational map $C \dashrightarrow D$ defined over $\mathbb Q$ such +that $D = \mathbb P^1(\mathbb C)$ if $n$ is odd, and $D\subset\mathbb P^2(\mathbb C)$ is a conic if $n$ is even. + +!!! note + If a conic $D$ contains a rational point, then there exists a parametrization of $D$ defined over $\mathbb Q$; + otherwise, there exists a parametrization of $D$ defined over a quadratic field extension of $\mathbb Q$. + +The approach of Hilbert und Hurwitz is constructive and allows one, in principle, to find rational parametrizations. +The resulting algorithm is not very practical, however, as the approach asks to compute adjoint curves repeatedly, +at each of a number of reduction steps. + +The algorithm implemented in OSCAR relies on reduction steps of a different type and requires the computation of adjoint +curves only once. Its individual steps are interesting in their own right: + + - Assure that the curve $C$ is rational by checking that its geometric genus is zero; + - compute a basis of the adjoint curves of $C$ of degree ${n-2}$; each such basis defines a birational map $C \dashrightarrow C_{n-2},$ + where $C_{n-2}$ is a rational normal curve in $\mathbb P^{n-2}(\mathbb C)$; + - the anticanonical linear system on $C_{n-2}$ defines a birational map $C_{n-2}\dashrightarrow C_{n-4}$, where $C_{n-4}$ is a rational normal curve in in $\mathbb P^{n-4}(\mathbb C)$; + - iterate the previous step to obtain a birational map $C_{n-2} \dashrightarrow \dots \dashrightarrow D$, + where $D = \mathbb P^1(\mathbb C)$ if $n$ is odd, and $D\subset\mathbb P^2(\mathbb C)$ is a conic if $n$ is even; + - invert the birational map $C \dashrightarrow C_{n-2} \dashrightarrow \dots \dashrightarrow D$; + - if $n$ is even, compute a parametrization of the conic $D$ and compose it with the inverted map above. + +!!! note + The defining property of an adjoint curve is that it passes with “sufficiently high” multiplicity through the singularities of $C$. + There are several concepts of making this precise. For each such concept, there is a corresponding *adjoint ideal* of $C$, + namely the homogeneous ideal formed by the defining polynomials of the adjoint curves. In OSCAR, we follow + the concept of Gorenstein which leads to the largest possible adjoint ideal. + +See [Bhm99](@cite) and [BDLP17](@cite) for details and further references. + + + + +## Adjoint Ideals of Plane Curves + +```@docs +adjoint_ideal(C::ProjectivePlaneCurve{QQField}) +``` + +## Rational Points on Conics + +```@docs +rational_point_conic(D::ProjectivePlaneCurve{QQField}) +``` +## Parametrizing Rational Plane Curves + +```@docs +parametrization(C::ProjectivePlaneCurve{QQField}) +``` + + +## Contact + +Please direct questions about this part of OSCAR to the following people: +* [Janko Böhm](https://www.mathematik.uni-kl.de/~boehm/), +* [Wolfram Decker](https://math.rptu.de/en/wgs/agag/people/head/decker). + +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/docs/src/AlgebraicGeometry/Curves/ProjectivePlaneCurves.md b/docs/src/AlgebraicGeometry/Curves/ProjectivePlaneCurves.md index 4f46250af2c5..167cf6044349 100644 --- a/docs/src/AlgebraicGeometry/Curves/ProjectivePlaneCurves.md +++ b/docs/src/AlgebraicGeometry/Curves/ProjectivePlaneCurves.md @@ -21,79 +21,3 @@ intersection_multiplicity(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: is_transverse_intersection(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: ProjectivePlaneCurve ``` -# Rational Parametrizations of Rational Plane Curves - -!!! note - In this section, $C$ will denote a complex projective plane curve, defined by an absolutely irreducible, - homogeneous polynomial in three variables, with coefficients in $\mathbb Q$. Moreover, we will write $n = \deg C$. - -Recall that the curve $C$ is *rational* if it is birationally equivalent to the projective line $\mathbb P^1(\mathbb C)$. -In other words, there exists a *rational parametrization* of $C$, that is, a birational map $\mathbb P^1(\mathbb C)\dashrightarrow C$. -Note that such a parametrization is given by three homogeneous polynomials of the same degree in the homogeneous coordinates on -$\mathbb P^1(\mathbb C)$. - -!!! note - The curve $C$ is rational iff its geometric genus is zero. - -Based on work of Max Noether on adjoint curves, Hilbert und Hurwitz showed that if -$C$ is rational, then there is a birational map $C \dashrightarrow D$ defined over $\mathbb Q$ such -that $D = \mathbb P^1(\mathbb C)$ if $n$ is odd, and $D\subset\mathbb P^2(\mathbb C)$ is a conic if $n$ is even. - -!!! note - If a conic $D$ contains a rational point, then there exists a parametrization of $D$ defined over $\mathbb Q$; - otherwise, there exists a parametrization of $D$ defined over a quadratic field extension of $\mathbb Q$. - -The approach of Hilbert und Hurwitz is constructive and allows one, in principle, to find rational parametrizations. -The resulting algorithm is not very practical, however, as the approach asks to compute adjoint curves repeatedly, -at each of a number of reduction steps. - -The algorithm implemented in OSCAR relies on reduction steps of a different type and requires the computation of adjoint -curves only once. Its individual steps are interesting in their own right: - - - Assure that the curve $C$ is rational by checking that its geometric genus is zero; - - compute a basis of the adjoint curves of $C$ of degree ${n-2}$; each such basis defines a birational map $C \dashrightarrow C_{n-2},$ - where $C_{n-2}$ is a rational normal curve in $\mathbb P^{n-2}(\mathbb C)$; - - the anticanonical linear system on $C_{n-2}$ defines a birational map $C_{n-2}\dashrightarrow C_{n-4}$, where $C_{n-4}$ is a rational normal curve in in $\mathbb P^{n-4}(\mathbb C)$; - - iterate the previous step to obtain a birational map $C_{n-2} \dashrightarrow \dots \dashrightarrow D$, - where $D = \mathbb P^1(\mathbb C)$ if $n$ is odd, and $D\subset\mathbb P^2(\mathbb C)$ is a conic if $n$ is even; - - invert the birational map $C \dashrightarrow C_{n-2} \dashrightarrow \dots \dashrightarrow D$; - - if $n$ is even, compute a parametrization of the conic $D$ and compose it with the inverted map above. - -!!! note - The defining property of an adjoint curve is that it passes with “sufficiently high” multiplicity through the singularities of $C$. - There are several concepts of making this precise. For each such concept, there is a corresponding *adjoint ideal* of $C$, - namely the homogeneous ideal formed by the defining polynomials of the adjoint curves. In OSCAR, we follow - the concept of Gorenstein which leads to the largest possible adjoint ideal. - -See [Bhm99](@cite) and [BDLP17](@cite) for details and further references. - - - - -## Adjoint Ideals of Plane Curves - -```@docs -adjoint_ideal(C::ProjectivePlaneCurve{QQField}) -``` - -## Rational Points on Conics - -```@docs -rational_point_conic(D::ProjectivePlaneCurve{QQField}) -``` -## Parametrizing Rational Plane Curves - -```@docs -parametrization(C::ProjectivePlaneCurve{QQField}) -``` - - -## Contact - -Please direct questions about this part of OSCAR to the following people: -* [Janko Böhm](https://www.mathematik.uni-kl.de/~boehm/), -* [Wolfram Decker](https://math.rptu.de/en/wgs/agag/people/head/decker). - -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/docs/src/AlgebraicGeometry/Schemes/ArchitectureOfAffineSchemes.md b/docs/src/AlgebraicGeometry/FrameWorks/ArchitectureOfAffineSchemes.md similarity index 80% rename from docs/src/AlgebraicGeometry/Schemes/ArchitectureOfAffineSchemes.md rename to docs/src/AlgebraicGeometry/FrameWorks/ArchitectureOfAffineSchemes.md index 29ddebd8c345..389ed8f2b138 100644 --- a/docs/src/AlgebraicGeometry/Schemes/ArchitectureOfAffineSchemes.md +++ b/docs/src/AlgebraicGeometry/FrameWorks/ArchitectureOfAffineSchemes.md @@ -10,21 +10,21 @@ CurrentModule = Oscar Any type of ring ``R`` to be used within the schemes framework must come with its own ideal type `IdealType<:Ideal` for which we require the following interface to be implemented: -``` - # constructor of ideals in R - ideal(R::RingType, g::Vector{<:RingElem})::Ideal +```julia +# constructor of ideals in R +ideal(R::RingType, g::Vector{<:RingElem})::Ideal - # constructor for quotient rings - quo(R::RingType, I::IdealType)::Tuple{<:Ring, <:Map} +# constructor for quotient rings +quo(R::RingType, I::IdealType)::Tuple{<:Ring, <:Map} - # ideal membership test - in(f::RingElem, I::IdealType)::Bool +# ideal membership test +in(f::RingElem, I::IdealType)::Bool - # a (fixed) set of generators - gens(I::IdealType)::Vector{<:RingElem} +# a (fixed) set of generators +gens(I::IdealType)::Vector{<:RingElem} - # writing an element as linear combination of the generators - coordinates(f::RingElem, I::IdealType)::Vector{<:RingElem} +# writing an element as linear combination of the generators +coordinates(f::RingElem, I::IdealType)::Vector{<:RingElem} ``` The latter function must return a vector ``v = (a_1,\dots, a_r)`` of elements in ``R`` such that ``f = a_1 \cdot g_1 + \dots + a_r \cdot g_r`` @@ -34,8 +34,8 @@ not belong to ``I``, it must error. Note that the ring returned by With a view towards the use of the `ambient_coordinate_ring(X)` for computations, it is customary to also implement -``` - saturated_ideal(I::IdealType)::MPolyIdeal +```julia +saturated_ideal(I::IdealType)::MPolyIdeal ``` returning an ideal ``J`` in the `ambient_coordinate_ring(X)` with the property that ``a \in I`` for some element ``a \in R`` if and only if @@ -51,9 +51,9 @@ this is another reason to include the ambient affine space in our abstract interface for affine schemes. In order to make the `ambient_coordinate_ring(X)` accessible for this purpose, we need the following methods to be implemented for elements ``a\in R`` of type `RingElemType`: -``` - lifted_numerator(a::RingElemType) - lifted_denominator(a::RingElemType) +```julia +lifted_numerator(a::RingElemType) +lifted_denominator(a::RingElemType) ``` These must return representatives of the numerator and the denominator of ``a``. Note that the denominator is equal to `one(P)` in case @@ -63,8 +63,8 @@ Recall that the coordinates ``x_i`` of ``X`` are induced by the coordinates of the ambient affine space. Moreover, we will assume that for homomorphisms from ``R`` there is a method -``` - hom(R::RingType, S::Ring, a::Vector{<:RingElem}) +```julia +hom(R::RingType, S::Ring, a::Vector{<:RingElem}) ``` where `RingType` is the type of ``R`` and `a` the images of the coordinates ``x_i`` in ``S``. This will be important @@ -83,7 +83,7 @@ Note that the morphism ``P → R`` is induced by natural coercions. The abstract type for affine schemes is ```@docs - AbsAffineScheme{BaseRingType, RingType<:Ring} +AbsAffineScheme{BaseRingType, RingType<:Ring} ``` For any concrete instance of this type, we require the following functions to be implemented: @@ -92,7 +92,7 @@ functions to be implemented: A concrete instance of this type is ```@docs - AffineScheme{BaseRingType, RingType} +AffineScheme{BaseRingType, RingType} ``` It provides an implementation of affine schemes for rings ``R`` of type `MPolyRing`, `MPolyQuoRing`, `MPolyLocRing`, and `MPolyQuoLocRing` @@ -102,8 +102,8 @@ concrete types `MyAffineScheme<:AbsAffineScheme` such as, for instance, group schemes, toric schemes, schemes of a particular dimension like curves and surfaces, etc. To this end, one has to store an instance `Y` of `AffineScheme` in `MyAffineScheme` and implement the methods -``` - underlying_scheme(X::MyAffineScheme)::AffineScheme # return Y as above +```julia +underlying_scheme(X::MyAffineScheme)::AffineScheme # return Y as above ``` Then all methods implemented for `AffineScheme` are automatically forwarded to any instance of `MyAffineScheme`. @@ -116,20 +116,20 @@ Of course, it can be overwritten for any higher type `MyAffineScheme<:AbsAffineS Any abstract morphism of affine schemes is of the following type: ```@docs - AbsAffineSchemeMor{DomainType<:AbsAffineScheme, - CodomainType<:AbsAffineScheme, - PullbackType<:Map, - MorphismType, - BaseMorType - } +AbsAffineSchemeMor{DomainType<:AbsAffineScheme, + CodomainType<:AbsAffineScheme, + PullbackType<:Map, + MorphismType, + BaseMorType + } ``` Any such morphism has the attributes `domain`, `codomain` and `pullback`. A concrete and minimalistic implementation exist for the type `AffineSchemeMor`: ```@docs - AffineSchemeMor{DomainType<:AbsAffineScheme, - CodomainType<:AbsAffineScheme, - PullbackType<:Map - } +AffineSchemeMor{DomainType<:AbsAffineScheme, + CodomainType<:AbsAffineScheme, + PullbackType<:Map + } ``` This basic functionality consists of - `compose(f::AbsAffineSchemeMor, g::AbsAffineSchemeMor)`, @@ -144,7 +144,7 @@ should run naturally. We may derive higher types of morphisms of affine schemes `MyAffineSchemeMor<:AbsAffineSchemeMor` by storing an instance `g` of `AffineSchemeMor` inside an instance `f` of `MyAffineSchemeMor` and implementing -``` - underlying_morphism(f::MyAffineSchemeMor)::AffineSchemeMor # return g +```julia +underlying_morphism(f::MyAffineSchemeMor)::AffineSchemeMor # return g ``` For example, this allows us to define closed embeddings. diff --git a/docs/src/AlgebraicGeometry/Schemes/CoveredSchemeMorphisms.md b/docs/src/AlgebraicGeometry/Schemes/CoveredSchemeMorphisms.md index 0fa5007cf320..1c4d66a44c46 100644 --- a/docs/src/AlgebraicGeometry/Schemes/CoveredSchemeMorphisms.md +++ b/docs/src/AlgebraicGeometry/Schemes/CoveredSchemeMorphisms.md @@ -14,8 +14,8 @@ This information is held by a `CoveringMorphism`: ``` The basic functionality of `CoveringMorphism`s comprises `domain` and `codomain` which both return a `Covering`, together with -``` - getindex(f::CoveringMorphism, U::AbsAffineScheme) +```julia +getindex(f::CoveringMorphism, U::AbsAffineScheme) ``` which for ``U = U_i`` returns the `AbsAffineSchemeMor` ``f_i : U_i \to V_{F(i)}``. @@ -27,10 +27,10 @@ in order to realize the covering morphism in the first place. ## The interface for morphisms of covered schemes Every `AbsCoveredSchemeMorphism` ``f : X \to Y`` is required to implement the following minimal interface. -``` - domain(f::AbsCoveredSchemeMorphism) # returns X - codomain(f::AbsCoveredSchemeMorphism) # returns Y - covering_morphism(f::AbsCoveredSchemeMorphism) # returns the underlying covering morphism {f_i} +```julia +domain(f::AbsCoveredSchemeMorphism) # returns X +codomain(f::AbsCoveredSchemeMorphism) # returns Y +covering_morphism(f::AbsCoveredSchemeMorphism) # returns the underlying covering morphism {f_i} ``` For the user's convenience, also the domain and codomain of the underlying `covering_morphism` are forwarded as `domain_covering` and @@ -64,7 +64,7 @@ on ``X`` and ``Y`` can be extracted from the ``f^* v_i`` more directly. A lazy concrete data structure to house this kind of morphism is ```@docs - MorphismFromRationalFunctions +MorphismFromRationalFunctions ``` Note that the key idea of this data type is to *not* use the `underlying_morphism` together with its `covering_morphism`, but to find cheaper ways to do computations! @@ -81,12 +81,12 @@ there is no need to realize the full `covering_morphism` of ``f``. In order to facilitate such computations as lazy as possible, there are various fine-grained entry points and caching mechanisms to realize ``f`` on open subsets: ```@docs - realize_on_patch(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme) - realize_on_open_subset(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - realization_preview(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - random_realization(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - cheap_realization(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - realize_maximally_on_open_subset(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) - realize(Phi::MorphismFromRationalFunctions) +realize_on_patch(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme) +realize_on_open_subset(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) +realization_preview(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) +random_realization(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) +cheap_realization(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) +realize_maximally_on_open_subset(Phi::MorphismFromRationalFunctions, U::AbsAffineScheme, V::AbsAffineScheme) +realize(Phi::MorphismFromRationalFunctions) ``` diff --git a/docs/src/AlgebraicGeometry/Schemes/CoveredSchemes.md b/docs/src/AlgebraicGeometry/Schemes/CoveredSchemes.md index 90b9aeb1d02b..8cc7e8492a8f 100644 --- a/docs/src/AlgebraicGeometry/Schemes/CoveredSchemes.md +++ b/docs/src/AlgebraicGeometry/Schemes/CoveredSchemes.md @@ -9,22 +9,22 @@ Oscar supports modeling abstract schemes by means of a covering by affine charts ## Types The abstract type for these is: ```@docs - AbsCoveredScheme{BaseRingType} +AbsCoveredScheme{BaseRingType} ``` The basic concrete instance of an `AbsCoveredScheme` is: ```@docs - CoveredScheme{BaseRingType} +CoveredScheme{BaseRingType} ``` ## Constructors You can manually construct a `CoveredScheme` from a `Covering` using ```@docs - CoveredScheme(C::Covering) +CoveredScheme(C::Covering) ``` In most cases, however, you may wish for the computer to provide you with a ready-made `Covering` and use a more high-level constructor, such as, for instance, ```@docs - covered_scheme(P::ProjectiveScheme) +covered_scheme(P::ProjectiveScheme) ``` Other constructors: @@ -35,25 +35,25 @@ disjoint_union(Xs::Vector{<:AbsCoveredScheme}) ## Attributes To access the affine charts of a `CoveredScheme` $X$ use ```@docs - affine_charts(X::AbsCoveredScheme) +affine_charts(X::AbsCoveredScheme) ``` Other attributes are the `base_ring` over which the scheme is defined and ```@docs - default_covering(X::AbsCoveredScheme) +default_covering(X::AbsCoveredScheme) ``` ## Properties An `AbsCoveredScheme` may have different properties such as -``` - is_empty(X::AbsCoveredScheme) - is_smooth(X::AbsCoveredScheme) +```julia +is_empty(X::AbsCoveredScheme) +is_smooth(X::AbsCoveredScheme) ``` ## Methods ```@docs - fiber_product(f::AbsCoveredSchemeMorphism, g::AbsCoveredSchemeMorphism) - is_normal(X::AbsCoveredScheme; check::Bool=true) - normalization(X::AbsCoveredScheme; check::Bool=true) +fiber_product(f::AbsCoveredSchemeMorphism, g::AbsCoveredSchemeMorphism) +is_normal(X::AbsCoveredScheme; check::Bool=true) +normalization(X::AbsCoveredScheme; check::Bool=true) ``` ## The modeling of covered schemes and their expected behavior @@ -63,7 +63,7 @@ several reasons; for instance, a morphism $f : X \to Y$ between `AbsCoveredSchem will in general only be given on affine patches on a refinement of the `default_covering` of `X`. The list of available `Covering`s can be obtained using ```@docs - coverings(X::AbsCoveredScheme) +coverings(X::AbsCoveredScheme) ``` Every `AbsCoveredScheme` $X$ has to be modeled using one original `default_covering` $C$, simply to gather the data necessary to fully describe $X$. The `affine_charts` of $X$ return the diff --git a/docs/src/AlgebraicGeometry/Schemes/CoveringsAndGluings.md b/docs/src/AlgebraicGeometry/Schemes/CoveringsAndGluings.md index 756e1fc08403..16e663656da6 100644 --- a/docs/src/AlgebraicGeometry/Schemes/CoveringsAndGluings.md +++ b/docs/src/AlgebraicGeometry/Schemes/CoveringsAndGluings.md @@ -6,24 +6,24 @@ CurrentModule = Oscar `Covering`s are the backbone data structure for `CoveredScheme`s in Oscar. ```@docs - Covering +Covering ``` ## Constructors ```@docs - Covering(patches::Vector{<:AbsAffineScheme}) - disjoint_union(C1::Covering, C2::Covering) +Covering(patches::Vector{<:AbsAffineScheme}) +disjoint_union(C1::Covering, C2::Covering) ``` ## Attributes ```@docs - affine_charts(C::Covering) - gluings(C::Covering) +affine_charts(C::Covering) +gluings(C::Covering) ``` ## Methods ```@docs - add_gluing!(C::Covering, G::AbsGluing) +add_gluing!(C::Covering, G::AbsGluing) ``` # Gluings @@ -34,32 +34,32 @@ of affine schemes along an isomorphism $f \colon U \leftrightarrow V \colon g$. ## Types The abstract type of any such gluing is ```@docs - AbsGluing +AbsGluing ``` The available concrete types are ```@docs - Gluing - SimpleGluing +Gluing +SimpleGluing ``` ## Constructors ```@docs - Gluing(X::AbsAffineScheme, Y::AbsAffineScheme, f::SchemeMor, g::SchemeMor) +Gluing(X::AbsAffineScheme, Y::AbsAffineScheme, f::SchemeMor, g::SchemeMor) ``` ## Attributes ```@docs - patches(G::AbsGluing) - gluing_domains(G::AbsGluing) - gluing_morphisms(G::AbsGluing) - inverse(G::AbsGluing) +patches(G::AbsGluing) +gluing_domains(G::AbsGluing) +gluing_morphisms(G::AbsGluing) +inverse(G::AbsGluing) ``` ## Methods ```@docs - compose(G::AbsGluing, H::AbsGluing) - maximal_extension(G::Gluing) - restrict(G::AbsGluing, f::AbsAffineSchemeMor, g::AbsAffineSchemeMor; check::Bool=true) +compose(G::AbsGluing, H::AbsGluing) +maximal_extension(G::Gluing) +restrict(G::AbsGluing, f::AbsAffineSchemeMor, g::AbsAffineSchemeMor; check::Bool=true) ``` diff --git a/docs/src/AlgebraicGeometry/Schemes/GeneralSchemes.md b/docs/src/AlgebraicGeometry/Schemes/GeneralSchemes.md index 21fc54fc074f..b7aeb9b5899c 100644 --- a/docs/src/AlgebraicGeometry/Schemes/GeneralSchemes.md +++ b/docs/src/AlgebraicGeometry/Schemes/GeneralSchemes.md @@ -11,14 +11,14 @@ Scheme{BaseRingType<:Ring} ``` Morphisms of schemes shall be derived from the abstract type ```@docs - SchemeMor{DomainType, CodomainType, MorphismType, BaseMorType} +SchemeMor{DomainType, CodomainType, MorphismType, BaseMorType} ``` ## Change of base ```@docs - base_change(phi::Any, X::Scheme) - base_change(phi::Any, f::SchemeMor; - domain_map::AbsSchemeMor, codomain_map::AbsSchemeMor - ) +base_change(phi::Any, X::Scheme) +base_change(phi::Any, f::SchemeMor; + domain_map::AbsSchemeMor, codomain_map::AbsSchemeMor + ) ``` diff --git a/docs/src/AlgebraicGeometry/Schemes/MorphismsOfAffineSchemes.md b/docs/src/AlgebraicGeometry/Schemes/MorphismsOfAffineSchemes.md index d251082e3b8a..a2192872bd89 100644 --- a/docs/src/AlgebraicGeometry/Schemes/MorphismsOfAffineSchemes.md +++ b/docs/src/AlgebraicGeometry/Schemes/MorphismsOfAffineSchemes.md @@ -40,7 +40,7 @@ graph(f::AbsAffineSchemeMor) In addition to the standard getters and methods for instances of `AffineSchemeMor`, we also have ```@docs - image_ideal(f::ClosedEmbedding) +image_ideal(f::ClosedEmbedding) ``` ### Undocumented diff --git a/docs/src/AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md b/docs/src/AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md index bb8eee589dfe..4f29c164973c 100644 --- a/docs/src/AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md +++ b/docs/src/AlgebraicGeometry/Schemes/MorphismsOfProjectiveSchemes.md @@ -17,30 +17,30 @@ out. ## Types ```@docs - ProjectiveSchemeMor +ProjectiveSchemeMor ``` ## Constructors ```@docs - morphism(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map; check::Bool=true ) - morphism(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map, h::SchemeMor; check::Bool=true ) - morphism(X::AbsProjectiveScheme, Y::AbsProjectiveScheme, a::Vector{<:RingElem}) +morphism(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map; check::Bool=true ) +morphism(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map, h::SchemeMor; check::Bool=true ) +morphism(X::AbsProjectiveScheme, Y::AbsProjectiveScheme, a::Vector{<:RingElem}) ``` ## Attributes As every instance of `Map`, a morphism of projective schemes can be asked for its (co-)domain: -``` - domain(phi::ProjectiveSchemeMor) - codomain(phi::ProjectiveSchemeMor) +```julia +domain(phi::ProjectiveSchemeMor) +codomain(phi::ProjectiveSchemeMor) ``` Moreover, we provide getters for the associated morphisms of rings: ```@docs - pullback(phi::ProjectiveSchemeMor) - base_ring_morphism(phi::ProjectiveSchemeMor) - base_map(phi::ProjectiveSchemeMor) - map_on_affine_cones(phi::ProjectiveSchemeMor) +pullback(phi::ProjectiveSchemeMor) +base_ring_morphism(phi::ProjectiveSchemeMor) +base_map(phi::ProjectiveSchemeMor) +map_on_affine_cones(phi::ProjectiveSchemeMor) ``` ## Methods ```@docs - covered_scheme_morphism(f::AbsProjectiveSchemeMorphism) +covered_scheme_morphism(f::AbsProjectiveSchemeMorphism) ``` diff --git a/docs/src/AlgebraicGeometry/Schemes/ProjectiveSchemes.md b/docs/src/AlgebraicGeometry/Schemes/ProjectiveSchemes.md index 26878c0ba805..b5e8bade3e12 100644 --- a/docs/src/AlgebraicGeometry/Schemes/ProjectiveSchemes.md +++ b/docs/src/AlgebraicGeometry/Schemes/ProjectiveSchemes.md @@ -20,27 +20,27 @@ and `MPolyQuoLocRing`. ## Abstract types and basic interface The abstract type for such projective schemes is -``` - AbsProjectiveScheme{CoeffRingType, RingType} where {CoeffRingType<:Ring} +```julia +AbsProjectiveScheme{CoeffRingType, RingType} where {CoeffRingType<:Ring} ``` where, in the above notation, `CoeffRingType` denotes the type of `A` and `RingType` the type of either `S` or `S/I`, respectively. The abstract type comes with the following interface: ```@docs - base_ring(X::AbsProjectiveScheme) - base_scheme(X::AbsProjectiveScheme) - homogeneous_coordinate_ring(P::AbsProjectiveScheme) - relative_ambient_dimension(X::AbsProjectiveScheme) - ambient_coordinate_ring(P::AbsProjectiveScheme) - ambient_space(P::AbsProjectiveScheme) - defining_ideal(X::AbsProjectiveScheme) - affine_cone(X::AbsProjectiveScheme) - homogeneous_coordinates_on_affine_cone(X::AbsProjectiveScheme) - covered_scheme(P::AbsProjectiveScheme) +base_ring(X::AbsProjectiveScheme) +base_scheme(X::AbsProjectiveScheme) +homogeneous_coordinate_ring(P::AbsProjectiveScheme) +relative_ambient_dimension(X::AbsProjectiveScheme) +ambient_coordinate_ring(P::AbsProjectiveScheme) +ambient_space(P::AbsProjectiveScheme) +defining_ideal(X::AbsProjectiveScheme) +affine_cone(X::AbsProjectiveScheme) +homogeneous_coordinates_on_affine_cone(X::AbsProjectiveScheme) +covered_scheme(P::AbsProjectiveScheme) ``` The minimal concrete type realizing this interface is -``` - ProjectiveScheme{CoeffRingType, RingType} <: AbsProjectiveScheme{CoeffRingType, RingType} +```julia +ProjectiveScheme{CoeffRingType, RingType} <: AbsProjectiveScheme{CoeffRingType, RingType} ``` @@ -48,32 +48,32 @@ The minimal concrete type realizing this interface is Besides `proj(S)` for some graded polynomial ring or a graded affine algebra `S`, we provide the following constructors: -``` - proj(S::MPolyDecRing) - proj(S::MPolyDecRing, I::MPolyIdeal{T}) where {T<:MPolyDecRingElem} - proj(I::MPolyIdeal{<:MPolyDecRingElem}) - proj(Q::MPolyQuoRing{<:MPolyDecRingElem}) +```julia +proj(S::MPolyDecRing) +proj(S::MPolyDecRing, I::MPolyIdeal{T}) where {T<:MPolyDecRingElem} +proj(I::MPolyIdeal{<:MPolyDecRingElem}) +proj(Q::MPolyQuoRing{<:MPolyDecRingElem}) ``` Subschemes defined by homogeneous ideals, ring elements, or lists of elements can be created via the respective methods of the `subscheme(P::AbsProjectiveScheme, ...)` function. Special constructors are provided for projective space itself via the function `projective_space` and its various methods. ```@docs - projective_space(A::Ring, var_symb::Vector{VarName}) - projective_space(A::Ring, r::Int; var_name::VarName=:s) +projective_space(A::Ring, var_symb::Vector{VarName}) +projective_space(A::Ring, r::Int; var_name::VarName=:s) ``` ## Attributes Besides those attributes already covered by the above general interface we have the following (self-explanatory) ones for projective schemes over a field. -``` - dim(P::AbsProjectiveScheme{<:Field}) - hilbert_polynomial(P::AbsProjectiveScheme{<:Field}) - degree(P::AbsProjectiveScheme{<:Field}) +```julia +dim(P::AbsProjectiveScheme{<:Field}) +hilbert_polynomial(P::AbsProjectiveScheme{<:Field}) +degree(P::AbsProjectiveScheme{<:Field}) ``` ```@docs - arithmetic_genus(P::AbsProjectiveScheme{<:Field}) +arithmetic_genus(P::AbsProjectiveScheme{<:Field}) ``` ## Methods @@ -81,22 +81,22 @@ Besides those attributes already covered by the above general interface we have To facilitate the interplay between an `AbsProjectiveScheme` and the affine charts of its `covered_scheme` we provide the following methods: ```@docs - dehomogenization_map(X::AbsProjectiveScheme, U::AbsAffineScheme) - homogenization_map(P::AbsProjectiveScheme, U::AbsAffineScheme) +dehomogenization_map(X::AbsProjectiveScheme, U::AbsAffineScheme) +homogenization_map(P::AbsProjectiveScheme, U::AbsAffineScheme) ``` ## Properties Further properties of projective schemes: ```@docs - is_smooth(P::AbsProjectiveScheme) -``` -``` - is_empty(P::AbsProjectiveScheme{<:Field}) - is_irreducible(P::AbsProjectiveScheme) - is_reduced(P::AbsProjectiveScheme) - is_geometrically_reduced(P::AbsProjectiveScheme{<:Field}) - is_geometrically_irreducible(P::AbsProjectiveScheme{<:Field}) - is_integral(X::AbsProjectiveScheme{<:Field}) - is_geometrically_integral(X::AbsProjectiveScheme{<:Field}) +is_smooth(P::AbsProjectiveScheme) +``` +```julia +is_empty(P::AbsProjectiveScheme{<:Field}) +is_irreducible(P::AbsProjectiveScheme) +is_reduced(P::AbsProjectiveScheme) +is_geometrically_reduced(P::AbsProjectiveScheme{<:Field}) +is_geometrically_irreducible(P::AbsProjectiveScheme{<:Field}) +is_integral(X::AbsProjectiveScheme{<:Field}) +is_geometrically_integral(X::AbsProjectiveScheme{<:Field}) ``` diff --git a/docs/src/AlgebraicGeometry/RationalPoints/Affine.md b/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md similarity index 94% rename from docs/src/AlgebraicGeometry/RationalPoints/Affine.md rename to docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md index 7d880545f07a..428864035015 100644 --- a/docs/src/AlgebraicGeometry/RationalPoints/Affine.md +++ b/docs/src/AlgebraicGeometry/Schemes/RationalPointsAffine.md @@ -6,7 +6,7 @@ CurrentModule = Oscar using Oscar ``` -# Affine +# Rational Points on Affine Schemes ```@docs AbsAffineRationalPoint diff --git a/docs/src/AlgebraicGeometry/RationalPoints/Projective.md b/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md similarity index 89% rename from docs/src/AlgebraicGeometry/RationalPoints/Projective.md rename to docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md index 5464dbb46bf2..bc9a3c45c376 100644 --- a/docs/src/AlgebraicGeometry/RationalPoints/Projective.md +++ b/docs/src/AlgebraicGeometry/Schemes/RationalPointsProjective.md @@ -6,7 +6,7 @@ CurrentModule = Oscar using Oscar ``` -# Projective +# Rational Points on Projective Schemes ```@docs AbsProjectiveRationalPoint diff --git a/docs/src/AlgebraicGeometry/Surfaces/AdjunctionProcess.md b/docs/src/AlgebraicGeometry/Surfaces/AdjunctionProcess.md index d84b4d618885..25b7026ed511 100644 --- a/docs/src/AlgebraicGeometry/Surfaces/AdjunctionProcess.md +++ b/docs/src/AlgebraicGeometry/Surfaces/AdjunctionProcess.md @@ -11,7 +11,7 @@ curve with self-intersection number $E^{2}=-1$. We speak of a *$(-1)$-curve*. A $(-1)$-curves. That is, the surface cannot be obtained by blowing up a point on another surface. A surface $X_{\text{min}}$ is called a *minimal model* of a surface $X$ if $X_{\text{min}}$ is minimal and $X$ can be obtained from $X_{\text{min}}$ by repeatedly blowing up a point. Each surface $X$ has a minimal model which is unique if $X$ has non-negative Kodaira dimension. -The Enriques-Kodaira classification classifies surfaces according to their minimal models. See ... for more on this. +The Enriques-Kodaira classification classifies surfaces according to their minimal models. See [BHPV-D-V04](@cite) for more on this. Given a surface, we may apply the *adjunction process* of Van de Ven and Sommese [SV-D-V87](@cite) to discover a minimal model. To describe this process, consider a surface $X \subset \mathbb P^{n}$ of codimension $c$. Let $S$ and $S_{X}$ diff --git a/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md b/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md index 70a8b9d8404d..9e86a66b41a3 100644 --- a/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md +++ b/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md @@ -101,7 +101,7 @@ For counting the partitions the recurrence relation $p_k(n) = p_{k - 1}(n - 1) + 7.2.1.4, Equation (39), and also [OEIS](@cite), [A008284](https://oeis.org/A008284). ```@docs -partitions(::T, ::Oscar.IntegerUnion, ::Oscar.IntegerUnion, ::Oscar.IntegerUnion) where T <: Oscar.IntegerUnion +partitions(::Oscar.IntegerUnion, ::Oscar.IntegerUnion, ::Oscar.IntegerUnion, ::Oscar.IntegerUnion) partitions(::T, ::Vector{T}) where T <: Oscar.IntegerUnion ``` diff --git a/docs/src/CommutativeAlgebra/FrameWorks/module_localizations.md b/docs/src/CommutativeAlgebra/FrameWorks/module_localizations.md index eb6a717cd0a0..550c9a775d33 100644 --- a/docs/src/CommutativeAlgebra/FrameWorks/module_localizations.md +++ b/docs/src/CommutativeAlgebra/FrameWorks/module_localizations.md @@ -23,7 +23,7 @@ requirements are met: 2) The user has to solve the *localization problem* by implementing `has_nonempty_intersection(U::MultSetType, I::IdealType)` for the type `MultSetType` of multiplicative sets and the type `IdealType` of ideals in `R` that they would like to consider. ```@docs - has_nonempty_intersection(U::AbsMultSet, I::Ideal) +has_nonempty_intersection(U::AbsMultSet, I::Ideal) ``` **Note:** In order to clear denominators of row vectors, the generic code uses the method `lcm(v::Vector{T})` where `T = elem_type(R)`. If no such method already exists, this has to also be provided; in the worst case by simply returning the product of the denominators. diff --git a/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases.md b/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases.md index 515ad80fabbb..b07c069edb57 100644 --- a/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases.md +++ b/docs/src/CommutativeAlgebra/GroebnerBases/groebner_bases.md @@ -53,6 +53,10 @@ The *leading monomial* $\text{LM}_>(f)$, the *leading exponent* $\text{LE}_>(f)$ positive weights. Then the corresponding `wdegrevlex` ordering is used. Given a free $R$-module $F$, the `default_ordering` is `default_ordering(R)*lex(gens(F))`. +```@docs +default_ordering(::MPolyRing) +``` + Here are some illustrating OSCAR examples: ##### Examples @@ -77,6 +81,11 @@ julia> default_ordering(S) wdegrevlex([x, y, z], [1, 2, 3]) ``` +Expert users may temporarily choose a different default ordering for a given ring. +```@docs +with_ordering +``` + ## [Monomials, Terms, and More](@id monomials_terms_more) Here are examples which indicate how to recover monomials, terms, and @@ -234,7 +243,7 @@ reduce(g::T, F::Vector{T}; reduce_with_quotients(g::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1]))) where T <: MPolyRingElem ``` - + ```@docs reduce_with_quotients_and_unit(g::T, F::Vector{T}; ordering::MonomialOrdering = default_ordering(parent(F[1]))) where T <: MPolyRingElem diff --git a/docs/src/CommutativeAlgebra/affine_algebras.md b/docs/src/CommutativeAlgebra/affine_algebras.md index 0b99386f88eb..a3e192213510 100644 --- a/docs/src/CommutativeAlgebra/affine_algebras.md +++ b/docs/src/CommutativeAlgebra/affine_algebras.md @@ -13,15 +13,15 @@ by an ideal $I$ of $R$, we refer to $A$ as an *affine algebra over $C$*, or an * discuss functionality for handling such algebras in OSCAR. !!! note - To emphasize this point: In this section, we view $R/I$ together with its ring structure. Realizing $R/I$ as an + We emphasize: In this section, we view $R/I$ together with its ring structure. Realizing $R/I$ as an $R$-module means to implement it as the quotient of a free $R$-module of rank 1. See the section on [modules](@ref modules_multivariate). !!! note Most functions discussed here rely on Gröbner basis techniques. In particular, they typically make use of a Gröbner basis for the modulus of the quotient. Nevertheless, the construction of quotients is lazy in the sense that the computation of such a Gröbner - basis is delayed until the user performs an operation that indeed requires it (the Gröbner basis is then computed with respect - to the monomial ordering entered by the user when creating the quotient; if no such ordering is entered, OSCAR will use the - `default_ordering` on the underlying polynomial ring; see the section on [Gröbner/Standard Bases](@ref gb_fields) for default orderings in OSCAR). + basis is delayed until the user performs an operation that indeed requires it. The Gröbner basis is then computed with respect + to the monomial ordering entered by the user when creating the quotient; if no ordering is entered, OSCAR will use the + `default_ordering` on the underlying polynomial ring. See the section on [Gröbner/Standard Bases](@ref gb_fields) for default orderings in OSCAR. Once computed, the Gröbner basis is cached for later reuse. !!! note @@ -62,8 +62,9 @@ If `A=R/I` is the quotient of a multivariate polynomial ring `R` modulo an ideal - `base_ring(A)` refers to `R`, - `modulus(A)` to `I`, - `gens(A)` to the generators of `A`, -- `number_of_generators(A)` / `ngens(A)` to the number of these generators, and -- `gen(A, i)` as well as `A[i]` to the `i`-th such generator. +- `number_of_generators(A)` / `ngens(A)` to the number of these generators, +- `gen(A, i)` as well as `A[i]` to the `i`-th such generator, and +- `ordering(A)` to the monomial ordering used in the construction of `A`. ###### Examples @@ -93,6 +94,9 @@ julia> number_of_generators(A) julia> gen(A, 2) y +julia> ordering(A) +degrevlex([x, y, z]) + ``` In the graded case, we additionally have: @@ -337,9 +341,9 @@ In OSCAR, such homomorphisms are created as follows: hom(A::MPolyQuoRing, S::NCRing, coeff_map, images::Vector; check::Bool = true) ``` -Given a ring homomorphism `F` : `R` $\to$ `S` as above, `domain(F)` and `codomain(F)` -refer to `R` and `S`, respectively. Given ring homomorphisms `F` : `R` $\to$ `S` and -`G` : `S` $\to$ `T` as above, `compose(F, G)` refers to their composition. +Given a ring homomorphism `F` : `A` $\to$ `S` as above, `domain(F)` and `codomain(F)` +refer to `A` and `S`, respectively. Given ring homomorphisms `F` : `A` $\to$ `B` and +`G` : `B` $\to$ `T` as above, `compose(F, G)` refers to their composition. ## Homomorphisms of Affine Algebras diff --git a/docs/src/CommutativeAlgebra/ideals.md b/docs/src/CommutativeAlgebra/ideals.md index 15e53fdea8d6..2a851310df4d 100644 --- a/docs/src/CommutativeAlgebra/ideals.md +++ b/docs/src/CommutativeAlgebra/ideals.md @@ -90,7 +90,7 @@ cm_regularity(I::MPolyIdeal) ```@docs degree(I::MPolyIdeal) ``` - + ## Operations on Ideals ### Simple Ideal Operations @@ -100,6 +100,7 @@ degree(I::MPolyIdeal) ```@docs ^(I::MPolyIdeal, m::Int) ``` + #### Sum of Ideals ```@docs diff --git a/docs/src/CommutativeAlgebra/localizations.md b/docs/src/CommutativeAlgebra/localizations.md index 5092672e83c5..41857e9ca52b 100644 --- a/docs/src/CommutativeAlgebra/localizations.md +++ b/docs/src/CommutativeAlgebra/localizations.md @@ -62,9 +62,9 @@ In accordance with the above mentioned types, we have the following constructors for multiplicatively closed subsets of multivariate polynomial rings. ```@docs - complement_of_point_ideal(R::MPolyRing, a::Vector) - complement_of_prime_ideal(P::MPolyIdeal; check::Bool=false) - powers_of_element(f::MPolyRingElem) +complement_of_point_ideal(R::MPolyRing, a::Vector) +complement_of_prime_ideal(P::MPolyIdeal; check::Bool=false) +powers_of_element(f::MPolyRingElem) ``` It is also possible to build products of multiplicatively closed sets already given: diff --git a/docs/src/DeveloperDocumentation/styleguide.md b/docs/src/DeveloperDocumentation/styleguide.md index 44490ecffaf8..665ac47fea93 100644 --- a/docs/src/DeveloperDocumentation/styleguide.md +++ b/docs/src/DeveloperDocumentation/styleguide.md @@ -168,7 +168,7 @@ end or ```julia for i in A - flag ||continue + flag || continue ... end ``` @@ -217,12 +217,12 @@ the user's convenience. Let's see an example. Say, you want to implement the characteristic polynomial of a matrix. You could do it as follows: ```julia - function characteristic_polynomial(A::MatrixElem) - kk = base_ring(A) - P, x = kk["x"] - AP = change_base_ring(P, A) - return det(AP - x*one(AP)) - end +function characteristic_polynomial(A::MatrixElem) + kk = base_ring(A) + P, x = kk["x"] + AP = change_base_ring(P, A) + return det(AP - x*one(AP)) +end ``` You can see that the polynomial ring `P`, i.e. the parent of the output, is newly created in the body of the function. In particular, calling this @@ -232,25 +232,25 @@ with different parents. Calling `p + q` will result in an error. To solve this, we should have implemented the function differently: ```julia - # Implementation of the recommended keyword argument signature: - function characteristic_polynomial( - A::MatrixElem; - parent::AbstractAlgebra.Ring=polynomial_ring(base_ring(A), :t)[1] - ) - AP = change_base_ring(parent, A) - x = first(gens(ring)) - return det(AP - x*one(AP)) - end - - # Optional second signature to also allow for the specification of the - # output's parent as the first argument: - function characteristic_polynomial( - P::PolyRing, - A::MatrixElem - ) - coefficient_ring(P) === base_ring(A) || error("coefficient rings incompatible") - return characteristic_polynomial(A, parent=P) - end +# Implementation of the recommended keyword argument signature: +function characteristic_polynomial( + A::MatrixElem; + parent::AbstractAlgebra.Ring=polynomial_ring(base_ring(A), :t)[1] + ) + AP = change_base_ring(parent, A) + x = first(gens(ring)) + return det(AP - x*one(AP)) +end + +# Optional second signature to also allow for the specification of the +# output's parent as the first argument: +function characteristic_polynomial( + P::PolyRing, + A::MatrixElem + ) + coefficient_ring(P) === base_ring(A) || error("coefficient rings incompatible") + return characteristic_polynomial(A, parent=P) +end ``` In fact this now allows for two different entry points for the parent ring `P` of the output: First as the required `parent` keyword argument and second diff --git a/docs/src/General/other.md b/docs/src/General/other.md index c2b4e89aa1d6..cc38f0da09d9 100644 --- a/docs/src/General/other.md +++ b/docs/src/General/other.md @@ -5,8 +5,6 @@ - Julia evaluates `2^100` to `0` because `2` is regarded as a 64 bit integer. Write `ZZRingElem(2)^100` to get a long. -- TODO: add more hints of this kind - ## Notes for GAP users @@ -110,11 +108,6 @@ This section describes differences between GAP and Oscar. transitive in GAP but as intransitive in Oscar. -## Notes for Singular users - -- TODO -- TODO: also talk about how to use it from OSCAR? - ## Notes for Polymake users - OSCAR (and Julia) is `1`-based, meaning that it counts from `1`, rather than @@ -144,12 +137,3 @@ This section describes differences between GAP and Oscar. c = Polymake.polytope.cube(3) Polymake.polytope.ambient_dim(c) ``` - - -## Notes for Magma users - -- TODO - -## Notes for SageMath users - -- TODO diff --git a/docs/src/General/serialization.md b/docs/src/General/serialization.md index 5e9630699814..1d95be072e12 100644 --- a/docs/src/General/serialization.md +++ b/docs/src/General/serialization.md @@ -18,8 +18,7 @@ load ## Objects that can be serialized -In this section we will list objects that may be (de-)serialized. This list may -be incomplete. +In this section we will list a selection of objects that may be (de-)serialized. Many low level objects may be stored and these in turn allow serializing higher level objects. Such low level objects are various types of matrices, vectors @@ -34,8 +33,19 @@ SimplicialComplex ### Commutative Algebra ```julia Ideal -Polynomial -polynomial_ring +PolyRing +PolyRingElem +MPolyRing +MPolyRingElem +``` + +### Groups +```julia +FPGroup +FinGenAbGroup +PcGroup +PermGroup +SubPcGroup ``` ### Polyhedral Geometry @@ -59,3 +69,16 @@ ToricDivisor TropicalCurve TropicalHypersurface ``` + +## Listing all serializable types of the current session + +If you are currious about whether your type can already be serialized given your version of Oscar +you can run the following command in your active session. + +```@setup oscar +using Oscar +``` + +```@repl oscar +Oscar.reverse_type_map +``` diff --git a/docs/src/Groups/basics.md b/docs/src/Groups/basics.md index 25fccca9d4b1..c3e6be6c6791 100644 --- a/docs/src/Groups/basics.md +++ b/docs/src/Groups/basics.md @@ -97,5 +97,6 @@ describe(G::GAPGroup) nilpotency_class(G::GAPGroup) prime_of_pgroup derived_length +schur_cover(G::GAPGroup) schur_multiplier(G::Union{GAPGroup, FinGenAbGroup}) ``` diff --git a/docs/src/Groups/grouphom.md b/docs/src/Groups/grouphom.md index 32c48ce2aa69..6327f9a1cde8 100644 --- a/docs/src/Groups/grouphom.md +++ b/docs/src/Groups/grouphom.md @@ -166,7 +166,7 @@ isomorphism(G::GAPGroup, H::GAPGroup) ``` ```@docs -isomorphism(::Type{T}, G::GAPGroup) where T <: Union{PcGroup, PermGroup} +isomorphism(::Type{T}, G::GAPGroup) where T <: Union{SubPcGroup, PermGroup} isomorphism(::Type{FinGenAbGroup}, G::GAPGroup) simplified_fp_group(G::FPGroup) ``` diff --git a/docs/src/Groups/matgroup.md b/docs/src/Groups/matgroup.md index df733faa6e50..289afcfad47c 100644 --- a/docs/src/Groups/matgroup.md +++ b/docs/src/Groups/matgroup.md @@ -72,15 +72,15 @@ isometry_group(f::SesquilinearForm{T}) where T orthogonal_sign(G::MatrixGroup) ``` -## Utilities for matrices (replace by available functions, or document elsewhere?) +## Utilities for matrices ```@docs pol_elementary_divisors(A::MatElem{T}) where T generalized_jordan_block(f::T, n::Int) where T<:PolyRingElem generalized_jordan_form(A::MatElem{T}; with_pol=false) where T matrix(A::Vector{AbstractAlgebra.Generic.FreeModuleElem{T}}) where T <: RingElem -upper_triangular_matrix(L) -lower_triangular_matrix(L) +upper_triangular_matrix(L::AbstractVector{T}) where {T <: NCRingElement} +lower_triangular_matrix(L::AbstractVector{T}) where {T <: NCRingElement} conjugate_transpose(x::MatElem{T}) where T <: FinFieldElem complement(V::AbstractAlgebra.Generic.FreeModule{T}, W::AbstractAlgebra.Generic.Submodule{T}) where T <: FieldElem permutation_matrix(F::Ring, Q::AbstractVector{<:IntegerUnion}) diff --git a/docs/src/Groups/quotients.md b/docs/src/Groups/quotients.md index d1bb83bbbd6b..5dc5a22c576b 100644 --- a/docs/src/Groups/quotients.md +++ b/docs/src/Groups/quotients.md @@ -11,7 +11,7 @@ Quotient groups in OSCAR can be defined using the instruction `quo` in two ways. * Quotients by normal subgroups. ```@docs -quo(G::T, H::T) where T <: GAPGroup +quo(G::GAPGroup, N::GAPGroup) ``` * Quotients by elements. diff --git a/docs/src/Groups/subgroups.md b/docs/src/Groups/subgroups.md index b46096f38cba..1f09cd5fb654 100644 --- a/docs/src/Groups/subgroups.md +++ b/docs/src/Groups/subgroups.md @@ -11,14 +11,14 @@ The following functions are available in OSCAR for subgroup properties: ```@docs sub(G::GAPGroup, gens::AbstractVector{<:GAPGroupElem}; check::Bool = true) -is_subset(H::T, G::T) where T <: GAPGroup -is_subgroup(H::T, G::T) where T <: GAPGroup -embedding(H::T, G::T) where T <: GAPGroup -index(G::T, H::T) where T <: Union{GAPGroup, FinGenAbGroup} -is_maximal_subgroup(H::T, G::T) where T <: GAPGroup -is_normalized_by(H::T, G::T) where T <: GAPGroup -is_normal_subgroup(H::T, G::T) where T <: GAPGroup -is_characteristic_subgroup(H::T, G::T) where T <: GAPGroup +is_subset(H::GAPGroup, G::GAPGroup) +is_subgroup(H::GAPGroup, G::GAPGroup) +embedding(H::GAPGroup, G::GAPGroup) +index(G::GAPGroup, H::GAPGroup) +is_maximal_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) +is_normalized_by(H::GAPGroup , G::GAPGroup) +is_normal_subgroup(H::GAPGroup, G::GAPGroup) +is_characteristic_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) ``` ## Standard subgroups @@ -72,7 +72,7 @@ Usually it is more efficient to work with (representatives of) the underlying conjugacy classes of subgroups instead. ```@docs -complements(G::T, N::T) where T <: GAPGroup +complements(G::GAPGroup, N::GAPGroup) hall_subgroups low_index_subgroups maximal_subgroups @@ -87,11 +87,11 @@ is_conjugate(G::GAPGroup, H::GAPGroup, K::GAPGroup) is_conjugate_with_data(G::GAPGroup, x::GAPGroupElem, y::GAPGroupElem) is_conjugate_with_data(G::GAPGroup, H::GAPGroup, K::GAPGroup) centralizer(G::GAPGroup, x::GAPGroupElem) -centralizer(G::T, H::T) where T <: GAPGroup +centralizer(G::GAPGroup, H::GAPGroup) normalizer(G::GAPGroup, x::GAPGroupElem) -normalizer(G::T, H::T) where T<:GAPGroup -core(G::T, H::T) where T<:GAPGroup -normal_closure(G::T, H::T) where T<:GAPGroup +normalizer(G::GAPGroup, H::GAPGroup) +core(G::GAPGroup, H::GAPGroup) +normal_closure(G::GAPGroup, H::GAPGroup) ``` ```@docs @@ -100,7 +100,7 @@ representative(G::GroupConjClass) acting_group(G::GroupConjClass) number_of_conjugacy_classes(G::GAPGroup) conjugacy_class(G::GAPGroup, g::GAPGroupElem) -conjugacy_class(G::T, g::T) where T<:GAPGroup +conjugacy_class(G::GAPGroup, H::GAPGroup) conjugacy_classes(G::GAPGroup) complement_classes hall_subgroup_classes @@ -121,13 +121,13 @@ is_left(c::GroupCoset) is_bicoset(C::GroupCoset) acting_domain(C::GroupCoset) representative(C::GroupCoset) -right_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup -left_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup -right_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup -left_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup +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 GroupDoubleCoset{T <: GAPGroup, S <: GAPGroupElem} -double_coset(G::T, g::GAPGroupElem{T}, H::T) where T<: GAPGroup -double_cosets(G::T, H::T, K::T; check::Bool) where T<: GAPGroup +double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup) +double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup left_acting_group(C::GroupDoubleCoset) right_acting_group(C::GroupDoubleCoset) representative(C::GroupDoubleCoset) diff --git a/docs/src/NoncommutativeAlgebra/PBWAlgebras/creation.md b/docs/src/NoncommutativeAlgebra/PBWAlgebras/creation.md index f410b760bb95..f114a161e880 100644 --- a/docs/src/NoncommutativeAlgebra/PBWAlgebras/creation.md +++ b/docs/src/NoncommutativeAlgebra/PBWAlgebras/creation.md @@ -37,7 +37,7 @@ Note that any global monomial ordering on $\text{Mon}_{2n}(x, \partial)$ is adm The constructor below returns the algebras equipped with `degrevlex`. ```@docs - weyl_algebra(K::Ring, xs::AbstractVector{<:VarName}) +weyl_algebra(K::Ring, xs::AbstractVector{<:VarName}) ``` ### Universal Enveloping Algebras of Finite Dimensional Lie Algebras diff --git a/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md b/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md index fce9de8db379..60981c8ab116 100644 --- a/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md +++ b/docs/src/NoncommutativeAlgebra/PBWAlgebras/quotients.md @@ -38,7 +38,7 @@ modulo the two-sided ideal $\langle e_1^2,\dots, e_n^2\rangle.$ ```@docs - exterior_algebra +exterior_algebra ``` ## Data Associated to Affine GR-Algebras diff --git a/examples/BinomIdeal.jl b/examples/BinomIdeal.jl deleted file mode 100644 index 3278293a02d4..000000000000 --- a/examples/BinomIdeal.jl +++ /dev/null @@ -1,1096 +0,0 @@ -module BinomialIdeal - -using Oscar -using Oscar: PartialCharacter, have_same_span, partial_character, saturations -import Oscar.lib4ti2_jll - -using DelimitedFiles - -export is_binomial, is_cellular, is_unital, QQAbField, Binomial, Cellular - -import Oscar: singular_assure, MPolyIdeal -import Oscar.Singular: std, Ideal, lead_exponent - -#functions in this file: - -#isCellular(I::Singular.sideal) -#saturate(I::Singular.sideal, J::Singular.sideal) -#cellularDecomp(I::Singular.sideal) -#isBinomial(f::Singular.spoly) -#isBinomialIdeal(I::Singular.sideal) -#isUnital(I::Singular.sideal) -#nemo(a::Singular.n_unknown{}) -#lead_coeff(f::Singular.spoly) -#intersectionArray(A::Vector{Singular.sideal}) -#extractInclusionMinimalIdeals(A::Vector{Any}) -#removeSomeRedundancies(A::Vector{Any}) -#monomialFromVector(a::Vector{Int64}, R::Singular.PolyRing) -#markov4ti2(L::ZZMatrix) -#"=="(I::Singular.sideal,J::Singular.sideal) -#isSubset(I::Singular.sideal,J::Singular.sideal) -#idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) -#partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) -#cellularStandardMonomials(I::Singular.sideal) -#witnessMonomials(I::Singular.sideal) -#cellularPrimaryDecomposition(I::Singular.sideal) -#cellularMinimalAssociatedPrimes(I::Singular.sideal) -#cellularAssociatedPrimes(I::Singular.sideal) -#cellularHull(I::Singular.sideal) - - -################################################################################### -# -# Helper functions -# -################################################################################### - -function saturate(I::Singular.sideal,J::Singular.sideal) - #input: two ideals in the same ring - #output: array with two entries: the first is the saturation of I with respect to J - #the second is an integer k for which I:J^k=I:J^(k+1)=I:J^\infty - - check_parent(I,J) - flag=true - k=0 - If=I - Iff=I - while flag - Iff = Singular.quotient(If, J) - if isSubset(Iff,If)==true - flag =false - return [If,k] - else - k=k+1 - end - If=Iff - end -end -function is_binomial(f::MPolyRingElem) - return length(f) <= 2 -end -function isBinomial(f::Singular.spoly) - #check if the input polynomial is a binomial - if Singular.length(f)<=2 - return(true) - else - return(false) - end -end -function is_binomial(I::MPolyIdeal) - singular_assure(I) - return isBinomialIdeal(I.gens.S) -end - -function isBinomialIdeal(I::Singular.sideal) - #check if the input ideal is a binomial ideal - #compute reduced GB and check if all elements in the GB are binomials - - I=std(I,complete_reduction=true) - return all(isBinomial, Singular.gens(I)) -end - -function is_unital(I::MPolyIdeal) - singular_assure(I) - return isUnital(I.gens.S) -end - -function isUnital(I::Singular.sideal) - #check if I is a unital ideal - #(i.e. if it is generated by pure difference binomials and monomials) - - I=std(I,complete_reduction=true) - counter=0 - for i=1:Singular.ngens(I) - if !isBinomial(I[i]) - return false - end - c = collect(coefficients(I[i])) - if length(c)==2 && c[1]==1 && c[2]==-1 - counter=counter+1 - end - if length(c)==2 && c[1]==-1 && c[2]==1 - counter=counter+1 - end - if length(c)==1 #case I[i] is monomial - counter=counter+1 - end - end - return counter==Singular.ngens(I) -end - -function markov4ti2(L::ZZMatrix) - #sanity checks noch einbauen!! - nc=Nemo.ncols(L) - nr=Nemo.nrows(L) - #have to prepare an input file for 4ti2 - #create the file julia4ti2.lat - name = tempname() - mkdir(name) - name = joinpath(name, "julia4ti2") - f=open("$name.lat","w") - write(f,"$nr ") - write(f,"$nc \n") - - for i=1:nr - for j=1:nc - write(f,"$(L[i,j]) ") - end - write(f,"\n") - end - close(f) - - #now we have the file julia4ti2.lat in the current working directory - #can run 4ti2 with this input file to get a markov basis - run(ignorestatus(`$(lib4ti2_jll.exe4ti2gmp()) markov -q $name`)) - #this creates the file julia4ti2.mar with the markov basis - - #now we have to get the matrix from julia4ti2.mat in julia - #this is an array of thype Any - helpArray=readdlm("$name.mar") - sizeHelpArray=size(helpArray) - - #the size of the markov basis matrix is - nColMarkov=Int(helpArray[1,2]) - nRowMarkov=Int(helpArray[1,1]) - - #now we have convert the lower part of the array helpArray into an Array of type Int64 - helpArrayInt=zeros(Int64,nRowMarkov,nColMarkov) - - for i=2:(nRowMarkov+1) - for j=1:nColMarkov - helpArrayInt[i-1,j]=helpArray[i,j] - end - end - - ##remove constructed files - #run(`rm julia4ti2.lat`) - #run(`rm julia4ti2.mar`) - - return helpArrayInt -end - -function monomialFromVector(a::Vector{Int64}, R::Singular.PolyRing) - #returns the monomial corresponding to an exponent vector - - Variables=Singular.gens(R) - monom=R(1) - for i=1:Singular.nvars(R) - monom=monom*Variables[i]^a[i] - end - return monom -end - -function lead_coeff(f::Singular.spoly) - return first(coefficients(f)) -end - -function matrixFromArray(A::Vector{Int64}) - #returns a matrix over FlintZZ with the entries of A transposed - Mat=zeros(Int64,size(A,1),1) - MatFlint=matrix(FlintZZ,size(A,1),1,Mat) - for i=1:size(A,1) - MatFlint[i,1]=A[i] - end - return(MatFlint) -end - -function nemo(a::Singular.n_unknown{}) - #returns the nemo value of a Singular.n_unknown element - return Singular.libSingular.julia(Singular.libSingular.cast_number_to_void(a.ptr)) -end - -import Base.== - -function ==(I::Singular.sideal,J::Singular.sideal) - if isSubset(I,J)==true && isSubset(J,I)==true - return true - else - return false - end -end - -function isSubset(I::Singular.sideal,J::Singular.sideal) - #returns true if I is contained in J - #else returns false - if J.isGB==false - J=std(J) - end - testIdeal=reduce(I,J) - if iszero(testIdeal)==true - return true - end - return false -end - -function removeSomeRedundancies(A::Vector{Any}) - #input:Array of ideals - #output:Array of ideals consisting of some ideals less which give the same intersections as - #all ideals before - - Result=A - for i=size(A,1):-1:1 - k=i-1 - flag=true - while flag==true && k>0 - if isSubset(Result[k],Result[i])==true - flag=false #we can remove the ideal Result[i] - end - k=k-1 - end - if flag==false - deleteat!(Result,i) - end - end - - for i=size(Result,1):-1:1 - k=i-1 - flag=true - while flag==true && k>0 - if isSubset(Result[i],Result[k])==true - flag=false #we can remove the ideal Result[k] - else - k=k-1 - end - end - if flag == false - deleteat!(Result,k) - end - end - - return Result -end - -function extractInclusionMinimalIdeals(A::Vector{Any}) - #returns all ideals of A which are minimal with respect to inclusion - - n=size(A,1) - #work with a copy of A - Result=A - - while n>0 - if typeof(Result[1])!= Singular.sideal{Singular.spoly{Singular.n_unknown{QQAbElem}}} - error("input has to be an Array of ideals") - end - helpIdeal=Result[1] - #now delete ideal from Array - deleteat!(Result,1) - flag=true #if ideal helpIdeal is redundant the flag is false - k=1 - while flag==true && k<=size(Result,1) - if isSubset(Result[k],helpIdeal)==true - flag=false - end - k=k+1 - end - if flag==true - Result=[Result;helpIdeal] - end - n=n-1 - end - return Result -end - -function extractInclusionMinimalIdeals(A::Vector{Singular.sideal}) - #returns all ideals of A which are minimal with respect to inclusion - - n=size(A,1) - #work with a copy of A - Result=A - - while n>0 - helpIdeal=Result[1] - #now delete ideal from Array - deleteat!(Result,1) - flag=true #if ideal helpIdeal is redundant the flag is false - k=1 - while flag==true && k<=size(Result,1) - if isSubset(Result[k],helpIdeal)==true - flag=false - end - k=k+1 - end - if flag==true - Result=[Result;helpIdeal] - end - n=n-1 - end - return Result -end - -function deleteZerosInHNF(m::ZZMatrix) - #deletes zero rows in the hnf of m - m=hnf(m) - i=Nemo.nrows(m) - cm=Nemo.ncols(m) - s=sub(m,i:i,1:cm) - ZeroTest=matrix(FlintZZ,1,cm,zeros(Int64,1,cm)) - while s==ZeroTest - m=sub(m,1:(rows(m)-1),1:cm) - i=rows(m) - s=sub(m,i:i,1:cm) - end - return m -end - - -################################################################################### -# -# Functions concerning a cellular decomposition -# -################################################################################### - -function isCellular(I::Singular.sideal) - #input: binomial ideal in a polynomial ring - #output: the decision true/false whether I is cellular or not - #if it is cellular, return true and the cellular variables, otherwise return the - #index of a variable which is a zerodivisor but not nilpotent modulo I - if I.isGB==false - I=std(I) - end - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - if iszero(I)==true - return(false,-1) - end - - if I[1]==1 - return(false,-1) - end - - DeltaC=Int64[] - Delta=Int64[] - Variables=Singular.gens(I.base_ring) - helpideal=Ideal(I.base_ring) - - for i=1:Singular.nvars(I.base_ring) - helpideal=Ideal(I.base_ring,Variables[i]) - satu=saturate(I,helpideal) - if (std(satu[1])[1])!=1 - push!(Delta,i) - end - end - - #compute product of ring variables in Delta - prodRingVar=one(I.base_ring) - for k in Delta - prodRingVar=prodRingVar*Variables[k] - end - - prodRingVarIdeal=Ideal(I.base_ring,prodRingVar) - J=saturate(I,prodRingVarIdeal) - - if isSubset(J[1],I)==true - #then I==J - #in this case I is cellular with respect to Delta - return(true,Delta) - else - for i in Delta - J=Singular.quotient(I,Ideal(I.base_ring,Variables[i])) - if iszero(reduce(J,I))==false - #if Singular.ngens(std(reduce(J,I)))!=0 - return (false,i) - end - end - end -end - -function is_cellular(I::MPolyIdeal) - singular_assure(I) - return isCellular(I.gens.S) -end - -function cellularDecomp(I::Singular.sideal) #with less redundancies - #input: binomial ideal I - #output: a cellular decomposition of I - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - A=isCellular(I) - if A[1]==true - return [I] - end - - #choose a variable which is a zero divisor but not nilptent modulo I -> A[2] (if not dummer fall) - #determine the power s s.t. (I:x_i^s)==(I:x_i^infty) - satu=saturate(I,Ideal(I.base_ring,Singular.gens(I.base_ring)[A[2]])) - s=satu[2] - #now compute the cellular decomposition of the binomial ideals (I:x_i^s) and I+(x_i^s) - #by recursively calling the algorithm - Decomp=Singular.sideal[] - I1=satu[1] - I2=I+Ideal(I.base_ring,(Singular.gens(I.base_ring)[A[2]])^s) - - DecompI1=cellularDecomp(I1) - DecompI2=cellularDecomp(I2) - - #now check for redundancies - redTest=Ideal(I.base_ring,one(I.base_ring)) - redTestIntersect=Ideal(I.base_ring,one(I.base_ring)) - - for i=1:size(DecompI1,1) - redTestIntersect=Singular.intersection(redTest,DecompI1[i]) - if Singular.ngens(std(reduce(redTest,std(redTestIntersect))))!=0 - #ideal not redundant - Decomp=[Decomp;DecompI1[i]] - end - redTest=redTestIntersect - end - for i=1:size(DecompI2,1) - redTestIntersect=Singular.intersection(redTest,DecompI2[i]) - if iszero(reduce(redTest,std(redTestIntersect)))==false - #ideal not redundant - Decomp=[Decomp;DecompI2[i]] - end - redTest=redTestIntersect - end - return Decomp -end - -function cellularDecompMacaulay(I::Singular.sideal) - #algorithm after Macaulay2 implementation for computing a cellular decomposition of a binomial ideal - #seems to be faster than cellularDecomp, but there are still examples which are really slow - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - R=base_ring(I) - n=Singular.nvars(R) - intersectAnswer=Ideal(R,R(1)) - Answer=Singular.sideal[] - ToDo=[([R(1)],Singular.gens(R),I)] - #every element in the todo list has three dedicated data: - #1: contains a list of variables w.r.t. which it is already saturated - #2: contains variables to be considered for cell variables - #3: is the ideal to decompose - - compo=0 - while size(ToDo,1)>0 - L=ToDo[1] - - if size(ToDo,1)>1 - ToDo=ToDo[2:size(ToDo,1)] - else - ToDo=[] - end - - if iszero(reduce(intersectAnswer,std(L[3])))==true - #found redundant component - elseif size(L[2],1)==0 - #no variables remain to check -> we have an answer - compo=compo+1 - newone=L[3] #ideal - Answer=[Answer;newone] - intersectAnswer=Singular.intersection(intersectAnswer,newone) - if intersectAnswer==I - ToDo=[] - end - else - #there are remaining variables - i=L[2][1] #variable under consideration - if size(L[2],1)>1 - newL2=L[2][2:size(L[2],1)] - else - newL2=[] - end - result=saturate(L[3],Ideal(R,i)) - J=result[1] #ideal - k=result[2] #saturation exponent - if k>0 - #if a division was needed we add the monomial i^k to the ideal - #under consideration - J2=L[3]+ Ideal(R,i^k) - - #compute product of all variables in L[1] - prod=R(1) - for m=1:size(L[1],1) - prod=prod*L[1][m] - end - J2=saturate(J2,Ideal(R,prod))[1] - if isSubset(Ideal(R,R(1)),J2)==false - #we have to decompose J2 further - ToDo=[ToDo; (L[1],newL2,J2)] - end - end - #continue with the next variable and add i to L[1] - if isSubset(Ideal(R,R(1)),J)==false - ToDo=[ToDo;([L[1];i], newL2, J)] - end - - end - end - return extractInclusionMinimalIdeals(Answer) -end - - -################################################################################### -# -# Partial characters and ideals -# -################################################################################### - -function idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) - #input: partial character P and a polynomial ring R - #output: the ideal $I_+(P)=\langle x^{u_+}- P(u)x^{u_-} \mid u \in P.A \rangle$ - - @assert Nemo.ncols(P.A)==Singular.nvars(R) - #test if the domain of the partial character is the zero lattice - Zero=matrix(FlintZZ,1,Nemo.ncols(P.A),zeros(Int64,1,Nemo.ncols(P.A))) - if Nemo.nrows(P.A)==1 && have_same_span(P.A,Zero)==true - return Ideal(R,zero(R)) - end - - #now case if P.A is the identity matrix - #then the ideal generated by the generators of P.A suffices and gives the whole ideal I_+(P) - #note that we can only compare the matrices if P.A is a square matrix - if Nemo.ncols(P.A)==Nemo.nrows(P.A) - id=convert(Array{Int64},eye(Nemo.ncols(P.A))) - ID=matrix(FlintZZ,Nemo.ncols(P.A),Nemo.ncols(P.A),id) - if P.A==ID - return(makeBinomials(P,R)) - end - end - - #now check if the only values of P taken on the generators of the lattice is one - #then we can use markov bases - #simple test - test=true - i=1 - Variables=Singular.gens(R) - I=Ideal(R,zero(R)) - - while test==true && i<=size(P.b,1) - if P.b[i]!=QQAbField()(1) - #in this case there is a generator g for which P(g)!=1 - test=false - end - i=i+1 - end - - if test==true - #then we can use markov bases to get the ideal - A=markov4ti2(P.A) - #now get the ideal corresponding to the computed markov basis - nr=size(A,1) #number of rows - nc=size(A,2) #number of columns - #-> we have nr generators for the ideal - #for each row vector compute the corresponding binomial - for k=1:nr - monomial1=one(R) - monomial2=one(R) - for s=1:nc - if A[k,s]<0 - monomial2=monomial2*Variables[s]^(-A[k,s]) - else - monomial1=monomial1*Variables[s]^(A[k,s]) - end - end - #the new generator for the ideal is monomial1-minomial2 - I=I+Ideal(R,monomial1-monomial2) - end - return I - end - - #now consider the last case where we have to saturate - I=makeBinomials(P,R) - #now we have to saturate the ideal by the product of the ring variables - varProduct=one(R) - for i=1:Singular.nvars(R) - varProduct=varProduct*Variables[i] - end - - return saturate(I,Ideal(R,varProduct))[1] -end - -function makeBinomials(P::PartialCharacter, R::Singular.PolyRing) - #output: ideal generated by the binomials corresponding to the generators of the domain P.A of the partial character P - #Note: This is not the ideal I_+(P)!! - - @assert Nemo.ncols(P.A)==Singular.nvars(R) - nc=Nemo.ncols(P.A) #number of columns - nr=Nemo.nrows(P.A) #number of rows - Variables=Singular.gens(R) - #-> we have nr binomial generators for the ideal - I=Ideal(R,zero(R)) - - for k=1:nr - monomial1=one(R) - monomial2=one(R) - for s=1:nc - if P.A[k,s]<0 - monomial2=monomial2*Variables[s]^(Int64(-P.A[k,s])) - else - monomial1=monomial1*Variables[s]^(Int64(P.A[k,s])) - end - end - #the new generator for the ideal is monomial1-P.b[k]*monomial2 - I=I+Ideal(R,monomial1-P.b[k]*monomial2) - end - return I -end - -function partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) - #input: cellular binomial ideal - #output: the partial character corresponding to the ideal I \cap k[\mathbb{N}^\Delta] - - #first test if the input ideal is really a cellular ideal - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - Delta=cell[2] #cell variables - if size(Delta,1)==0 - P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) - return P - end - - #now consider the case where Delta is not empty - #fist compute the intersection I \cap k[\Delta] - #for this use eliminate function from Singular. We first have to compute the product of all - #variables not in Delta -#= -Singular.eliminate seems to want a sequence of variables after the ideal -argument, so we make an array prodDeltaC, push to it, then splice it in -=# - prodDeltaC = Any[] - Variables=Singular.gens(R) - for i=1:Singular.nvars(R) - if (i in Delta)==false - push!(prodDeltaC, Variables[i]) - end - end - - J=Singular.eliminate(I, prodDeltaC...) - if iszero(J)==true - #if Singular.ngens(J)==0 || (Singular.ngens(J)==1 && J[1]== R(0)) - #return another trivial character - #lattice has only one generator, namely the zero vector - P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) - return P - end - #now case if J \neq 0 - #let ts be a list of minimal binomial generators for J - J=std(J,complete_reduction=true) - ts=Array{Singular.spoly}[] - for i=1:Singular.ngens(J) - ts=[ts; J[i]] - end - vs=zeros(Int64,Singular.nvars(R),1) - images=QQAbElem[] - for t in ts - tCopy=t - u=lead_exponent(t) - m=monomialFromVector(u,R) - tCopy=tCopy-m - v=lead_exponent(tCopy) - #now test if we need the vector uv - uv=matrixFromArray(u-v) #this is the vector of u-v - vst=transpose(vs) - vstMat=matrix(FlintZZ,size(vst,1), size(vst,2),vst) - if(can_solve(transpose(vstMat),uv)==false) - images=[images; nemo(-lead_coeff(tCopy))] - #we have to save u-v as generator for the lattice - #now concatenate the vector vs on bottom of the matrix vs - vs=[vs (u-v)] - end - end - #now vs has a zero in the left column and we have to delete it - #but first we have to transpose vs - vs=transpose(vs) - - vs=vs[2:size(vs,1),1:size(vs,2)] - #now convert to matrix - vsMat=matrix(FlintZZ,size(vs,1), size(vs,2),vs) - #delete zero rows in the hnf of vsMat so that we do not get problems when considering a - #saturation - vsMat=deleteZerosInHNF(vsMat) - P=partial_character(vsMat, images , Set{Int64}(Delta)) - return P -end - - -################################################################################### -# -# Embedded associated lattice witnesses and hull -# -################################################################################### - -function cellularStandardMonomials(I::Singular.sideal) - #input: cellular ideal I - #output: the standardmonomials of the ideal I \cap k[\mathbb{N}^{\Delta^c}], - #this are only finitely many! - - if I.isGB==false - I=std(I) - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - R=Singular.base_ring(I) - - #now we start computing the standardmonomials - #first determine the set Delta^c of noncellular variables - DeltaC=Array{Int64}[] - for i=1: Singular.nvars(R) - if (i in cell[2])==false - DeltaC=[DeltaC;i] - end - end - - #eliminate the variables in Delta - Variables=Singular.gens(R) - prodDelta = [Variables[i] for i in cell[2]] - J=Singular.eliminate(I, prodDelta...) - - leadIdeal=Singular.lead(J) - leadIdeal=Singular.std(leadIdeal) - - mon=Array{Singular.spoly}[] #this will hold set of standard monomials - Answer=Array[] - Result=Array{Singular.spoly}[] - - for i in DeltaC - flag=true - mon=[mon;Variables[i]^0] - d=1 - while flag ==true - if reduce(Variables[i]^d,I) == 0 - flag=false - else - mon=[mon;Variables[i]^d] - d=d+1 - end - - end - push!(Answer,mon) - mon=Array{Singular.spoly}[] - end - - for a in my_product(Answer) - testmon=R(1) - for l in a - testmon=testmon*l - end - if reduce(testmon,leadIdeal)!= 0 - Result=[Result;testmon] - end - end - return Result -end - -function my_product(P::Array) - T = ntuple(x->P[x], length(P)) - return Iterators.product(T...) -end - -function witnessMonomials(I::Singular.sideal) - #input: cellular binomial ideal - #output: M_{emb}(I) (not the ideal, but the generators of it in an array) - - #test if input ideal is cellular - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - R=Singular.base_ring(I) - Delta=cell[2] - - #compute the PartialCharacter corresponding to I and the standard monomials of I \cap k[N^Delta] - P=partialCharacterFromIdeal(I, R) - M=cellularStandardMonomials(I) #array of standard monomials, this is our to-do list - Memb=Singular.spoly[] #this will hold our set of witness monomials - Iquotm=Ideal(R,R(0)) - Pquotm=partialCharacterFromIdeal(I, R) - - while size(M,1)!=0 - Iquotm=Singular.quotient(I,Ideal(R,M[1])) - Pquotm=partialCharacterFromIdeal(Iquotm, R) - if rank(Pquotm.A)>rank(P.A) - Memb=[Memb;M[1]] - end - #by checking for divisibility of the monomials in M by M[1] respectively of M[1] - #by monomials in M, some monomials in M necessarily belong to Memb, respectively can #be directly excluded from being elements of Memb - #todo: implement this for improvement - deleteat!(M,1) - end - return(Memb) -end - -function cellularHull(I::Singular.sideal) - #input: cellular binomial ideal - #output: hull(I), the intersection of all minimal primary components of I - #by theorems we know that Hull(I)=M_emb(I)+I - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - #now construct the ideal M_emb with the above algorithm witnessMonomials - R = I.base_ring - Memb=Ideal(I.base_ring,R(0)) #this will hold the ideal M_emb(I) - M=witnessMonomials(I) - - for m in M - Memb=Memb + Ideal(R,m) - end - - return (I+Memb) -end - - -################################################################################### -# -# Associated primes -# -################################################################################### - -function cellularAssociatedPrimes(I::Singular.sideal) - #input: cellular binomial ideal - #output: the set of associated primes of I - - if isUnital(I)==false - error("input ideal has to be a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - if I.isGB==false - I=std(I) - end - - Ass=Array{Singular.sideal}[] #this will hold the set of associated primes of I - R = I.base_ring - Variables=Singular.gens(R) - U=cellularStandardMonomials(I) #set of standard monomials - - #construct the ideal (x_i \mid i \in \Delta^c) - idealDeltaC=Ideal(R,R(0)) - for i=1:Singular.nvars(R) - if (i in cell[2])==false - idealDeltaC=idealDeltaC + Ideal(R,Variables[i]) - end - end - - for m in U - Im=Singular.quotient(I,Ideal(I.base_ring,m)) - Pm=partialCharacterFromIdeal(Im,Im.base_ring) - #now compute all saturations of the partial character Pm - PmSat=saturations(Pm) - for P in PmSat - Ass=[Ass; (idealFromCharacter(P, I.base_ring)+idealDeltaC)] - end - end - - #now check if there are superfluous elements in Ass - for i=size(Ass,1):-1:1 - j=i-1 - flag = false - while (j>0 && flag==false) - if Ass[j]==Ass[i] - deleteat!(Ass,i) - flag =true - end - j=j-1 - end - end - - return Ass -end - -function cellularMinimalAssociatedPrimes(I::Singular.sideal) - #input: cellular unital ideal - #output: the set of minimal associated primes of I - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - P=partialCharacterFromIdeal(I,I.base_ring) - PSat=saturations(P) - minAss=Array{Singular.sideal}[] #this will hold the set of minimal associated primes - - #construct the ideal (x_i \mid i \in \Delta^c) - R = I.base_ring - Variables=Singular.gens(R) - idealDeltaC=Ideal(R,R(0)) - for i=1:Singular.nvars(I.base_ring) - if (i in cell[2])==false - idealDeltaC=idealDeltaC + Ideal(I.base_ring,Variables[i]) - end - end - - for Q in PSat - minAss=[minAss; (idealFromCharacter(Q,I.base_ring)+idealDeltaC)] - end - - return minAss -end - -function binomialAssociatedPrimes(I::Singular.sideal) - #input:unital ideal - #output: the associated primes, but only implemented effectively in the cellular case - #in the noncellular case compute a primary decomp and take radicals - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==true - return cellularAssociatedPrimes(I) - end - - #now consider the case when I is not cellular and compute a primary decomposition - PD=binomialPrimaryDecomposition(I) - - #todo: take radicals and delete the superfluous elements from the array - error("not yet implemented") - return 1 -end - - -################################################################################### -# -# Primary decomposition -# -################################################################################### - -function cellularPrimaryDecomposition(I::Singular.sideal) #algorithm from macaulay2 - #input: unital cellular binomial ideal in k[x] - #output: binomial primary ideals which form a minimal primary decomposition of I - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - #compute associated primes - Ass=cellularAssociatedPrimes(I) - C=Singular.sideal[] #this will hold the set of primary components - - #compute product of all non cellular variables and the product of all cell variables - prodDeltaC=Any[] - prodDelta=Any[] - Variables=Singular.gens(I.base_ring) - R = I.base_ring - for i=1:Singular.nvars(R) - if (i in cell[2])==false - push!(prodDeltaC, Variables[i]) - else - push!(prodDelta, Variables[i]) - end - end - - for P in Ass - helpIdeal=I+Singular.eliminate(P,prodDeltaC...) - #now saturate the ideal with respect to the cellular variables - helpIdeal=saturate(helpIdeal,Ideal(I.base_ring,prodDelta...)) - C=[C; cellularHull(helpIdeal[1])] - end - return C -end - -function binomialPrimaryDecomposition(I::Singular.sideal) - #input: unital ideal - #output: binomial primary ideals which form a not necessarily - #minimal primary decomposition of I - - #first compute a cellular decomposition of I - #cellComps=cellularDecomp(I) - cellComps=cellularDecompMacaulay(I) - - C=Array{Singular.sideal}[] #this will hold the set of primary components - - #now compute a primary decomposition of each cellular component - for J in cellComps - C=[C; cellularPrimaryDecomposition(J)] - end - - #remove some redundancies - C=removeSomeRedundancies(C) - return C -end - -module Binomial - -import Oscar: singular_assure, MPolyIdeal -import ..binomialPrimaryDecomposition - -function primary_decomposition(I::MPolyIdeal) - singular_assure(I) - p = binomialPrimaryDecomposition(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = p] -end -end - -module Cellular - -import Oscar: singular_assure, MPolyIdeal -import ..witnessMonomials, ..cellularPrimaryDecomposition, ..cellularAssociatedPrimes, - ..cellularMinimalAssociatedPrimes, ..cellularDecompMacaulay, ..cellularDecomp - -function primary_decomposition(I::MPolyIdeal) - singular_assure(I) - p = cellularPrimaryDecomposition(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = p] -end - -function witness_monomials(I::MPolyIdeal) - singular_assure(I) - m = witnessMonomials(I.gens.S) - return map(I.gens.Ox, m) -end - -function minimal_associated_primes(I::MPolyIdeal) - singular_assure(I) - a = cellularMinimalAssociatedPrimes(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = a] -end - -function associated_primes(I::MPolyIdeal) - singular_assure(I) - a = cellularAssociatedPrimes(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = a] -end - -function decomposition(I::MPolyIdeal; algorithm::Symbol = :auto) - singular_assure(I) - if algorithm == :auto - C = cellularDecomp(I.gens.S) - else - C = cellularDecompMacaulay(I.gens.S) - end - return [MPolyIdeal(I.gens.Ox, x) for x = C] -end - - -end - -using .Binomial, .Cellular - -end - -using Main.BinomialIdeal diff --git a/examples/BinomIdeal2.jl b/examples/BinomIdeal2.jl deleted file mode 100644 index ec0e0c037bc2..000000000000 --- a/examples/BinomIdeal2.jl +++ /dev/null @@ -1,1108 +0,0 @@ -module BinomialIdeal - -using Oscar -using Oscar: PartialCharacter, have_same_span, partial_character -import Oscar.lib4ti2_jll - -#using DelimitedFiles - -export is_binomial, is_cellular, is_unital, QQAbField, Binomial, Cellular - -import Oscar: singular_assure, MPolyIdeal -import Oscar.Singular: std, Ideal, lead_exponent - - -#functions in this file: - -#isCellular(I::Singular.sideal) -#saturate(I::Singular.sideal, J::Singular.sideal) -#cellularDecomp(I::Singular.sideal) -#isBinomial(f::Singular.spoly) -#isBinomialIdeal(I::Singular.sideal) -#isUnital(I::Singular.sideal) -#nemo(a::Singular.n_unknown{}) -#lead_coeff(f::Singular.spoly) -#intersectionArray(A::Vector{Singular.sideal}) -#extractInclusionMinimalIdeals(A::Vector{Any}) -#removeSomeRedundancies(A::Vector{Any}) -#monomialFromVector(a::Vector{Int64}, R::Singular.PolyRing) -#markov4ti2(L::ZZMatrix) -#"=="(I::Singular.sideal,J::Singular.sideal) -#isSubset(I::Singular.sideal,J::Singular.sideal) -#idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) -#partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) -#cellularStandardMonomials(I::Singular.sideal) -#witnessMonomials(I::Singular.sideal) -#cellularPrimaryDecomposition(I::Singular.sideal) -#cellularMinimalAssociatedPrimes(I::Singular.sideal) -#cellularAssociatedPrimes(I::Singular.sideal) -#cellularHull(I::Singular.sideal) - -################################################################################### -# -# Helper functions -# -################################################################################### - -function saturate(I::Singular.sideal,J::Singular.sideal) - #input: two ideals in the same ring - #output: array with two entries: the first is the saturation of I with respect to J - #the second is an integer k for which I:J^k=I:J^(k+1)=I:J^\infty - - check_parent(I,J) - flag=true - k=0 - If=I - Iff=I - while flag - Iff = Singular.quotient(If, J) - if isSubset(Iff,If)==true - flag =false - return [If,k] - else - k=k+1 - end - If=Iff - end -end -function is_binomial(f::MPolyRingElem) - return length(f) <= 2 -end -function isBinomial(f::Singular.spoly) - #check if the input polynomial is a binomial - if Singular.length(f)<=2 - return(true) - else - return(false) - end -end -function is_binomial(I::MPolyIdeal) - singular_assure(I) - return isBinomialIdeal(I.gens.S) -end - -function isBinomialIdeal(I::Singular.sideal) - #check if the input ideal is a binomial ideal - #compute reduced GB and check if all elements in the GB are binomials - - I=std(I,complete_reduction=true) - return all(isBinomial, Singular.gens(I)) -end - -function is_unital(I::MPolyIdeal) - singular_assure(I) - return isUnital(I.gens.S) -end - -function isUnital(I::Singular.sideal) - #check if I is a unital ideal - #(i.e. if it is generated by pure difference binomials and monomials) - - I=std(I,complete_reduction=true) - counter=0 - for i=1:Singular.ngens(I) - if !isBinomial(I[i]) - return false - end - c = collect(coefficients(I[i])) - if length(c)==2 && c[1]==1 && c[2]==-1 - counter=counter+1 - end - if length(c)==2 && c[1]==-1 && c[2]==1 - counter=counter+1 - end - if length(c)==1 #case I[i] is monomial - counter=counter+1 - end - end - return counter==Singular.ngens(I) -end - -function markov4ti2(L::ZZMatrix) - #sanity checks noch einbauen!! - nc=Nemo.ncols(L) - nr=Nemo.nrows(L) - #have to prepare an input file for 4ti2 - #create the file julia4ti2.lat - name = tempname() - mkdir(name) - name = joinpath(name, "julia4ti2") - f=open("$name.lat","w") - write(f,"$nr ") - write(f,"$nc \n") - - for i=1:nr - for j=1:nc - write(f,"$(L[i,j]) ") - end - write(f,"\n") - end - close(f) - - #now we have the file julia4ti2.lat in the current working directory - #can run 4ti2 with this input file to get a markov basis - run(ignorestatus(`$(lib4ti2_jll.exe4ti2gmp()) markov -q $name`)) - -# run(`$(lib4ti2_jll.markov) -q $name`) - #this creates the file julia4ti2.mar with the markov basis - - #now we have to get the matrix from julia4ti2.mat in julia - #this is an array of thype Any - helpArray=readdlm("$name.mar") - sizeHelpArray=size(helpArray) - - #the size of the markov basis matrix is - nColMarkov=Int(helpArray[1,2]) - nRowMarkov=Int(helpArray[1,1]) - - #now we have convert the lower part of the array helpArray into an Array of type Int64 - helpArrayInt=zeros(Int64,nRowMarkov,nColMarkov) - - for i=2:(nRowMarkov+1) - for j=1:nColMarkov - helpArrayInt[i-1,j]=helpArray[i,j] - end - end - - ##remove constructed files - #run(`rm julia4ti2.lat`) - #run(`rm julia4ti2.mar`) - - return helpArrayInt -end - -function monomialFromVector(a::Vector{Int64}, R::Singular.PolyRing) - #returns the monomial corresponding to an exponent vector - - Variables=Singular.gens(R) - monom=R(1) - for i=1:Singular.nvars(R) - monom=monom*Variables[i]^a[i] - end - return monom -end - -function lead_coeff(f::Singular.spoly) - return first(coefficients(f)) -end - -function matrixFromArray(A::Vector{Int64}) - #returns a matrix over FlintZZ with the entries of A transposed - Mat=zeros(Int64,size(A,1),1) - MatFlint=matrix(FlintZZ,size(A,1),1,Mat) - for i=1:size(A,1) - MatFlint[i,1]=A[i] - end - return(MatFlint) -end - -function nemo(a::Singular.n_unknown{}) - #returns the nemo value of a Singular.n_unknown element - return Singular.libSingular.julia(Singular.libSingular.cast_number_to_void(a.ptr)) -end - -import Base.== - -function ==(I::Singular.sideal,J::Singular.sideal) - if isSubset(I,J)==true && isSubset(J,I)==true - return true - else - return false - end -end - -function isSubset(I::Singular.sideal,J::Singular.sideal) - #returns true if I is contained in J - #else returns false - if J.isGB==false - J=std(J) - end - testIdeal=reduce(I,J) - if iszero(testIdeal)==true - return true - end - return false -end - -function removeSomeRedundancies(A::Vector{Any}, B::Vector{Any}) - #input:two Array of ideals, the first are primary ideals, the second the corresponding associated primes - #output:Arrays of ideals consisting of some ideals less which give the same intersections as - #all ideals before - - Result1=A - Result2=B - for i=size(A,1):-1:1 - k=i-1 - flag=true - while flag==true && k>0 - if isSubset(Result1[k],Result1[i])==true - flag=false #we can remove the ideal Result1/2[i] - end - k=k-1 - end - if flag==false - deleteat!(Result1,i) - deleteat!(Result2,i) - end - end - - for i=size(Result1,1):-1:1 - k=i-1 - flag=true - while flag==true && k>0 - if isSubset(Result1[i],Result1[k])==true - flag=false #we can remove the ideal Result1/2[k] - else - k=k-1 - end - end - if flag == false - deleteat!(Result1,k) - deleteat!(Result2,k) - end - end - - return Result1,Result2 -end - -function extractInclusionMinimalIdeals(A::Vector{Any}) - #returns all ideals of A which are minimal with respect to inclusion - - n=size(A,1) - #work with a copy of A - Result=A - - while n>0 - if typeof(Result[1])!= Singular.sideal{Singular.spoly{Singular.n_unknown{QQAbElem}}} - error("input has to be an Array of ideals") - end - helpIdeal=Result[1] - #now delete ideal from Array - deleteat!(Result,1) - flag=true #if ideal helpIdeal is redundant the flag is false - k=1 - while flag==true && k<=size(Result,1) - if isSubset(Result[k],helpIdeal)==true - flag=false - end - k=k+1 - end - if flag==true - Result=[Result;helpIdeal] - end - n=n-1 - end - return Result -end - -function extractInclusionMinimalIdeals(A::Vector{Singular.sideal}) - #returns all ideals of A which are minimal with respect to inclusion - - n=size(A,1) - #work with a copy of A - Result=A - - while n>0 - helpIdeal=Result[1] - #now delete ideal from Array - deleteat!(Result,1) - flag=true #if ideal helpIdeal is redundant the flag is false - k=1 - while flag==true && k<=size(Result,1) - if isSubset(Result[k],helpIdeal)==true - flag=false - end - k=k+1 - end - if flag==true - Result=[Result;helpIdeal] - end - n=n-1 - end - return Result -end - -function deleteZerosInHNF(m::ZZMatrix) - #deletes zero rows in the hnf of m - m=hnf(m) - i=Nemo.nrows(m) - cm=Nemo.ncols(m) - s=sub(m,i:i,1:cm) - ZeroTest=matrix(FlintZZ,1,cm,zeros(Int64,1,cm)) - while s==ZeroTest - m=sub(m,1:(rows(m)-1),1:cm) - i=rows(m) - s=sub(m,i:i,1:cm) - end - return m -end - - -################################################################################### -# -# Functions concerning a cellular decomposition -# -################################################################################### - -function isCellular(I::Singular.sideal) - #input: binomial ideal in a polynomial ring - #output: the decision true/false whether I is cellular or not - #if it is cellular, return true and the cellular variables, otherwise return the - #index of a variable which is a zerodivisor but not nilpotent modulo I - if I.isGB==false - I=std(I) - end - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - if iszero(I)==true - return(false,-1) - end - - if I[1]==1 - return(false,-1) - end - - DeltaC=Int64[] - Delta=Int64[] - Variables=Singular.gens(I.base_ring) - helpideal=Ideal(I.base_ring) - - for i=1:Singular.nvars(I.base_ring) - helpideal=Ideal(I.base_ring,Variables[i]) - satu=saturate(I,helpideal) - if (std(satu[1])[1])!=1 - push!(Delta,i) - end - end - - #compute product of ring variables in Delta - prodRingVar=one(I.base_ring) - for k in Delta - prodRingVar=prodRingVar*Variables[k] - end - - prodRingVarIdeal=Ideal(I.base_ring,prodRingVar) - J=saturate(I,prodRingVarIdeal) - - if isSubset(J[1],I)==true - #then I==J - #in this case I is cellular with respect to Delta - return(true,Delta) - else - for i in Delta - J=Singular.quotient(I,Ideal(I.base_ring,Variables[i])) - if iszero(reduce(J,I))==false - #if Singular.ngens(std(reduce(J,I)))!=0 - return (false,i) - end - end - end -end - -function is_cellular(I::MPolyIdeal) - singular_assure(I) - return isCellular(I.gens.S) -end - -function cellularDecomp(I::Singular.sideal) #with less redundancies - #input: binomial ideal I - #output: a cellular decomposition of I - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - A=isCellular(I) - if A[1]==true - return [I] - end - - #choose a variable which is a zero divisor but not nilptent modulo I -> A[2] (if not dummer fall) - #determine the power s s.t. (I:x_i^s)==(I:x_i^infty) - satu=saturate(I,Ideal(I.base_ring,Singular.gens(I.base_ring)[A[2]])) - s=satu[2] - #now compute the cellular decomposition of the binomial ideals (I:x_i^s) and I+(x_i^s) - #by recursively calling the algorithm - Decomp=Singular.sideal[] - I1=satu[1] - I2=I+Ideal(I.base_ring,(Singular.gens(I.base_ring)[A[2]])^s) - - DecompI1=cellularDecomp(I1) - DecompI2=cellularDecomp(I2) - - #now check for redundancies - redTest=Ideal(I.base_ring,one(I.base_ring)) - redTestIntersect=Ideal(I.base_ring,one(I.base_ring)) - - for i=1:size(DecompI1,1) - redTestIntersect=Singular.intersection(redTest,DecompI1[i]) - if Singular.ngens(std(reduce(redTest,std(redTestIntersect))))!=0 - #ideal not redundant - Decomp=[Decomp;DecompI1[i]] - end - redTest=redTestIntersect - end - for i=1:size(DecompI2,1) - redTestIntersect=Singular.intersection(redTest,DecompI2[i]) - if iszero(reduce(redTest,std(redTestIntersect)))==false - #ideal not redundant - Decomp=[Decomp;DecompI2[i]] - end - redTest=redTestIntersect - end - return Decomp -end - -function cellularDecompMacaulay(I::Singular.sideal) - #algorithm after Macaulay2 implementation for computing a cellular decomposition of a binomial ideal - #seems to be faster than cellularDecomp, but there are still examples which are really slow - - if (isBinomialIdeal(I)==false) - error("Input ideal is not binomial") - end - - R=base_ring(I) - n=Singular.nvars(R) - intersectAnswer=Ideal(R,R(1)) - Answer=Singular.sideal[] - ToDo=[([R(1)],Singular.gens(R),I)] - #every element in the todo list has three dedicated data: - #1: contains a list of variables w.r.t. which it is already saturated - #2: contains variables to be considered for cell variables - #3: is the ideal to decompose - - compo=0 - while size(ToDo,1)>0 - L=ToDo[1] - - if size(ToDo,1)>1 - ToDo=ToDo[2:size(ToDo,1)] - else - ToDo=[] - end - - if iszero(reduce(intersectAnswer,std(L[3])))==true - #found redundant component - elseif size(L[2],1)==0 - #no variables remain to check -> we have an answer - compo=compo+1 - newone=L[3] #ideal - Answer=[Answer;newone] - intersectAnswer=Singular.intersection(intersectAnswer,newone) - if intersectAnswer==I - ToDo=[] - end - else - #there are remaining variables - i=L[2][1] #variable under consideration - if size(L[2],1)>1 - newL2=L[2][2:size(L[2],1)] - else - newL2=[] - end - result=saturate(L[3],Ideal(R,i)) - J=result[1] #ideal - k=result[2] #saturation exponent - if k>0 - #if a division was needed we add the monomial i^k to the ideal - #under consideration - J2=L[3]+ Ideal(R,i^k) - - #compute product of all variables in L[1] - prod=R(1) - for m=1:size(L[1],1) - prod=prod*L[1][m] - end - J2=saturate(J2,Ideal(R,prod))[1] - if isSubset(Ideal(R,R(1)),J2)==false - #we have to decompose J2 further - ToDo=[ToDo; (L[1],newL2,J2)] - end - end - #continue with the next variable and add i to L[1] - if isSubset(Ideal(R,R(1)),J)==false - ToDo=[ToDo;([L[1];i], newL2, J)] - end - - end - end - return extractInclusionMinimalIdeals(Answer) -end - - -################################################################################### -# -# Partial characters and ideals -# -################################################################################### - -function idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) - #input: partial character P and a polynomial ring R - #output: the ideal $I_+(P)=\langle x^{u_+}- P(u)x^{u_-} \mid u \in P.A \rangle$ - - @assert Nemo.ncols(P.A)==Singular.nvars(R) - #test if the domain of the partial character is the zero lattice - Zero=matrix(FlintZZ,1,Nemo.ncols(P.A),zeros(Int64,1,Nemo.ncols(P.A))) - if Nemo.nrows(P.A)==1 && have_same_span(P.A,Zero) - return Ideal(R,zero(R)) - end - - #now case if P.A is the identity matrix - #then the ideal generated by the generators of P.A suffices and gives the whole ideal I_+(P) - #note that we can only compare the matrices if P.A is a square matrix - if Nemo.ncols(P.A)==Nemo.nrows(P.A) - id=convert(Array{Int64}, eye(Nemo.ncols(P.A))) - ID=matrix(FlintZZ,Nemo.ncols(P.A),Nemo.ncols(P.A),id) - if P.A==ID - return(makeBinomials(P,R)) - end - end - - #now check if the only values of P taken on the generators of the lattice is one - #then we can use markov bases - #simple test - test=true - i=1 - Variables=Singular.gens(R) - I=Ideal(R,zero(R)) - - while test==true && i<=size(P.b,1) - if P.b[i]!=QQAbField()(1) - #in this case there is a generator g for which P(g)!=1 - test=false - end - i=i+1 - end - - if test==true - #then we can use markov bases to get the ideal - A=markov4ti2(P.A) - #now get the ideal corresponding to the computed markov basis - nr=size(A,1) #number of rows - nc=size(A,2) #number of columns - #-> we have nr generators for the ideal - #for each row vector compute the corresponding binomial - for k=1:nr - monomial1=one(R) - monomial2=one(R) - for s=1:nc - if A[k,s]<0 - monomial2=monomial2*Variables[s]^(-A[k,s]) - else - monomial1=monomial1*Variables[s]^(A[k,s]) - end - end - #the new generator for the ideal is monomial1-minomial2 - I=I+Ideal(R,monomial1-monomial2) - end - return I - end - - #now consider the last case where we have to saturate - I=makeBinomials(P,R) - #now we have to saturate the ideal by the product of the ring variables - varProduct=one(R) - for i=1:Singular.nvars(R) - varProduct=varProduct*Variables[i] - end - - return saturate(I,Ideal(R,varProduct))[1] -end - -function makeBinomials(P::PartialCharacter, R::Singular.PolyRing) - #output: ideal generated by the binomials corresponding to the generators of the domain P.A of the partial character P - #Note: This is not the ideal I_+(P)!! - - @assert Nemo.ncols(P.A)==Singular.nvars(R) - nc=Nemo.ncols(P.A) #number of columns - nr=Nemo.nrows(P.A) #number of rows - Variables=Singular.gens(R) - #-> we have nr binomial generators for the ideal - I=Ideal(R,zero(R)) - - for k=1:nr - monomial1=one(R) - monomial2=one(R) - for s=1:nc - if P.A[k,s]<0 - monomial2=monomial2*Variables[s]^(Int64(-P.A[k,s])) - else - monomial1=monomial1*Variables[s]^(Int64(P.A[k,s])) - end - end - #the new generator for the ideal is monomial1-P.b[k]*monomial2 - I=I+Ideal(R,monomial1-P.b[k]*monomial2) - end - return I -end - -function partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) - #input: cellular binomial ideal - #output: the partial character corresponding to the ideal I \cap k[\mathbb{N}^\Delta] - - #first test if the input ideal is really a cellular ideal - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - Delta=cell[2] #cell variables - if size(Delta,1)==0 - P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) - return P - end - - #now consider the case where Delta is not empty - #fist compute the intersection I \cap k[\Delta] - #for this use eliminate function from Singular. We first have to compute the product of all - #variables not in Delta -#= -Singular.eliminate seems to want a sequence of variables after the ideal -argument, so we make an array prodDeltaC, push to it, then splice it in -=# - prodDeltaC = Any[] - Variables=Singular.gens(R) - for i=1:Singular.nvars(R) - if (i in Delta)==false - push!(prodDeltaC, Variables[i]) - end - end - - J=Singular.eliminate(I, prodDeltaC...) - if iszero(J)==true - #if Singular.ngens(J)==0 || (Singular.ngens(J)==1 && J[1]== R(0)) - #return another trivial character - #lattice has only one generator, namely the zero vector - P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) - return P - end - #now case if J \neq 0 - #let ts be a list of minimal binomial generators for J - J=std(J,complete_reduction=true) - ts=Array{Singular.spoly}[] - for i=1:Singular.ngens(J) - ts=[ts; J[i]] - end - vs=zeros(Int64,Singular.nvars(R),1) - images=QQAbElem[] - for t in ts - tCopy=t - u=lead_exponent(t) - m=monomialFromVector(u,R) - tCopy=tCopy-m - v=lead_exponent(tCopy) - #now test if we need the vector uv - uv=matrixFromArray(u-v) #this is the vector of u-v - vst=transpose(vs) - vstMat=matrix(FlintZZ,size(vst,1), size(vst,2),vst) - if(can_solve(transpose(vstMat),uv)==false) - images=[images; nemo(-lead_coeff(tCopy))] - #we have to save u-v as generator for the lattice - #now concatenate the vector vs on bottom of the matrix vs - vs=[vs (u-v)] - end - end - #now vs has a zero in the left column and we have to delete it - #but first we have to transpose vs - vs=transpose(vs) - - vs=vs[2:size(vs,1),1:size(vs,2)] - #now convert to matrix - vsMat=matrix(FlintZZ,size(vs,1), size(vs,2),vs) - #delete zero rows in the hnf of vsMat so that we do not get problems when considering a - #saturation - vsMat=deleteZerosInHNF(vsMat) - P=partial_character(vsMat, images , Set{Int64}(Delta)) - return P -end - - -################################################################################### -# -# Embedded associated lattice witnesses and hull -# -################################################################################### - -function cellularStandardMonomials(I::Singular.sideal) - #input: cellular ideal I - #output: the standardmonomials of the ideal I \cap k[\mathbb{N}^{\Delta^c}], - #this are only finitely many! - - if I.isGB==false - I=std(I) - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - R=Singular.base_ring(I) - - #now we start computing the standardmonomials - #first determine the set Delta^c of noncellular variables - DeltaC=Array{Int64}[] - for i=1: Singular.nvars(R) - if (i in cell[2])==false - DeltaC=[DeltaC;i] - end - end - - #eliminate the variables in Delta - Variables=Singular.gens(R) - prodDelta = [Variables[i] for i in cell[2]] - J=Singular.eliminate(I, prodDelta...) - - leadIdeal=Singular.lead(J) - leadIdeal=Singular.std(leadIdeal) - - mon=Array{Singular.spoly}[] #this will hold set of standard monomials - Answer=Array[] - Result=Array{Singular.spoly}[] - - for i in DeltaC - flag=true - mon=[mon;Variables[i]^0] - d=1 - while flag ==true - if reduce(Variables[i]^d,I) == 0 - flag=false - else - mon=[mon;Variables[i]^d] - d=d+1 - end - - end - push!(Answer,mon) - mon=Array{Singular.spoly}[] - end - - for a in my_product(Answer) - testmon=R(1) - for l in a - testmon=testmon*l - end - if reduce(testmon,leadIdeal)!= 0 - Result=[Result;testmon] - end - end - return Result -end - -function my_product(P::Array) - T = ntuple(x->P[x], length(P)) - return Iterators.product(T...) -end - -function witnessMonomials(I::Singular.sideal) - #input: cellular binomial ideal - #output: M_{emb}(I) (not the ideal, but the generators of it in an array) - - #test if input ideal is cellular - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - R=Singular.base_ring(I) - Delta=cell[2] - - #compute the PartialCharacter corresponding to I and the standard monomials of I \cap k[N^Delta] - P=partialCharacterFromIdeal(I, R) - M=cellularStandardMonomials(I) #array of standard monomials, this is our to-do list - Memb=Singular.spoly[] #this will hold our set of witness monomials - Iquotm=Ideal(R,R(0)) - Pquotm=partialCharacterFromIdeal(I, R) - - while size(M,1)!=0 - Iquotm=Singular.quotient(I,Ideal(R,M[1])) - Pquotm=partialCharacterFromIdeal(Iquotm, R) - if rank(Pquotm.A)>rank(P.A) - Memb=[Memb;M[1]] - end - #by checking for divisibility of the monomials in M by M[1] respectively of M[1] - #by monomials in M, some monomials in M necessarily belong to Memb, respectively can #be directly excluded from being elements of Memb - #todo: implement this for improvement - deleteat!(M,1) - end - return(Memb) -end - -function cellularHull(I::Singular.sideal) - #input: cellular binomial ideal - #output: hull(I), the intersection of all minimal primary components of I - #by theorems we know that Hull(I)=M_emb(I)+I - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - #now construct the ideal M_emb with the above algorithm witnessMonomials - R = I.base_ring - Memb=Ideal(I.base_ring,R(0)) #this will hold the ideal M_emb(I) - M=witnessMonomials(I) - - for m in M - Memb=Memb + Ideal(R,m) - end - - return (I+Memb) -end - - -################################################################################### -# -# Associated primes -# -################################################################################### - -function cellularAssociatedPrimes(I::Singular.sideal) - #input: cellular binomial ideal - #output: the set of associated primes of I - - if isUnital(I)==false - error("input ideal has to be a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - if I.isGB==false - I=std(I) - end - - Ass=Array{Singular.sideal}[] #this will hold the set of associated primes of I - R = I.base_ring - Variables=Singular.gens(R) - U=cellularStandardMonomials(I) #set of standard monomials - - #construct the ideal (x_i \mid i \in \Delta^c) - idealDeltaC=Ideal(R,R(0)) - for i=1:Singular.nvars(R) - if (i in cell[2])==false - idealDeltaC=idealDeltaC + Ideal(R,Variables[i]) - end - end - - for m in U - Im=Singular.quotient(I,Ideal(I.base_ring,m)) - Pm=partialCharacterFromIdeal(Im,Im.base_ring) - #now compute all saturations of the partial character Pm - PmSat=saturations(Pm) - for P in PmSat - Ass=[Ass; (idealFromCharacter(P, I.base_ring)+idealDeltaC)] - end - end - - #now check if there are superfluous elements in Ass - for i=size(Ass,1):-1:1 - j=i-1 - flag = false - while (j>0 && flag==false) - if Ass[j]==Ass[i] - deleteat!(Ass,i) - flag =true - end - j=j-1 - end - end - - return Ass -end - -function cellularMinimalAssociatedPrimes(I::Singular.sideal) - #input: cellular unital ideal - #output: the set of minimal associated primes of I - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - P=partialCharacterFromIdeal(I,I.base_ring) - PSat=saturations(P) - minAss=Array{Singular.sideal}[] #this will hold the set of minimal associated primes - - #construct the ideal (x_i \mid i \in \Delta^c) - R = I.base_ring - Variables=Singular.gens(R) - idealDeltaC=Ideal(R,R(0)) - for i=1:Singular.nvars(I.base_ring) - if (i in cell[2])==false - idealDeltaC=idealDeltaC + Ideal(I.base_ring,Variables[i]) - end - end - - for Q in PSat - minAss=[minAss; (idealFromCharacter(Q,I.base_ring)+idealDeltaC)] - end - - return minAss -end - -function binomialAssociatedPrimes(I::Singular.sideal) - #input:unital ideal - #output: the associated primes, but only implemented effectively in the cellular case - #in the noncellular case compute a primary decomp and take radicals - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==true - return cellularAssociatedPrimes(I) - end - - #now consider the case when I is not cellular and compute a primary decomposition - PD=binomialPrimaryDecomposition(I) - - #todo: take radicals and delete the superfluous elements from the array - error("not yet implemented") - return 1 -end - - -################################################################################### -# -# Primary decomposition -# -################################################################################### - -function cellularPrimaryDecomposition(I::Singular.sideal) #algorithm from macaulay2 - #input: unital cellular binomial ideal in k[x] - #output: binomial primary ideals which form a minimal primary decomposition of I - # and the corresponding associated primes in a second array - - if isUnital(I)==false - error("input ideal is not a unital ideal") - end - - cell=isCellular(I) - if cell[1]==false - error("input ideal is not cellular") - end - - #compute associated primes - Ass=cellularAssociatedPrimes(I) - C=Singular.sideal[] #this will hold the set of primary components - - #compute product of all non cellular variables and the product of all cell variables - prodDeltaC=Any[] - prodDelta=Any[] - Variables=Singular.gens(I.base_ring) - R = I.base_ring - for i=1:Singular.nvars(R) - if (i in cell[2])==false - push!(prodDeltaC, Variables[i]) - else - push!(prodDelta, Variables[i]) - end - end - - for P in Ass - helpIdeal=I+Singular.eliminate(P,prodDeltaC...) - #now saturate the ideal with respect to the cellular variables - helpIdeal=saturate(helpIdeal,Ideal(I.base_ring,prodDelta...)) - C=[C; cellularHull(helpIdeal[1])] - end - return C,Ass -end - -function binomialPrimaryDecomposition(I::Singular.sideal) - #input: unital ideal - #output: binomial primary ideals which form a not necessarily - #minimal primary decomposition of I, together with its corresponding associated primes - #in the same order as the primary components - - #first compute a cellular decomposition of I - #cellComps=cellularDecomp(I) - cellComps=cellularDecompMacaulay(I) - - C=Array{Singular.sideal}[] #this will hold the set of primary components - Ass=Array{Singular.sideal}[] - C2=Array{Singular.sideal}[] - Ass2=Array{Singular.sideal}[] - - #now compute a primary decomposition of each cellular component - for J in cellComps - C2,Ass2=cellularPrimaryDecomposition(J) - C=[C; C2] - Ass=[Ass; Ass2] - end - - #remove some redundancies - C, Ass=removeSomeRedundancies(C,Ass) - return C,Ass -end - -module Binomial - -import Oscar: singular_assure, MPolyIdeal -import ..binomialPrimaryDecomposition - -function primary_decomposition(I::MPolyIdeal) - singular_assure(I) - p,a = binomialPrimaryDecomposition(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = p],[MPolyIdeal(I.gens.Ox, x) for x = a] -end -end - -module Cellular - -import Oscar: singular_assure, MPolyIdeal -import ..witnessMonomials, ..cellularPrimaryDecomposition, ..cellularAssociatedPrimes, - ..cellularMinimalAssociatedPrimes, ..cellularDecompMacaulay, ..cellularDecomp - -function primary_decomposition(I::MPolyIdeal) - singular_assure(I) - p,ass = cellularPrimaryDecomposition(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = p],[MPolyIdeal(I.gens.Ox, x) for x = a] -end - -function witness_monomials(I::MPolyIdeal) - singular_assure(I) - m = witnessMonomials(I.gens.S) - return map(I.gens.Ox, m) -end - -function minimal_associated_primes(I::MPolyIdeal) - singular_assure(I) - a = cellularMinimalAssociatedPrimes(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = a] -end - -function associated_primes(I::MPolyIdeal) - singular_assure(I) - a = cellularAssociatedPrimes(I.gens.S) - return [MPolyIdeal(I.gens.Ox, x) for x = a] -end - -function decomposition(I::MPolyIdeal; algorithm::Symbol = :auto) - singular_assure(I) - if algorithm == :auto - C = cellularDecomp(I.gens.S) - else - C = cellularDecompMacaulay(I.gens.S) - end - return [MPolyIdeal(I.gens.Ox, x) for x = C] -end - - -end - -using .Binomial, .Cellular - -end - -#using Main.BinomialIdeal diff --git a/examples/H2.jl b/examples/H2.jl index d8becb6329e8..8b3ee157c706 100644 --- a/examples/H2.jl +++ b/examples/H2.jl @@ -1,14 +1,6 @@ module H2_G_QmodZ_mod using Oscar -function schur_cover(G::Oscar.GAPGroup) - f = GAP.Globals.EpimorphismSchurCover(GapObj(G)) - k = GAP.Globals.Source(f) - S = FPGroup(k) - return S, GAPGroupHomomorphism(S, G, f) -end - - #from https://sites.google.com/view/andre-macedo/code?pli=1 """ Should compute diff --git a/examples/MatrixDisplay.jl b/examples/MatrixDisplay.jl deleted file mode 100644 index 4284a713dd58..000000000000 --- a/examples/MatrixDisplay.jl +++ /dev/null @@ -1,292 +0,0 @@ -############################################################################# -## -## functionality for displaying labeled matrices, -## that is, 2-dimensional arrays of strings -## together with row labels, columns labels, header, and footer -## -## Typical examples are character tables of groups. -## -## There is support for the output of \LaTeX format strings -## as well as for column aligned text in a Julia session. -## -## The code was inspired by the ideas underlying -## [GAP's Browse package](http://www.math.rwth-aachen.de/~Browse/), -## and by Jean Michel's implementation of character table display in his -## [Julia package Gapjm.jl](https://github.com/jmichel7/Gapjm.jl). -## -#T TODO: -#T - Support left/centered/right alignment of columns and of the whole table. -#T - Concerning irrational values in character tables: -#T in `matrix_of_strings`, -#T identify also unique Galois conjugates (mark with superscript *); -#T show both values in the footer. -#T (For elements in quadratic fields, show also an expression in terms of -#T square roots.) - - -# `lpad` is wrong for example for strings containing overlined characters, -# such as "A̅". -# (This had been observed already by Jean Michel.) -mylpad(s::String,n::Int) = " "^(n-textwidth(s))*s - -# for conversions from \LaTeX format to unicode format, -# deals with subscripts and superscripts in braces `{}`, -# character values, character names -const sup = Dict(zip("-0123456789", "⁻⁰¹²³⁴⁵⁶⁷⁸⁹")) -const sub = Dict(zip("-0123456789", "₋₀₁₂₃₄₅₆₇₈₉")) - -function replace_TeX(str::String) - str = replace(str, "\\chi" => "χ") - str = replace(str, "\\phi" => "φ") - str = replace(str, "\\varphi" => "φ") - str = replace(str, "\\zeta" => "ζ") - str = replace(str, r"\^\{[-0123456789]*\}" => (s -> map(x->sup[x], s[3:end-1]))) - str = replace(str, r"_\{[-0123456789]*\}" => (s -> map(x->sub[x], s[3:end-1]))) - str = replace(str, r"\\overline\{(?[A-Z]*)\}" => s"\g\u0305") - return str -end - -""" - labeled_matrix_formatted(io::IO, mat::Matrix{String}) - -Output a formatted version of `mat` to `io`. -The following attributes of `io` are supported. - -- `:TeX`: - Produce a LaTeX format `array` if the value is `true`, - otherwise produce column aligned text format with unicode characters and - sub-/superscripts. - -- `:subset_row`: - array of positions of those rows of `mat` that shall be shown, - -- `:subset_col`: - array of positions of those columns of `mat` that shall be shown, - -- `:header`: - array of strings to be shown above `mat`, - -- `:corner`: - matrix (or vector) of strings to be shown on the left of (and aligned to) - the the column labels and above (and aligned to) the row labels - -- `:labels_col`: - matrix of strings to be shown above and aligned to the columns of `mat`, - -- `:labels_row`: - matrix (or vector) of strings to be shown on the left of (and aligned to) - the rows of `mat`, - -- `:footer`: - array of strings to be shown below `mat`, - -- `:column_separators`: - array of column numbers after which a vertical line shall be drawn - instead of a blank (enter `0` for a line in front of the first column), - -- `:row_separators`: - array of row numbers after which a horizontal line shall be drawn - (enter `0` for a line above the first row), - -- `:column_portions`: - array of column numbers after which a new labeled table shall be started; - the default is to have just one portion in the `:TeX` case, - and to create portions according to the screen width otherwise, - -- `:row_portions`: - array of row numbers after which a new labeled table shall be started; - the default is to have just one portion. - -## Examples: -... -""" -function labeled_matrix_formatted(io::IO, mat::Matrix{String}) - TeX = get(io, :TeX, false) - - subset_row = get(io, :subset_row, 1:size(mat, 1)) - subset_col = get(io, :subset_col, 1:size(mat, 2)) - m2 = size(subset_row, 1) - n2 = size(subset_col, 1) - - # Get the three surrounding matrices. - labels_col = get(io, :labels_col, fill("", 0, n2)) - labels_row = get(io, :labels_row, fill("", m2, 0)) - m1 = size(labels_col, 1) - n1 = size(labels_row, 2) - corner = get(io, :corner, fill("", m1, n1)) -#FIXME: -# If `subset_row`, `subset_col` are given, are `labels_row`, `labels_col` -# to be read relative to the subsets or to `mat`? - - @assert size(corner, 1) == m1 "row numbers in corner and column labels differ" - @assert size(corner, 2) == n1 "column numbers in corner and row labels differ" - @assert size(labels_row, 1) == m2 "row numbers in row labels and matrix differ" - @assert size(labels_col, 2) == n2 "column numbers in column labels and matrix differ" - - m = m1 + m2 - - header = get(io, :header, []) - footer = get(io, :footer, []) - - # Concatenate corner part and row labels. - leftpart = vcat(corner, labels_row) - - # Concatenate column labels and `mat`. - rightpart = vcat(labels_col, mat[subset_row, subset_col]) - - if ! TeX - # If no `TeX` output is required then - # replace markup in the matrices, ... - leftpart = map(replace_TeX, leftpart) - rightpart = map(replace_TeX, rightpart) - - header = map(replace_TeX, header) - footer = map(replace_TeX, footer) - - # ... compute columns widths of the corner entries and row labels ... - leftwidth = map(textwidth, leftpart) - leftrows = [leftwidth[i,:] for i in 1:m] - widths_leftpart = map(max, leftrows...) - - # ... and of the column labels and matrix entries. - rightwidth = map(textwidth, rightpart) - rightrows = [rightwidth[i,:] for i in 1:m] - widths_rightpart = map(max, rightrows...) - - # Compute the width of the row labels part. - leftwidthsum = sum(widths_leftpart) + length(widths_leftpart) - 1 - end - - # Print the header (a vector of strings). - for line in header - println(io, line) - end - - column_separators = get(io, :column_separators, []) - row_separators = map(x -> x+m1, get(io, :row_separators, [])) - - # Determine the column portions. - column_portions = get(io, :column_portions, []) - if column_portions == [] - # Set a default. - if TeX - # Do not split into column portions. - column_portions = [n2] - else - # Distribute the matrix into column portions, - # according to the screen width. - screen_width = displaysize(io)[2] - if leftwidthsum >= screen_width - println(io, "(row label part is too wide for the screen)") - return - end - - num = 0 - width = leftwidthsum - for w in widths_rightpart - width = width + w + 1 - if width >= screen_width - if num == 0 - # This column will be too wide - # but we have to take at least one. - push!(column_portions, 1) - num = 0 - width = leftwidthsum - else - push!(column_portions, num) - num = 1 - width = leftwidthsum + w + 1 - end - else - num = num+1 - end - end - push!(column_portions, num) - end - end - - # We do not distribute the table into row portions - # if this is not wanted. - row_portions = get(io, :row_portions, [m2]) - - # Run over the column portions. - cpos = 0 - lastportion = (column_portions[end], row_portions[end]) - for cnum in column_portions - crange = (cpos + 1):(cpos + cnum) - cpos = cpos + cnum - - if TeX - cpattern = 0 in column_separators ? "|" : "" - for j in crange - cpattern = cpattern * (j in column_separators ? "r|" : "r") - end - else - cprefix = 0 in column_separators ? "\u2502" : " " - cpattern = String[] - for j in crange - push!(cpattern, j in column_separators ? "\u2502" : " ") - end - end - - # For each column portion, run over the row portions. - rpos = 0 - for rnum in row_portions - # Print first the column labels and then the row portion. - rrange = vcat(1:m1, (rpos + m1 + 1):(rpos + m1 + rnum)) - rpos = rpos + rnum - if TeX - println(io, "\\begin{array}{" * repeat("r", n1) * cpattern * "}") - end - - # Compute the horizontal separator line. - if TeX - hline = "\\hline" - else - hline = repeat("\u2500", leftwidthsum) - if 0 in column_separators - hline = hline * "\u253C" - else - hline = hline * "\u2500" - end - for j in crange - hline = hline * repeat("\u2500", widths_rightpart[j]) - if j in column_separators - hline = hline * "\u253C" - else - hline = hline * "\u2500" - end - end - end - - # Print the matrices. - for i in rrange - if TeX - println(io, join(leftpart[i,:], " & "), " & ", join(rightpart[i,:][crange], " & "), " \\\\") - if i in row_separators - println(io, "\\hline") - end - else - println(io, join(map(mylpad, leftpart[i,:], widths_leftpart), " "), - cprefix, - join(join.(collect(zip(map(mylpad, rightpart[i,:][crange], widths_rightpart[crange]), cpattern))))) - if i in row_separators - println(io, hline) - end - end - end - if TeX - println(io, "\\end{array}") - end - if (cnum, rnum) != lastportion - println(io, "") - end - end - end - - # footer (vector of strings) - for line in footer - println(io, line) - end -end - diff --git a/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md b/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md index 5ddf0a023350..c96bf85fb017 100644 --- a/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md +++ b/experimental/DoubleAndHyperComplexes/docs/src/user_interface.md @@ -23,13 +23,13 @@ abstract type `AbsDoubleComplexOfMorphisms`. These functions comprise getindex(D::AbsDoubleComplexOfMorphisms, i::Int, j::Int) # Get the `(i,j)`-th entry of `D` ``` ```@docs - horizontal_map(dc::AbsDoubleComplexOfMorphisms, i::Int, j::Int) - vertical_map(dc::AbsDoubleComplexOfMorphisms, i::Int, j::Int) +horizontal_map(dc::AbsDoubleComplexOfMorphisms, i::Int, j::Int) +vertical_map(dc::AbsDoubleComplexOfMorphisms, i::Int, j::Int) ``` In which direction the maps in the rows and columns go can be asked with the following methods: ```@docs - horizontal_direction(dc::AbsDoubleComplexOfMorphisms) - vertical_direction(dc::AbsDoubleComplexOfMorphisms) +horizontal_direction(dc::AbsDoubleComplexOfMorphisms) +vertical_direction(dc::AbsDoubleComplexOfMorphisms) ``` Double complexes can be bounded or unbounded. It is important to note that even if such bounds exist and are known, this is a priori **not** related to whether or not @@ -50,28 +50,28 @@ various generic functionalities. For example, computing a total complex is only possible in practice if one has an a priori estimate where the non-zero entries are located. For such purposes, we provide the following functionality: ```@docs - has_upper_bound(D::AbsDoubleComplexOfMorphisms) - has_lower_bound(D::AbsDoubleComplexOfMorphisms) - has_right_bound(D::AbsDoubleComplexOfMorphisms) - has_left_bound(D::AbsDoubleComplexOfMorphisms) +has_upper_bound(D::AbsDoubleComplexOfMorphisms) +has_lower_bound(D::AbsDoubleComplexOfMorphisms) +has_right_bound(D::AbsDoubleComplexOfMorphisms) +has_left_bound(D::AbsDoubleComplexOfMorphisms) ``` If they exist, these bounds can be asked for using ```@docs - right_bound(D::AbsDoubleComplexOfMorphisms) - left_bound(D::AbsDoubleComplexOfMorphisms) - upper_bound(D::AbsDoubleComplexOfMorphisms) - lower_bound(D::AbsDoubleComplexOfMorphisms) +right_bound(D::AbsDoubleComplexOfMorphisms) +left_bound(D::AbsDoubleComplexOfMorphisms) +upper_bound(D::AbsDoubleComplexOfMorphisms) +lower_bound(D::AbsDoubleComplexOfMorphisms) ``` It is also possible to query whether or not a double complex is already complete in the sense that it knows about all of its non-zero entries. ```@docs - is_complete(D::AbsDoubleComplexOfMorphisms) +is_complete(D::AbsDoubleComplexOfMorphisms) ``` ## Generic functionality ```@docs - total_complex(D::AbsDoubleComplexOfMorphisms) +total_complex(D::AbsDoubleComplexOfMorphisms) ``` ## Constructors diff --git a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl index e4f93992c305..1b453900bb2b 100644 --- a/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl +++ b/experimental/DoubleAndHyperComplexes/src/Morphisms/simplified_complexes.jl @@ -332,6 +332,14 @@ function can_compute(fac::BaseChangeFromOriginalFactory, phi::AbsHyperComplexMor end +function simplify(c::FreeResolution) + return simplify(SimpleComplexWrapper(c.C)) +end + +function simplify(c::ComplexOfMorphisms) + return simplify(SimpleComplexWrapper(c)) +end + function simplify(c::AbsHyperComplex{ChainType, MorphismType}) where {ChainType, MorphismType} @assert dim(c) == 1 "complex must be one-dimensional" chain_fac = SimplifiedChainFactory(c) diff --git a/experimental/DoubleAndHyperComplexes/test/ext.jl b/experimental/DoubleAndHyperComplexes/test/ext.jl index b326ee0140bc..ecb9d613e869 100644 --- a/experimental/DoubleAndHyperComplexes/test/ext.jl +++ b/experimental/DoubleAndHyperComplexes/test/ext.jl @@ -360,21 +360,7 @@ end I = ideal(S, V); X = variety(I, check = false, is_radical = false); - - function canonical_bundle(X::AbsProjectiveVariety) - SX = homogeneous_coordinate_ring(X) - n = ngens(SX)-1 - c = codim(X) - res, _ = free_resolution(Oscar.SimpleFreeResolution, SX) - C_simp = simplify(res); - C_shift = shift(C_simp, c); - S = base_ring(SX) - OmegaPn = graded_free_module(S, [n+1]) - D = hom(C_shift, OmegaPn); - D_simp = simplify(D); - return homology(D_simp, 0)[1] - end - + @test !is_zero(canonical_bundle(X)) end diff --git a/experimental/ExperimentalTemplate/docs/doc.main b/experimental/ExperimentalTemplate/docs/doc.main index c42ec6a31cee..a6c717e1966c 100644 --- a/experimental/ExperimentalTemplate/docs/doc.main +++ b/experimental/ExperimentalTemplate/docs/doc.main @@ -3,6 +3,6 @@ This is a sample text to outline the structure of the packages in the `Experimental` folder. You can show docstrings like this: ```@docs - my_access_func(S::ExampleStruct) +my_access_func(S::ExampleStruct) ``` - + diff --git a/experimental/FTheoryTools/src/FTheoryTools.jl b/experimental/FTheoryTools/src/FTheoryTools.jl index f0d687ecdeb0..3774867fb0aa 100644 --- a/experimental/FTheoryTools/src/FTheoryTools.jl +++ b/experimental/FTheoryTools/src/FTheoryTools.jl @@ -23,11 +23,12 @@ include("HypersurfaceModels/methods.jl") include("standard_constructions.jl") +include("LiteratureModels/constructors.jl") +include("LiteratureModels/create_index.jl") + include("Serialization/tate_models.jl") include("Serialization/weierstrass_models.jl") include("Serialization/hypersurface_models.jl") - -include("LiteratureModels/constructors.jl") -include("LiteratureModels/create_index.jl") +include("Serialization/qsm_models.jl") include("exports.jl") diff --git a/experimental/FTheoryTools/src/HypersurfaceModels/methods.jl b/experimental/FTheoryTools/src/HypersurfaceModels/methods.jl index c688c6d008a6..e22046c94985 100644 --- a/experimental/FTheoryTools/src/HypersurfaceModels/methods.jl +++ b/experimental/FTheoryTools/src/HypersurfaceModels/methods.jl @@ -83,7 +83,7 @@ function tune(h::HypersurfaceModel, input_sections::Dict{String, <:Any}; complet vars = [string(k) for k in gens(R)] S = cox_ring(ambient_space(h)) images = [k in secs_names ? eval_poly(string(explicit_secs[k]), S) : k == "Kbar" ? eval_poly("0", S) : eval_poly(k, S) for k in vars] - map = hom(R, S, images) + map = hom(R, S, images; check=false) new_hypersurface_equation = map(parametrized_hypersurface_equation) # 3. Build the new model diff --git a/experimental/FTheoryTools/src/LiteratureModels/Models/model1903.00009.json b/experimental/FTheoryTools/src/LiteratureModels/Models/model1903.00009.json new file mode 100644 index 000000000000..974d06d74a0b --- /dev/null +++ b/experimental/FTheoryTools/src/LiteratureModels/Models/model1903.00009.json @@ -0,0 +1,63 @@ +{ + "model_index": "13", + "arxiv_data": { + "id": "1903.00009", + "doi": "10.48550/arXiv.1903.00009", + "version": "3", + "discipline": "hep-th", + "link": "https://arxiv.org/abs/1903.00009v3", + "model_location": { + "section": "II", + "equation": "2", + "page": "2" + } + }, + "journal_data": { + "doi": "10.1103/PhysRevLett.123.101601", + "report_numbers": ["UPR-1297-T"], + "journal": "Physical Review Letters", + "volume": "123", + "pages": "", + "year": "2019", + "link": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.123.101601", + "model_location": { + "section": "", + "equation": "2", + "page": "2" + } + }, + "paper_metadata": { + "authors": [ + "Mirjam Cvetič", + "James Halverson", + "Ling Lin", + "Muyang Liu", + "Jiahua Tian" + ], + "title": "Quadrillion F-theory Compactifications with the Exact Chiral Spectrum of the Standard Model", + "description": "As of 2024, largest class of F-theory Standard Models with gauge coupling unification and no chiral exotics", + "buzzwords": ["Standard model", "Chiral spectrum", "quadrillion"] + }, + "model_descriptors": { + "type": "hypersurface", + "description": "Quadrillion F-theory Standard Model", + "gauge_algebra": ["(su3 x su2 x u1)/Z6"] + }, + "model_parameters": ["k"], + "model_data": { + "base_dim": 3, + "model_sections": ["s1", "s2", "s3", "s5", "s6", "s9"], + "base_coordinates": ["s1", "s2", "s3", "s5", "s6", "s9"], + "auxiliary_base_grading": [ + [1, 1, 1, 1, 1, 1] + ], + "fiber_ambient_space_rays": [[0,-1], [-1,-1], [-1,0], [-1,1], [-1,2], [0,1], [1,0]], + "fiber_ambient_space_max_cones": [[1, 2], [1, 7], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]], + "fiber_ambient_space_coordinates": ["v", "e3", "e2", "u", "e4", "e1", "w"], + "fiber_ambient_space_name": "P^{F_11}", + "fiber_ambient_space_weights": [[1,0,-1,-1,0], [0,0,0,1,0], [0,0,1,-1,0], [1,-1,-1,0,-1], [0,0,0,0,1], [0,1,0,0,-1], [1,-1,0,0,0]], + "D1": [0, 0], + "D2": [0, 0], + "hypersurface_equation": "s1*e1^2*e2^2*e3*e4^4*u^3+s2*e1*e2^2*e3^2*e4^2*u^2*v+s3*e2^2*e3^3*u*v^2+s5*e1^2*e2*e4^3*u^2*w+s6*e1*e2*e3*e4*u*v*w+s9*e1*v*w^2" + } +} diff --git a/experimental/FTheoryTools/src/LiteratureModels/constructors.jl b/experimental/FTheoryTools/src/LiteratureModels/constructors.jl index b2832dd4157c..a48683fae8d8 100644 --- a/experimental/FTheoryTools/src/LiteratureModels/constructors.jl +++ b/experimental/FTheoryTools/src/LiteratureModels/constructors.jl @@ -1,3 +1,97 @@ +####################################################### +# Struct for Quadrillion F-theory Standard Model (QSM) model, i.e. QSM-Model or for short QSMModel. + +struct QSMModel + # Information about the polytope underlying the F-theory QSM. + vertices::Vector{Vector{QQFieldElem}} + poly_index::Int + + # We build toric 3-fold from triangulating the lattice points in said polytope. + # Oftentimes, there are a lot of such triangulations (up to 10^15 for the case at hand), which we cannot + # hope to enumerate in a reasonable time in a computer. The following gives us metadata, to gauge how hard + # this triangulation task is. First, the boolean triang_quick tells if we can hope to enumerate all + # triangulations in a reasonable time. This in turn is linked to the question if we can find + # fine regular triangulations of all facets, the difficulty of which scales primarily with the number of + # lattice points. Hence, we also provide the maximal number of lattice points in a facet of the polytope in question + # in the integer max_lattice_pts_in_facet. On top of this, an estimate for the total number of triangulations + # is provided by the big integer estimated_number_oftriangulations. This estimate is exact if triang_quick = true. + triang_quick::Bool + max_lattice_pts_in_facet::Int + estimated_number_of_triangulations::Int + + # We select one of the many triangulations, construct a 3d toric base B3 and thereby the hypersurface model in question, + # that is then the key object of study of this F-theory construction. + hs_model::HypersurfaceModel + + # As per usual, topological data of this geometry is important. Key is the triple intersection number of the + # anticanonical divisor of the 3-dimensional toric base, as well as its Hodge numbers. + Kbar3::Int + h11::Int + h12::Int + h13::Int + h22::Int + + # Recall that B3 is 3-dimensional toric variety. Let s in H^0(B3, Kbar_B3), then V(s) is a K3-surface. + # Moreover, let xi the coordinates of the Cox ring of B3. Then V(xi) is a divisor in B3. + # Furthermore, Ci = V(xi) cap V(s) is a divisor in the K3-surface V(s). We study these curves Ci in large detail. + # Here is some information about these curves: + genus_ci::Dict{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}, Int} + degree_of_Kbar_of_tv_restricted_to_ci::Dict{MPolyDecRingElem{QQFieldElem, QQMPolyRingElem}, Int} + intersection_number_among_ci_cj::Matrix{Int} + index_facet_interior_divisors::Vector{Int} + intersection_number_among_nontrivial_ci_cj::Matrix{Int} + + # The collection of the Ci form a nodal curve. To every nodal curve one can associate a (dual) graph. In this graph, + # every irreducible component of the nodal curve becomes a node/vertex of the dual graph, and every + # nodal singularity of the nodal curve turns into an edge of the dual graph. Here this is rather simple. + # Every Ci above is an irreducible component of the nodal curve in question and the topological intersection numbers + # among the Ci tell us how many nodal singularities link the Ci. Hence, we construct the dual graph as follows: + # 1. View the Ci as nodes of an undirected graph G. + # 2. If the top. intersection number of Ci and Cj is zero, there is no edge between the nodes of G corresponding to Ci and Cj. + # 3. If the top. intersection number of Ci and Cj is n (> 0), then there are n edges between the nodes of G corresponding to Ci and Cj. + # The following lists the information about this dual graph. + # Currently, we cannot label the nodes/vertices of a OSCAR graph. However, it is important to remember what vertex/node in the + # dual graph corresponds to which geometric locus V(xi, s). Therefore, we keep the labels that link the node of the OSCAR graph + # to the geometric loci V(xi, s) in the vector components_of_dual_graph::Vector{String}. At least for now. + # Should it ever be possible (favorable?) to directly attach these labels to the graph, one can remove components_of_dual_graph. + dual_graph::Graph{Undirected} + components_of_dual_graph::Vector{String} + degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph::Dict{String, Int64} + genus_of_components_of_dual_graph::Dict{String, Int64} + + # In our research, we conduct certain combinatoric computations based on this graph, the data in + # degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph, genus_of_components_of_dual_graph and a bit more meta data + # that is not currently included (yet). These computations are hard. It turns out, that one can replace the graph with another + # graph, so that the computations are easier (a.k.a. the runtimes are a lot shorter). In a nutshell, this means to remove + # a lot of nodes, and adjust the edges accordingly. Let me not go into more details here. A full description can e.g. be found in + # https://arxiv.org/abs/2104.08297 and the follow-up papers thereof. Here we collect the information of said simplified graph, + # by mirroring the strategy for the above dual graph. + simplified_dual_graph::Graph{Undirected} + components_of_simplified_dual_graph::Vector{String} + degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph::Dict{String, Int64} + genus_of_components_of_simplified_dual_graph::Dict{String, Int64} + +end + + +function _parse_rational(str::String) + if occursin("/", str) + # If the string contains a '/', parse as a rational number + parts = split(str, '/') + if length(parts) != 2 + throw(ArgumentError("Invalid rational number format")) + end + numerator = parse(Int, parts[1]) + denominator = parse(Int, parts[2]) + return QQ(numerator//denominator) + else + # If the string doesn't contain a '/', parse as a whole number + return QQ(parse(Int, str)) + end +end + + + ####################################################### # 1. User interface for literature models ####################################################### @@ -124,6 +218,73 @@ Hypersurface model over a concrete base julia> hypersurface_equation_parametrization(h2) b*w*v^2 - c0*u^4 - c1*u^3*v - c2*u^2*v^2 - c3*u*v^3 + w^2 ``` +A yet more special instance of literature model are the F-theory QSMs. Those consist of 708 families of geometries, +each of which is obtained from triangulations of polytopes in the 3-dimensional Kreuzer-Skarke list. In particular, +for each of these 708 families, a lot of information is known. Still, those geometries are also somewhat involved. +Let us demonstrate this on the F-theory QSM based on the 4th polytope in the 3-dimensional Kreuzer-Skarke list. +We can create and study this model as follows (TODO: currently under development): +```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" => 4)); + +julia> model = qsm_model.hs_model +Hypersurface model over a concrete base + +julia> cox_ring(base_space(model)) +Multivariate polynomial ring in 29 variables over QQ graded by + x1 -> [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x2 -> [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x3 -> [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x4 -> [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x5 -> [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x6 -> [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x7 -> [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x8 -> [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + x9 -> [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 0] + x10 -> [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] + x11 -> [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] + x12 -> [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] + x13 -> [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] + x14 -> [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] + x15 -> [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] + x16 -> [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] + x17 -> [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] + x18 -> [0 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] + x19 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0] + x20 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0] + x21 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0] + x22 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0] + x23 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0] + x24 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0] + x25 -> [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0] + x26 -> [1 4 0 1 2 3 1 2 3 4 0 1 1 1 1 1 1 1 1 4 0 2 3 4 4 5] + x27 -> [3 2 2 0 -1 -1 1 0 0 1 1 2 0 1 -1 0 -2 -1 -3 -2 -2 1 1 -1 0 0] + x28 -> [-2 -4 -1 -1 -1 -2 -1 -2 -3 -4 -1 -2 -2 -2 -1 -1 0 0 1 -2 1 -2 -3 -3 -3 -4] + x29 -> [0 -1 0 -1 -1 -1 0 -1 -1 -1 0 0 0 0 0 0 0 0 0 -1 0 -1 -1 -1 -1 -1] + +julia> qsm_model.Kbar3 +6 + +julia> qsm_model.triang_quick +true + +julia> qsm_model.estimated_number_of_triangulations +212533333333 + +julia> qsm_model.dual_graph +Undirected graph with 21 nodes and the following edges: +(5, 1)(6, 5)(7, 6)(8, 7)(9, 4)(9, 8)(10, 1)(11, 4)(12, 3)(12, 10)(13, 3)(13, 11)(14, 1)(15, 4)(16, 3)(17, 3)(18, 2)(18, 14)(19, 2)(19, 15)(20, 2)(20, 16)(21, 2)(21, 17) + +julia> qsm_model.components_of_simplified_dual_graph +4-element Vector{String}: + "C0" + "C1" + "C2" + "C3" + +julia> qsm_model.simplified_dual_graph +Undirected graph with 4 nodes and the following edges: +(2, 1)(3, 1)(3, 2)(4, 1)(4, 2)(4, 3) +``` """ function literature_model(; doi::String="", arxiv_id::String="", version::String="", equation::String="", type::String="", model_parameters::Dict{String,<:Any} = Dict{String,Any}(), base_space::FTheorySpace = affine_space(NormalToricVariety, 0), model_sections::Dict{String, <:Any} = Dict{String,Any}(), defining_classes::Dict{String, <:Any} = Dict{String,Any}(), completeness_check::Bool = true) model_dict = _find_model(doi, arxiv_id, version, equation, type) @@ -155,8 +316,27 @@ function literature_model(model_dict::Dict{String, Any}; model_parameters::Dict{ end end - - # (2a) Construct the model over concrete base + # (2) The QSM need special treatment... + if model_dict["arxiv_data"]["id"] == "1903.00009" + + # Remember how we identified this model + model_dict["literature_identifier"] = "1903.00009" + k = model_parameters["k"] + qsmd_path = artifact"QSMDB" + qsm_model = load(joinpath(qsmd_path, "$k.mrdi")) + # TODO: Antony, please notice that the serialization of hs_model (as well as Tate and Weierstrass_model) involves a + # a hack to serialize a dicts. Maybe this can be fixed in this PR? Maybe even must? + + # TODO: Also notice, that the serialization of hs_model will forget all of the meta_data that is set by the command + # _set_all_attributes(hs_model, model_dict, model_parameters) + # So also this needs addressing? + + # Finally, return the QSMModel in question... + return qsm_model + end + + + # (3) Construct the model over concrete or arbitrary base if dim(base_space) > 0 # Currently, support only for toric bases @@ -180,13 +360,12 @@ function literature_model(model_dict::Dict{String, Any}; model_parameters::Dict{ model = _construct_literature_model_over_concrete_base(model_dict, base_space, defining_classes_provided, completeness_check) @vprint :FTheoryModelPrinter 0 "Construction over concrete base may lead to singularity enhancement. Consider computing singular_loci. However, this may take time!\n\n" - # (2b) Construct the model over arbitrary base else model = _construct_literature_model_over_arbitrary_base(model_dict) end - # (3) Return the model after we set all required attributes + # (4) Return the model after we set all required attributes _set_all_attributes(model, model_dict, model_parameters) return model end diff --git a/experimental/FTheoryTools/src/LiteratureModels/index.json b/experimental/FTheoryTools/src/LiteratureModels/index.json index befc19a6b76d..d7d40778f909 100644 --- a/experimental/FTheoryTools/src/LiteratureModels/index.json +++ b/experimental/FTheoryTools/src/LiteratureModels/index.json @@ -1 +1 @@ -[{"journal_section":"3","arxiv_page":"10","journal_page":"9","arxiv_id":"1109.3454","arxiv_equation":"3.1","arxiv_version":"2","journal_doi":"10.1016/j.nuclphysb.2011.12.013","journal_equation":"3.1","arxiv_section":"3","journal":"Nucl. Phys. B","file":"model1109_3454.json","arxiv_doi":"10.48550/arXiv.1109.3454","model_index":"1","type":"tate"},{"journal_section":"B","arxiv_page":"32","journal_page":"33","arxiv_id":"1208.2695","arxiv_equation":"B.5","arxiv_version":"2","journal_doi":"10.1007/JHEP10(2012)128","journal_equation":"B.5","arxiv_section":"B","journal":"JHEP","file":"model1208_2695-1.json","arxiv_doi":"10.48550/arXiv.1208.2695","model_index":"2","type":"hypersurface"},{"journal_section":"B","arxiv_page":"34","journal_page":"34","arxiv_id":"1208.2695","arxiv_equation":"B.19","arxiv_version":"2","journal_doi":"10.1007/JHEP10(2012)128","journal_equation":"B.19","arxiv_section":"B","journal":"JHEP","file":"model1208_2695-2.json","arxiv_doi":"10.48550/arXiv.1208.2695","model_index":"3","type":"weierstrass"},{"journal_section":"","arxiv_page":"19","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"3.2","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"3.1","journal":"JHEP","file":"model1212_2949-1.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"4","type":"tate"},{"journal_section":"","arxiv_page":"31","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"3.42","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"3.2","journal":"JHEP","file":"model1212_2949-2.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"5","type":"tate"},{"journal_section":"","arxiv_page":"37","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"4.1","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"4.1","journal":"JHEP","file":"model1212_2949-3.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"6","type":"tate"},{"journal_section":"","arxiv_page":"44","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"4.23","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"4.2","journal":"JHEP","file":"model1212_2949-4.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"7","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.1","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-5.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"8","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.7","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-6.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"9","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.13","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-7.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"10","type":"tate"},{"journal_section":"3","arxiv_page":"18","journal_page":"19","arxiv_id":"1408.4808v2","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.4","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-1-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"13","type":"weierstrass"},{"journal_section":"3","arxiv_page":"18","journal_page":"19","arxiv_id":"1408.4808v2","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.4","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-1.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"14","type":"hypersurface"},{"journal_section":"3","arxiv_page":"62","journal_page":"62","arxiv_id":"1408.4808v2","arxiv_equation":"3.130","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.129","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-10-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"31","type":"weierstrass"},{"journal_section":"3","arxiv_page":"62","journal_page":"62","arxiv_id":"1408.4808v2","arxiv_equation":"3.130","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.129","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-10.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"32","type":"hypersurface"},{"journal_section":"3","arxiv_page":"67","journal_page":"67","arxiv_id":"1408.4808v2","arxiv_equation":"3.142","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.141","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-11-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"33","type":"weierstrass"},{"journal_section":"3","arxiv_page":"67","journal_page":"67","arxiv_id":"1408.4808v2","arxiv_equation":"3.142","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.141","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-11.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"34","type":"hypersurface"},{"journal_section":"3","arxiv_page":"70","journal_page":"70","arxiv_id":"1408.4808v2","arxiv_equation":"3.155","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.154","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-12-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"35","type":"weierstrass"},{"journal_section":"3","arxiv_page":"70","journal_page":"70","arxiv_id":"1408.4808v2","arxiv_equation":"3.155","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.154","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-12.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"36","type":"hypersurface"},{"journal_section":"3","arxiv_page":"79","journal_page":"79","arxiv_id":"1408.4808v2","arxiv_equation":"3.181","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.180","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-13-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"37","type":"weierstrass"},{"journal_section":"3","arxiv_page":"79","journal_page":"79","arxiv_id":"1408.4808v2","arxiv_equation":"3.181","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.180","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-13.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"38","type":"hypersurface"},{"journal_section":"3","arxiv_page":"75","journal_page":"75","arxiv_id":"1408.4808v2","arxiv_equation":"3.168","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.167","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-14-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"39","type":"weierstrass"},{"journal_section":"3","arxiv_page":"75","journal_page":"75","arxiv_id":"1408.4808v2","arxiv_equation":"3.168","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.167","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-14.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"40","type":"hypersurface"},{"journal_section":"3","arxiv_page":"83","journal_page":"83","arxiv_id":"1408.4808v2","arxiv_equation":"3.190","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.189","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-15-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"41","type":"weierstrass"},{"journal_section":"3","arxiv_page":"83","journal_page":"83","arxiv_id":"1408.4808v2","arxiv_equation":"3.190","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.189","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-15.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"42","type":"hypersurface"},{"journal_section":"3","arxiv_page":"87","journal_page":"88","arxiv_id":"1408.4808v2","arxiv_equation":"3.203","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.202","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-16-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"43","type":"weierstrass"},{"journal_section":"3","arxiv_page":"87","journal_page":"88","arxiv_id":"1408.4808v2","arxiv_equation":"3.203","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.202","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-16.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"44","type":"hypersurface"},{"journal_section":"3","arxiv_page":"20","journal_page":"20","arxiv_id":"1408.4808v2","arxiv_equation":"3.12","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.12","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-2-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"15","type":"weierstrass"},{"journal_section":"3","arxiv_page":"20","journal_page":"20","arxiv_id":"1408.4808v2","arxiv_equation":"3.12","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.12","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-2.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"16","type":"hypersurface"},{"journal_section":"3","arxiv_page":"36","journal_page":"36","arxiv_id":"1408.4808v2","arxiv_equation":"3.54","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.53","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-3-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"17","type":"weierstrass"},{"journal_section":"3","arxiv_page":"36","journal_page":"36","arxiv_id":"1408.4808v2","arxiv_equation":"3.54","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.53","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-3.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"18","type":"hypersurface"},{"journal_section":"3","arxiv_page":"22","journal_page":"22","arxiv_id":"1408.4808v2","arxiv_equation":"3.17","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.17","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-4-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"19","type":"weierstrass"},{"journal_section":"3","arxiv_page":"22","journal_page":"22","arxiv_id":"1408.4808v2","arxiv_equation":"3.17","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.17","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-4.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"20","type":"hypersurface"},{"journal_section":"3","arxiv_page":"43","journal_page":"43","arxiv_id":"1408.4808v2","arxiv_equation":"3.73","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.72","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-5-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"21","type":"weierstrass"},{"journal_section":"3","arxiv_page":"43","journal_page":"43","arxiv_id":"1408.4808v2","arxiv_equation":"3.73","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.72","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-5.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"22","type":"hypersurface"},{"journal_section":"3","arxiv_page":"46","journal_page":"46","arxiv_id":"1408.4808v2","arxiv_equation":"3.82","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.81","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-6-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"23","type":"weierstrass"},{"journal_section":"3","arxiv_page":"46","journal_page":"46","arxiv_id":"1408.4808v2","arxiv_equation":"3.82","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.81","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-6.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"24","type":"hypersurface"},{"journal_section":"3","arxiv_page":"50","journal_page":"51","arxiv_id":"1408.4808v2","arxiv_equation":"3.96","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.95","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-7-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"25","type":"weierstrass"},{"journal_section":"3","arxiv_page":"50","journal_page":"51","arxiv_id":"1408.4808v2","arxiv_equation":"3.96","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.95","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-7.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"26","type":"hypersurface"},{"journal_section":"3","arxiv_page":"54","journal_page":"55","arxiv_id":"1408.4808v2","arxiv_equation":"3.106","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.105","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-8-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"27","type":"weierstrass"},{"journal_section":"3","arxiv_page":"54","journal_page":"55","arxiv_id":"1408.4808v2","arxiv_equation":"3.106","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.105","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-8.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"28","type":"hypersurface"},{"journal_section":"3","arxiv_page":"58","journal_page":"59","arxiv_id":"1408.4808v2","arxiv_equation":"3.118","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.117","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-9-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"29","type":"weierstrass"},{"journal_section":"3","arxiv_page":"58","journal_page":"59","arxiv_id":"1408.4808v2","arxiv_equation":"3.118","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.117","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-9.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"30","type":"hypersurface"},{"journal_section":"","arxiv_page":"17","journal_page":"","arxiv_id":"1507.05954","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP11(2015)204","journal_equation":"","arxiv_section":"3","journal":"JHEP","file":"model1507_05954-1.json","arxiv_doi":"10.48550/arXiv.1507.05954","model_index":"11","type":"hypersurface"},{"journal_section":"","arxiv_page":"71","journal_page":"","arxiv_id":"1507.05954","arxiv_equation":"A.1","arxiv_version":"2","journal_doi":"10.1007/JHEP11(2015)204","journal_equation":"","arxiv_section":"A","journal":"JHEP","file":"model1507_05954-2.json","arxiv_doi":"10.48550/arXiv.1507.05954","model_index":"12","type":"weierstrass"}] \ No newline at end of file +[{"journal_section":"3","arxiv_page":"10","journal_page":"9","arxiv_id":"1109.3454","arxiv_equation":"3.1","arxiv_version":"2","journal_doi":"10.1016/j.nuclphysb.2011.12.013","journal_equation":"3.1","arxiv_section":"3","journal":"Nucl. Phys. B","file":"model1109_3454.json","arxiv_doi":"10.48550/arXiv.1109.3454","model_index":"1","type":"tate"},{"journal_section":"B","arxiv_page":"32","journal_page":"33","arxiv_id":"1208.2695","arxiv_equation":"B.5","arxiv_version":"2","journal_doi":"10.1007/JHEP10(2012)128","journal_equation":"B.5","arxiv_section":"B","journal":"JHEP","file":"model1208_2695-1.json","arxiv_doi":"10.48550/arXiv.1208.2695","model_index":"2","type":"hypersurface"},{"journal_section":"B","arxiv_page":"34","journal_page":"34","arxiv_id":"1208.2695","arxiv_equation":"B.19","arxiv_version":"2","journal_doi":"10.1007/JHEP10(2012)128","journal_equation":"B.19","arxiv_section":"B","journal":"JHEP","file":"model1208_2695-2.json","arxiv_doi":"10.48550/arXiv.1208.2695","model_index":"3","type":"weierstrass"},{"journal_section":"","arxiv_page":"19","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"3.2","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"3.1","journal":"JHEP","file":"model1212_2949-1.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"4","type":"tate"},{"journal_section":"","arxiv_page":"31","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"3.42","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"3.2","journal":"JHEP","file":"model1212_2949-2.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"5","type":"tate"},{"journal_section":"","arxiv_page":"37","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"4.1","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"4.1","journal":"JHEP","file":"model1212_2949-3.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"6","type":"tate"},{"journal_section":"","arxiv_page":"44","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"4.23","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"4.2","journal":"JHEP","file":"model1212_2949-4.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"7","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.1","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-5.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"8","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.7","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-6.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"9","type":"tate"},{"journal_section":"","arxiv_page":"49","journal_page":"","arxiv_id":"1212.2949","arxiv_equation":"5.13","arxiv_version":"2","journal_doi":"10.1007/JHEP04(2013)061","journal_equation":"","arxiv_section":"5.1","journal":"JHEP","file":"model1212_2949-7.json","arxiv_doi":"10.48550/arXiv.1212.2949","model_index":"10","type":"tate"},{"journal_section":"3","arxiv_page":"18","journal_page":"19","arxiv_id":"1408.4808v2","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.4","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-1-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"13","type":"weierstrass"},{"journal_section":"3","arxiv_page":"18","journal_page":"19","arxiv_id":"1408.4808v2","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.4","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-1.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"14","type":"hypersurface"},{"journal_section":"3","arxiv_page":"62","journal_page":"62","arxiv_id":"1408.4808v2","arxiv_equation":"3.130","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.129","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-10-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"31","type":"weierstrass"},{"journal_section":"3","arxiv_page":"62","journal_page":"62","arxiv_id":"1408.4808v2","arxiv_equation":"3.130","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.129","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-10.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"32","type":"hypersurface"},{"journal_section":"3","arxiv_page":"67","journal_page":"67","arxiv_id":"1408.4808v2","arxiv_equation":"3.142","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.141","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-11-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"33","type":"weierstrass"},{"journal_section":"3","arxiv_page":"67","journal_page":"67","arxiv_id":"1408.4808v2","arxiv_equation":"3.142","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.141","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-11.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"34","type":"hypersurface"},{"journal_section":"3","arxiv_page":"70","journal_page":"70","arxiv_id":"1408.4808v2","arxiv_equation":"3.155","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.154","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-12-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"35","type":"weierstrass"},{"journal_section":"3","arxiv_page":"70","journal_page":"70","arxiv_id":"1408.4808v2","arxiv_equation":"3.155","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.154","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-12.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"36","type":"hypersurface"},{"journal_section":"3","arxiv_page":"79","journal_page":"79","arxiv_id":"1408.4808v2","arxiv_equation":"3.181","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.180","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-13-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"37","type":"weierstrass"},{"journal_section":"3","arxiv_page":"79","journal_page":"79","arxiv_id":"1408.4808v2","arxiv_equation":"3.181","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.180","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-13.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"38","type":"hypersurface"},{"journal_section":"3","arxiv_page":"75","journal_page":"75","arxiv_id":"1408.4808v2","arxiv_equation":"3.168","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.167","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-14-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"39","type":"weierstrass"},{"journal_section":"3","arxiv_page":"75","journal_page":"75","arxiv_id":"1408.4808v2","arxiv_equation":"3.168","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.167","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-14.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"40","type":"hypersurface"},{"journal_section":"3","arxiv_page":"83","journal_page":"83","arxiv_id":"1408.4808v2","arxiv_equation":"3.190","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.189","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-15-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"41","type":"weierstrass"},{"journal_section":"3","arxiv_page":"83","journal_page":"83","arxiv_id":"1408.4808v2","arxiv_equation":"3.190","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.189","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-15.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"42","type":"hypersurface"},{"journal_section":"3","arxiv_page":"87","journal_page":"88","arxiv_id":"1408.4808v2","arxiv_equation":"3.203","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.202","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-16-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"43","type":"weierstrass"},{"journal_section":"3","arxiv_page":"87","journal_page":"88","arxiv_id":"1408.4808v2","arxiv_equation":"3.203","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.202","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-16.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"44","type":"hypersurface"},{"journal_section":"3","arxiv_page":"20","journal_page":"20","arxiv_id":"1408.4808v2","arxiv_equation":"3.12","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.12","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-2-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"15","type":"weierstrass"},{"journal_section":"3","arxiv_page":"20","journal_page":"20","arxiv_id":"1408.4808v2","arxiv_equation":"3.12","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.12","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-2.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"16","type":"hypersurface"},{"journal_section":"3","arxiv_page":"36","journal_page":"36","arxiv_id":"1408.4808v2","arxiv_equation":"3.54","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.53","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-3-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"17","type":"weierstrass"},{"journal_section":"3","arxiv_page":"36","journal_page":"36","arxiv_id":"1408.4808v2","arxiv_equation":"3.54","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.53","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-3.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"18","type":"hypersurface"},{"journal_section":"3","arxiv_page":"22","journal_page":"22","arxiv_id":"1408.4808v2","arxiv_equation":"3.17","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.17","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-4-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"19","type":"weierstrass"},{"journal_section":"3","arxiv_page":"22","journal_page":"22","arxiv_id":"1408.4808v2","arxiv_equation":"3.17","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.17","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-4.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"20","type":"hypersurface"},{"journal_section":"3","arxiv_page":"43","journal_page":"43","arxiv_id":"1408.4808v2","arxiv_equation":"3.73","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.72","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-5-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"21","type":"weierstrass"},{"journal_section":"3","arxiv_page":"43","journal_page":"43","arxiv_id":"1408.4808v2","arxiv_equation":"3.73","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.72","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-5.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"22","type":"hypersurface"},{"journal_section":"3","arxiv_page":"46","journal_page":"46","arxiv_id":"1408.4808v2","arxiv_equation":"3.82","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.81","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-6-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"23","type":"weierstrass"},{"journal_section":"3","arxiv_page":"46","journal_page":"46","arxiv_id":"1408.4808v2","arxiv_equation":"3.82","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.81","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-6.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"24","type":"hypersurface"},{"journal_section":"3","arxiv_page":"50","journal_page":"51","arxiv_id":"1408.4808v2","arxiv_equation":"3.96","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.95","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-7-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"25","type":"weierstrass"},{"journal_section":"3","arxiv_page":"50","journal_page":"51","arxiv_id":"1408.4808v2","arxiv_equation":"3.96","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.95","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-7.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"26","type":"hypersurface"},{"journal_section":"3","arxiv_page":"54","journal_page":"55","arxiv_id":"1408.4808v2","arxiv_equation":"3.106","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.105","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-8-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"27","type":"weierstrass"},{"journal_section":"3","arxiv_page":"54","journal_page":"55","arxiv_id":"1408.4808v2","arxiv_equation":"3.106","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.105","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-8.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"28","type":"hypersurface"},{"journal_section":"3","arxiv_page":"58","journal_page":"59","arxiv_id":"1408.4808v2","arxiv_equation":"3.118","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.117","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-9-WSF.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"29","type":"weierstrass"},{"journal_section":"3","arxiv_page":"58","journal_page":"59","arxiv_id":"1408.4808v2","arxiv_equation":"3.118","arxiv_version":"2","journal_doi":"10.1007/JHEP01(2015)142","journal_equation":"3.117","arxiv_section":"3","journal":"JHEP","file":"model1408_4808-9.json","arxiv_doi":"10.48550/arXiv.1408.4808","model_index":"30","type":"hypersurface"},{"journal_section":"","arxiv_page":"17","journal_page":"","arxiv_id":"1507.05954","arxiv_equation":"3.4","arxiv_version":"2","journal_doi":"10.1007/JHEP11(2015)204","journal_equation":"","arxiv_section":"3","journal":"JHEP","file":"model1507_05954-1.json","arxiv_doi":"10.48550/arXiv.1507.05954","model_index":"11","type":"hypersurface"},{"journal_section":"","arxiv_page":"71","journal_page":"","arxiv_id":"1507.05954","arxiv_equation":"A.1","arxiv_version":"2","journal_doi":"10.1007/JHEP11(2015)204","journal_equation":"","arxiv_section":"A","journal":"JHEP","file":"model1507_05954-2.json","arxiv_doi":"10.48550/arXiv.1507.05954","model_index":"12","type":"weierstrass"},{"journal_section":"","arxiv_page":"2","journal_page":"2","arxiv_id":"1903.00009","arxiv_equation":"2","arxiv_version":"3","journal_doi":"10.1103/PhysRevLett.123.101601","journal_equation":"2","arxiv_section":"II","journal":"Physical Review Letters","file":"model1903.00009.json","arxiv_doi":"10.48550/arXiv.1903.00009","model_index":"13","type":"hypersurface"}] \ No newline at end of file diff --git a/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl b/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl index e8c196544959..f4035e5d3f96 100644 --- a/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl +++ b/experimental/FTheoryTools/src/Serialization/hypersurface_models.jl @@ -21,48 +21,12 @@ function save_type_params(s::SerializerState, h::HypersurfaceModel) end save_data_dict(s, :params) do - if serialize_with_id(base) - parent_ref = save_as_ref(s, base) - save_object(s, parent_ref, :base_space) - else - save_typed_object(s, base, :base_space) - end - - if serialize_with_id(ambient) - parent_ref = save_as_ref(s, ambient) - save_object(s, parent_ref, :ambient_space) - else - save_typed_object(s, ambient, :ambient_space) - end - - if serialize_with_id(fiber_amb_space) - parent_ref = save_as_ref(s, fiber_amb_space) - save_object(s, parent_ref, :fiber_ambient_space) - else - save_typed_object(s, fiber_amb_space, :fiber_ambient_space) - end - - if serialize_with_id(hypersurface_equation_ring) - parent_ref = save_as_ref(s, hypersurface_equation_ring) - save_object(s, parent_ref, :hypersurface_equation_ring) - else - save_typed_object(s, hypersurface_equation_ring, :hypersurface_equation_ring) - end - - if serialize_with_id(hypersurface_equation_parametrization_ring) - parent_ref = save_as_ref(s, hypersurface_equation_parametrization_ring) - save_object(s, parent_ref, :hypersurface_equation_parametrization_ring) - else - save_typed_object(s, hypersurface_equation_parametrization_ring, :hypersurface_equation_parametrization_ring) - end - - if serialize_with_id(explicit_model_section_ring) - parent_ref = save_as_ref(s, explicit_model_section_ring) - save_object(s, parent_ref, :explicit_model_section_ring) - else - save_typed_object(s, explicit_model_section_ring, :explicit_model_section_ring) - end - + save_typed_object(s, base, :base_space) + save_typed_object(s, ambient, :ambient_space) + save_typed_object(s, fiber_amb_space, :fiber_ambient_space) + save_typed_object(s, hypersurface_equation_ring, :hypersurface_equation_ring) + save_typed_object(s, hypersurface_equation_parametrization_ring, :hypersurface_equation_parametrization_ring) + save_typed_object(s, explicit_model_section_ring, :explicit_model_section_ring) end end end diff --git a/experimental/FTheoryTools/src/Serialization/qsm_models.jl b/experimental/FTheoryTools/src/Serialization/qsm_models.jl new file mode 100644 index 000000000000..e9e2bb532752 --- /dev/null +++ b/experimental/FTheoryTools/src/Serialization/qsm_models.jl @@ -0,0 +1,105 @@ +@register_serialization_type QSMModel + +function save_object(s::SerializerState, qsm::QSMModel) + save_data_dict(s) do + save_object(s, qsm.vertices, :vertices) + save_object(s, qsm.poly_index, :poly_inx) + + save_object(s, qsm.triang_quick, :triang_quick) + save_object(s, qsm.max_lattice_pts_in_facet, :max_lattice_pts_in_facet) + save_object(s, qsm.estimated_number_of_triangulations, :estimated_number_of_triangulations) + save_typed_object(s, qsm.hs_model, :hs_model) + + save_object(s, qsm.Kbar3, :Kbar3) + save_object(s, qsm.h11, :h11) + save_object(s, qsm.h12, :h12) + save_object(s, qsm.h13, :h13) + save_object(s, qsm.h22, :h22) + + save_typed_object(s, qsm.genus_ci, :genus_ci) + save_typed_object(s, qsm.degree_of_Kbar_of_tv_restricted_to_ci, :degree_of_Kbar_of_tv_restricted_to_ci) + save_object(s, qsm.intersection_number_among_ci_cj, :intersection_number_among_ci_cj) + save_object(s, qsm.index_facet_interior_divisors, :index_facet_interior_divisors) + save_object(s, qsm.intersection_number_among_nontrivial_ci_cj, :intersection_number_among_nontrivial_ci_cj) + + save_object(s, qsm.dual_graph, :dual_graph) + save_object(s, qsm.components_of_dual_graph, :components_of_dual_graph) + save_object(s, qsm.degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph, :degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph) + save_object(s, qsm.genus_of_components_of_dual_graph, :genus_of_components_of_dual_graph) + + save_object(s, qsm.simplified_dual_graph, :simplified_dual_graph) + save_object(s, qsm.components_of_simplified_dual_graph, :components_of_simplified_dual_graph) + save_object(s, qsm.degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph, :degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph) + save_object(s, qsm.genus_of_components_of_simplified_dual_graph, :genus_of_components_of_simplified_dual_graph) + end +end + +function load_object(s::DeserializerState, ::Type{QSMModel}) + vertices = load_object(s, Vector{Vector{QQFieldElem}}, :vertices) + poly_index = load_object(s, Int, :poly_inx) + triang_quick = load_object(s, Bool, :triang_quick) + max_lattice_pts_in_facet = load_object(s, Int, :max_lattice_pts_in_facet) + estimated_number_of_triangulations = load_object(s, Int, :estimated_number_of_triangulations) + hs_model = load_typed_object(s, :hs_model) + + Kbar3 = load_object(s, Int, :Kbar3) + h11 = load_object(s, Int, :h11) + h12 = load_object(s, Int, :h12) + h13 = load_object(s, Int, :h13) + h22 = load_object(s, Int, :h22) + + genus_ci = load_typed_object(s, :genus_ci) + degree_of_Kbar_of_tv_restricted_to_ci = load_typed_object(s, :degree_of_Kbar_of_tv_restricted_to_ci) + intersection_number_among_ci_cj = load_object(s, Matrix, Int, :intersection_number_among_ci_cj) + index_facet_interior_divisors = load_object(s, Vector{Int}, :index_facet_interior_divisors) + intersection_number_among_nontrivial_ci_cj = load_object(s, Matrix, Int, :intersection_number_among_nontrivial_ci_cj) + + dual_graph = load_object(s, Graph{Undirected}, :dual_graph) + components_of_dual_graph = load_object(s, Vector{String}, :components_of_dual_graph) + degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph = load_object( + s, Dict{String, Int64}, :degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph) + genus_of_components_of_dual_graph = load_object(s, Dict{String, Int64}, :genus_of_components_of_dual_graph) + + simplified_dual_graph = load_object(s, Graph{Undirected}, :simplified_dual_graph) + components_of_simplified_dual_graph = load_object(s, Vector{String}, :components_of_simplified_dual_graph) + + degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph = load_object( + s, Dict{String, Int}, + :degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph) + + genus_of_components_of_simplified_dual_graph = load_object( + s, Dict{String, Int}, + :genus_of_components_of_simplified_dual_graph + ) + + h = hypersurface_equation(hs_model) + S = cox_ring(ambient_space(hs_model)) + var_names = symbols(parent(h)) + S.R.S = var_names + hs_model.hypersurface_equation = Oscar.eval_poly(string(h), S) + + return QSMModel(vertices, + poly_index, + triang_quick, + max_lattice_pts_in_facet, + estimated_number_of_triangulations, + hs_model, + Kbar3, + h11, + h12, + h13, + h22, + genus_ci, + degree_of_Kbar_of_tv_restricted_to_ci, + intersection_number_among_ci_cj, + index_facet_interior_divisors, + intersection_number_among_nontrivial_ci_cj, + dual_graph, + components_of_dual_graph, + degree_of_Kbar_of_tv_restricted_to_components_of_dual_graph, + genus_of_components_of_dual_graph, + simplified_dual_graph, + components_of_simplified_dual_graph, + degree_of_Kbar_of_tv_restricted_to_components_of_simplified_dual_graph, + genus_of_components_of_simplified_dual_graph) +end diff --git a/experimental/FTheoryTools/src/exports.jl b/experimental/FTheoryTools/src/exports.jl index 3a75bd92d815..c8a7331a62d2 100644 --- a/experimental/FTheoryTools/src/exports.jl +++ b/experimental/FTheoryTools/src/exports.jl @@ -105,6 +105,7 @@ export paper_buzzwords export paper_description export paper_title export put_over_concrete_base +export QSMModel export related_literature_models export resolution_generating_sections export resolution_zero_sections diff --git a/experimental/GModule/Brueckner.jl b/experimental/GModule/Brueckner.jl index 0e2ca28bb2a4..fbe502d4b0bb 100644 --- a/experimental/GModule/Brueckner.jl +++ b/experimental/GModule/Brueckner.jl @@ -57,7 +57,7 @@ function reps(K, G::Oscar.GAPGroup) r = R[pos] F = r.M @assert group(r) == s - rh = gmodule(group(r), [action(r, preimage(ms, x^h)) for x = gens(s)]) + rh = gmodule(group(r), [action(r, preimage(ms, ms(x)^h)) for x = gens(s)]) @hassert :BruecknerSQ 2 Oscar.GrpCoh.is_consistent(rh) l = Oscar.GModuleFromGap.hom_base(r, rh) @assert length(l) <= 1 diff --git a/experimental/GModule/Cohomology.jl b/experimental/GModule/Cohomology.jl index 77e5bf91e9a3..b9372098fc51 100644 --- a/experimental/GModule/Cohomology.jl +++ b/experimental/GModule/Cohomology.jl @@ -1750,7 +1750,7 @@ end ########################################################### #= -function get_collector(G::GAP.GapObj) +function get_collector(G::GapObj) @show G return GAP.evalstr("x -> FamilyObj(x.1)!.rewritingSystem")(G) end @@ -1836,7 +1836,7 @@ function pc_group_with_isomorphism(M::FinGenAbGroup; refine::Bool = true) return GAP.Globals.ObjByExtRep(FB, GAP.Obj(r, recursive = true)) end - gap_to_julia = function(a::GAP.GapObj) + gap_to_julia = function(a::GapObj) e = GAPWrap.ExtRepOfObj(a) z = zeros(ZZRingElem, ngens(M)) for i=1:2:length(e) @@ -1905,7 +1905,7 @@ function pc_group_with_isomorphism(M::AbstractAlgebra.FPModule{<:FinFieldElem}; end - gap_to_julia = function(a::GAP.GapObj) + gap_to_julia = function(a::GapObj) e = GAPWrap.ExtRepOfObj(a) z = zeros(ZZRingElem, ngens(M)*degree(k)) for i=1:2:length(e) diff --git a/experimental/GModule/GModule.jl b/experimental/GModule/GModule.jl index 34450024d1ff..86f500660457 100644 --- a/experimental/GModule/GModule.jl +++ b/experimental/GModule/GModule.jl @@ -612,7 +612,7 @@ function Oscar.natural_character(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<: vals = [GAP.Globals.BrauerCharacterValue(GAP.Obj(map(h, matrix(action(C, representative(x)))))) for x in ccl] - return Oscar.class_function(modtbl, GAPWrap.ClassFunction(Oscar.GAPTable(modtbl), Oscar.GapObj(vals))) + return Oscar.class_function(modtbl, GAPWrap.ClassFunction(GapObj(modtbl), GapObj(vals))) end function Oscar.sub(C::GModule{<:Any, <:AbstractAlgebra.FPModule{T}}, m::MatElem{T}) where {T <: FinFieldElem} @@ -1528,7 +1528,7 @@ function Oscar.gmodule(::Type{FinGenAbGroup}, C::GModule{T, <:AbstractAlgebra.FP end function Oscar.gmodule(chi::Oscar.GAPGroupClassFunction) - f = GAP.Globals.IrreducibleAffordingRepresentation(chi.values) + f = GAP.Globals.IrreducibleAffordingRepresentation(GapObj(chi)) K = abelian_closure(QQ)[1] g = GAP.Globals.List(GAP.Globals.GeneratorsOfGroup(GapObj(group(chi))), f) z = map(x->matrix(map(y->map(K, y), g[x])), 1:GAP.Globals.Size(g)) diff --git a/experimental/GModule/Misc.jl b/experimental/GModule/Misc.jl index 9bc37f9b1bb3..43f48e04d47b 100644 --- a/experimental/GModule/Misc.jl +++ b/experimental/GModule/Misc.jl @@ -4,7 +4,6 @@ import Base: ==, parent export relative_field -Hecke.minpoly(a::QQBarFieldElem) = minpoly(Hecke.Globals.Qx, a) function primitive_element(a::Vector{QQBarFieldElem}) pe = a[1] @@ -73,12 +72,6 @@ end Base.getindex(::QQField, a::QQBarFieldElem) = number_field(QQ, a) Base.getindex(::QQField, a::Vector{QQBarFieldElem}) = number_field(QQ, a) -function Hecke.numerator(f::QQPolyRingElem, parent::ZZPolyRing = Hecke.Globals.Zx) - g = parent() - ccall((:fmpq_poly_get_numerator, Nemo.libflint), Cvoid, (Ref{ZZPolyRingElem}, Ref{QQPolyRingElem}), g, f) - return g -end - function cyclo_fixed_group_gens(a::AbsSimpleNumFieldElem) C = parent(a) fl, f = Hecke.is_cyclotomic_type(C) diff --git a/experimental/IntersectionTheory/src/IntersectionTheory.jl b/experimental/IntersectionTheory/src/IntersectionTheory.jl index 562acf03edf3..15641420a476 100644 --- a/experimental/IntersectionTheory/src/IntersectionTheory.jl +++ b/experimental/IntersectionTheory/src/IntersectionTheory.jl @@ -3,7 +3,7 @@ using ..Oscar import Base: +, -, *, ^, ==, div, zero, one, parent import ..Oscar: AffAlgHom, Ring, MPolyDecRingElem, symmetric_power, exterior_power, pullback, canonical_bundle, graph, euler_characteristic, pullback -import ..Oscar: basis, betti, codomain, degree, det, dim, domain, dual, gens, hilbert_polynomial, hom, integral, rank, signature +import ..Oscar: basis, betti, codomain, degree, det, dim, domain, dual, gens, hilbert_polynomial, hom, integral, rank, signature, partitions import ..Oscar.AbstractAlgebra: combinations import ..Oscar.AbstractAlgebra.Generic: FunctionalMap diff --git a/experimental/IntersectionTheory/src/Main.jl b/experimental/IntersectionTheory/src/Main.jl index 6d374c6a76b7..244ddceeec91 100644 --- a/experimental/IntersectionTheory/src/Main.jl +++ b/experimental/IntersectionTheory/src/Main.jl @@ -1281,5 +1281,9 @@ Return all the Schubert classes in codimension $m$ on a (relative) Grassmannian function schubert_classes(G::AbstractVariety, m::Int) get_attribute(G, :grassmannian) === nothing && error("the abstract_variety is not a Grassmannian") S, Q = G.bundles - [schubert_class(G, λ) for λ in partitions(m, rank(S), rank(Q))] + res = elem_type(G.ring)[] + for i in 0:rank(S) + append!(res, [schubert_class(G, l) for l in partitions(m, i, 1, rank(Q))]) + end + return res end diff --git a/experimental/IntersectionTheory/src/Misc.jl b/experimental/IntersectionTheory/src/Misc.jl index 048a079ab1aa..57dcd0102fc1 100644 --- a/experimental/IntersectionTheory/src/Misc.jl +++ b/experimental/IntersectionTheory/src/Misc.jl @@ -53,31 +53,6 @@ function Base.getindex(x::MPolyDecRingOrQuoElem, I::AbstractUnitRange) return getindex(x, [D([n]) for n in I]) end -############################################################################### -# -# Combinatoric functions -# - -# partitions of n with at most k numbers each ≤ m -function partitions(n::Int, k::Int=n, m::Int=-1) - ans = Partition[] - (n > 0 && k == 0) && return ans - if m < 0 m = n end - n <= m && push!(ans, partition(n > 0 ? [n] : Int[])) - for v in Int(min(n-1,m)):-1:1 - for p in partitions(n-v, k-1, v) - push!(ans, partition(pushfirst!(collect(p), v))) - end - end - ans -end - -# make combinations work for arrays -# function combinations(I::AbstractUnitRange, k::Int) combinations(collect(I), k) end -# function combinations(l::Vector, k::Int) -# [[l[i] for i in c] for c in combinations(length(l), k)] -# end - ############################################################################### # # pretty printing diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index a12d9531f60d..85448181c689 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -111,7 +111,9 @@ end # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", L::AbstractLieAlgebra) +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") println(io, Indent(), "of dimension $(dim(L))", Dedent()) @@ -120,6 +122,8 @@ function Base.show(io::IO, ::MIME"text/plain", L::AbstractLieAlgebra) end function Base.show(io::IO, L::AbstractLieAlgebra) + @show_name(io, L) + @show_special(io, L) if is_terse(io) print(io, "Abstract Lie algebra") else diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index eb20ad352e98..16ad659ba801 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -477,7 +477,7 @@ end ############################################################################### @doc raw""" - lie_algebra(gapL::GAP.GapObj, s::Vector{<:VarName}; cached::Bool) -> LieAlgebra{elem_type(R)} + lie_algebra(gapL::GapObj, s::Vector{<:VarName}; cached::Bool) -> LieAlgebra{elem_type(R)} Construct a Lie algebra isomorphic to the GAP Lie algebra `gapL`. Its basis element are named by `s`, or by `x_i` by default. @@ -487,7 +487,7 @@ properties of `gapL`, in particular, whether GAP knows about a matrix representa If `cached` is `true`, the constructed Lie algebra is cached. """ function lie_algebra( - gapL::GAP.GapObj, + gapL::GapObj, s::Vector{<:VarName}=[Symbol("x_$i") for i in 1:GAPWrap.Dimension(gapL)]; cached::Bool=true, ) diff --git a/experimental/LieAlgebras/src/LieAlgebraHom.jl b/experimental/LieAlgebras/src/LieAlgebraHom.jl index 91ec4cc2aea5..de1a468cc6eb 100644 --- a/experimental/LieAlgebras/src/LieAlgebraHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraHom.jl @@ -69,9 +69,11 @@ end # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", h::LieAlgebraHom) +function Base.show(io::IO, mime::MIME"text/plain", h::LieAlgebraHom) + @show_name(io, h) + @show_special(io, mime, h) io = pretty(io) - println(terse(io), h) + println(io, LowercaseOff(), "Lie algebra morphism") print(io, Indent()) println(io, "from ", Lowercase(), domain(h)) print(io, "to ", Lowercase(), codomain(h)) @@ -79,6 +81,8 @@ function Base.show(io::IO, ::MIME"text/plain", h::LieAlgebraHom) end function Base.show(io::IO, h::LieAlgebraHom) + @show_name(io, h) + @show_special(io, h) io = pretty(io) if is_terse(io) print(io, LowercaseOff(), "Lie algebra morphism") diff --git a/experimental/LieAlgebras/src/LieAlgebraIdeal.jl b/experimental/LieAlgebras/src/LieAlgebraIdeal.jl index c90a3960b3ed..b03a1580e9e7 100644 --- a/experimental/LieAlgebras/src/LieAlgebraIdeal.jl +++ b/experimental/LieAlgebras/src/LieAlgebraIdeal.jl @@ -101,7 +101,9 @@ dim(I::LieAlgebraIdeal) = length(basis(I)) # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", I::LieAlgebraIdeal) +function Base.show(io::IO, mime::MIME"text/plain", I::LieAlgebraIdeal) + @show_name(io, I) + @show_special(io, mime, I) io = pretty(io) println(io, LowercaseOff(), "Lie algebra ideal") println(io, Indent(), "of dimension $(dim(I))", Dedent()) @@ -113,6 +115,8 @@ function Base.show(io::IO, ::MIME"text/plain", I::LieAlgebraIdeal) end function Base.show(io::IO, I::LieAlgebraIdeal) + @show_name(io, I) + @show_special(io, I) io = pretty(io) if is_terse(io) print(io, LowercaseOff(), "Lie algebra ideal") diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 853c2cdd3c0a..4ab1e87170bc 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -151,7 +151,9 @@ end # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", V::LieAlgebraModule) +function Base.show(io::IO, mime::MIME"text/plain", V::LieAlgebraModule) + @show_name(io, V) + @show_special(io, mime, V) io = pretty(io) println(io, _module_type_to_string(V)) println(io, Indent(), "of dimension $(dim(V))") @@ -211,6 +213,8 @@ function _show_inner(io::IO, V::LieAlgebraModule) end function Base.show(io::IO, V::LieAlgebraModule) + @show_name(io, V) + @show_special(io, V) if is_terse(io) print(io, _module_type_to_string(V)) else @@ -1000,7 +1004,7 @@ julia> L = special_linear_lie_algebra(QQ, 2); julia> V = symmetric_power(standard_module(L), 2)[1]; # some module julia> E, map = exterior_power(V, 2) -(Exterior power module of dimension 3 over sl_2, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> exterior power module) +(Exterior power module of dimension 3 over L, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> E) julia> E Exterior power module @@ -1129,7 +1133,7 @@ julia> L = special_linear_lie_algebra(QQ, 4); julia> V = exterior_power(standard_module(L), 3)[1]; # some module julia> S, map = symmetric_power(V, 2) -(Symmetric power module of dimension 10 over sl_4, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> symmetric power module) +(Symmetric power module of dimension 10 over L, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> S) julia> S Symmetric power module @@ -1283,7 +1287,7 @@ julia> L = special_linear_lie_algebra(QQ, 3); julia> V = exterior_power(standard_module(L), 2)[1]; # some module julia> T, map = tensor_power(V, 2) -(Tensor power module of dimension 9 over sl_3, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> tensor power module) +(Tensor power module of dimension 9 over L, Map: parent of tuples of type Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}} -> T) julia> T Tensor power module diff --git a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl index c2885ebe4b67..e2ebcbb6d59d 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModuleHom.jl @@ -76,9 +76,11 @@ end # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", h::LieAlgebraModuleHom) +function Base.show(io::IO, mime::MIME"text/plain", h::LieAlgebraModuleHom) + @show_name(io, h) + @show_special(io, mime, h) io = pretty(io) - println(terse(io), h) + println(io, LowercaseOff(), "Lie algebra module morphism") print(io, Indent()) println(io, "from ", Lowercase(), domain(h)) print(io, "to ", Lowercase(), codomain(h)) @@ -86,6 +88,8 @@ function Base.show(io::IO, ::MIME"text/plain", h::LieAlgebraModuleHom) end function Base.show(io::IO, h::LieAlgebraModuleHom) + @show_name(io, h) + @show_special(io, h) io = pretty(io) if is_terse(io) print(io, LowercaseOff(), "Lie algebra module morphism") @@ -220,8 +224,8 @@ julia> V2 = direct_sum(V1, V3); julia> h = hom(V1, V2, [V2([v, zero(V3)]) for v in basis(V1)]) Lie algebra module morphism - from standard module of dimension 2 over sl_2 - to direct sum module of dimension 5 over sl_2 + from standard module of dimension 2 over L + to direct sum module of dimension 5 over L julia> [(v, h(v)) for v in basis(V1)] 2-element Vector{Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}}}: @@ -258,8 +262,8 @@ julia> V2 = trivial_module(L); julia> h = hom(V1, V2, matrix(QQ, 3, 1, [0, 0, 0])) Lie algebra module morphism - from standard module of dimension 3 over gl_3 - to abstract Lie algebra module of dimension 1 over gl_3 + from standard module of dimension 3 over L + to abstract Lie algebra module of dimension 1 over L julia> [(v, h(v)) for v in basis(V1)] 3-element Vector{Tuple{LieAlgebraModuleElem{QQFieldElem}, LieAlgebraModuleElem{QQFieldElem}}}: @@ -290,8 +294,8 @@ over special linear Lie algebra of degree 3 over QQ julia> identity_map(V) Lie algebra module morphism - from standard module of dimension 3 over sl_3 - to standard module of dimension 3 over sl_3 + from standard module of dimension 3 over L + to standard module of dimension 3 over L ``` """ function identity_map(V::LieAlgebraModule) @@ -315,8 +319,8 @@ over special linear Lie algebra of degree 3 over QQ julia> zero_map(V) Lie algebra module morphism - from standard module of dimension 3 over sl_3 - to standard module of dimension 3 over sl_3 + from standard module of dimension 3 over L + to standard module of dimension 3 over L ``` """ function zero_map(V1::LieAlgebraModule{C}, V2::LieAlgebraModule{C}) where {C<:FieldElem} diff --git a/experimental/LieAlgebras/src/LieSubalgebra.jl b/experimental/LieAlgebras/src/LieSubalgebra.jl index 4350e26731d0..b4cb4549e979 100644 --- a/experimental/LieAlgebras/src/LieSubalgebra.jl +++ b/experimental/LieAlgebras/src/LieSubalgebra.jl @@ -87,7 +87,7 @@ function basis(S::LieSubalgebra, i::Int) end @doc raw""" - dim(S::LieSubalgebra) -> Int + dim(S::LieSubalgebra) -> Int Return the dimension of the Lie subalgebra `S`. """ @@ -99,7 +99,9 @@ dim(S::LieSubalgebra) = length(basis(S)) # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", S::LieSubalgebra) +function Base.show(io::IO, mime::MIME"text/plain", S::LieSubalgebra) + @show_name(io, S) + @show_special(io, mime, S) io = pretty(io) println(io, LowercaseOff(), "Lie subalgebra") println(io, Indent(), "of dimension $(dim(S))", Dedent()) @@ -111,6 +113,8 @@ function Base.show(io::IO, ::MIME"text/plain", S::LieSubalgebra) end function Base.show(io::IO, S::LieSubalgebra) + @show_name(io, S) + @show_special(io, S) io = pretty(io) if is_terse(io) print(io, LowercaseOff(), "Lie subalgebra") diff --git a/experimental/LieAlgebras/src/LinearLieAlgebra.jl b/experimental/LieAlgebras/src/LinearLieAlgebra.jl index 55df81638c8d..0a85c7e14761 100644 --- a/experimental/LieAlgebras/src/LinearLieAlgebra.jl +++ b/experimental/LieAlgebras/src/LinearLieAlgebra.jl @@ -82,7 +82,9 @@ end # ############################################################################### -function Base.show(io::IO, ::MIME"text/plain", L::LinearLieAlgebra) +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()) @@ -91,6 +93,8 @@ function Base.show(io::IO, ::MIME"text/plain", L::LinearLieAlgebra) end function Base.show(io::IO, L::LinearLieAlgebra) + @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)) else diff --git a/experimental/LieAlgebras/src/RootSystem.jl b/experimental/LieAlgebras/src/RootSystem.jl index 8c7598fc83c7..b22921553075 100644 --- a/experimental/LieAlgebras/src/RootSystem.jl +++ b/experimental/LieAlgebras/src/RootSystem.jl @@ -72,15 +72,19 @@ function root_system(type::Tuple{Symbol,Int}...) return root_system(collect(type)) end -function Base.show(io::IO, ::MIME"text/plain", R::RootSystem) +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"text/plain"(), cartan_matrix(R)) + show(io, mime, cartan_matrix(R)) print(io, Dedent()) end function Base.show(io::IO, R::RootSystem) + @show_name(io, R) + @show_special(io, R) if is_terse(io) print(io, "Root system") else @@ -722,7 +726,7 @@ function Base.deepcopy_internal(w::WeightLatticeElem, dict::IdDict) end @doc raw""" - getindex(w::WeightLatticeElem, i::Int) -> ZZRingElem + getindex(w::WeightLatticeElem, i::Int) -> ZZRingElem Return the coefficient of the `i`-th fundamental weight in `w`. """ diff --git a/experimental/LieAlgebras/src/WeylGroup.jl b/experimental/LieAlgebras/src/WeylGroup.jl index cc11fe979eab..dd6af2e8c7ee 100644 --- a/experimental/LieAlgebras/src/WeylGroup.jl +++ b/experimental/LieAlgebras/src/WeylGroup.jl @@ -122,7 +122,9 @@ function Base.one(W::WeylGroup) end function Base.show(io::IO, W::WeylGroup) - print(io, "Weyl group for $(W.root_system)") + @show_name(io, W) + @show_special(io, W) + print(pretty(io), LowercaseOff(), "Weyl group for $(W.root_system)") end function coxeter_matrix(W::WeylGroup) @@ -376,6 +378,8 @@ function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{WeylGroup} end function Base.show(io::IO, x::WeylGroupElem) + @show_name(io, x) + @show_special_elem(io, x) if length(x.word) == 0 print(io, "id") else diff --git a/experimental/LieAlgebras/src/iso_gap_oscar.jl b/experimental/LieAlgebras/src/iso_gap_oscar.jl index b25de7f97e52..c06cacaf677f 100644 --- a/experimental/LieAlgebras/src/iso_gap_oscar.jl +++ b/experimental/LieAlgebras/src/iso_gap_oscar.jl @@ -4,7 +4,7 @@ # ############################################################################### -function _iso_gap_oscar_lie_algebra(F::GAP.GapObj) +function _iso_gap_oscar_lie_algebra(F::GapObj) if GAPWrap.IsFiniteDimensional(F) if GAPWrap.IsLieObjectCollection(F) return _iso_gap_oscar_linear_lie_algebra(F) @@ -19,9 +19,9 @@ end push!(Oscar._iso_gap_oscar_methods, "IsLieAlgebra" => _iso_gap_oscar_lie_algebra) function _iso_gap_oscar_abstract_lie_algebra( - LG::GAP.GapObj, + LG::GapObj, s::Vector{<:VarName}=[Symbol("x_$i") for i in 1:GAPWrap.Dimension(LG)]; - coeffs_iso::Map{GAP.GapObj}=Oscar.iso_gap_oscar(GAPWrap.LeftActingDomain(LG)), + coeffs_iso::Map{GapObj}=Oscar.iso_gap_oscar(GAPWrap.LeftActingDomain(LG)), cached::Bool=true, ) LO = _abstract_lie_algebra_from_GAP(LG, coeffs_iso, s; cached) @@ -33,9 +33,9 @@ function _iso_gap_oscar_abstract_lie_algebra( end function _iso_gap_oscar_linear_lie_algebra( - LG::GAP.GapObj, + LG::GapObj, s::Vector{<:VarName}=[Symbol("x_$i") for i in 1:GAPWrap.Dimension(LG)]; - coeffs_iso::Map{GAP.GapObj}=Oscar.iso_gap_oscar(GAPWrap.LeftActingDomain(LG)), + coeffs_iso::Map{GapObj}=Oscar.iso_gap_oscar(GAPWrap.LeftActingDomain(LG)), cached::Bool=true, ) LO = _linear_lie_algebra_from_GAP(LG, coeffs_iso, s; cached) @@ -47,7 +47,7 @@ function _iso_gap_oscar_linear_lie_algebra( end function _abstract_lie_algebra_from_GAP( - LG::GAP.GapObj, coeffs_iso::Map{GAP.GapObj}, s::Vector{<:VarName}; cached::Bool=true + LG::GapObj, coeffs_iso::Map{GapObj}, s::Vector{<:VarName}; cached::Bool=true ) RO = codomain(coeffs_iso) dimL = GAPWrap.Dimension(LG) @@ -72,7 +72,7 @@ function _abstract_lie_algebra_from_GAP( end function _linear_lie_algebra_from_GAP( - LG::GAP.GapObj, coeffs_iso::Map{GAP.GapObj}, s::Vector{<:VarName}; cached::Bool=true + LG::GapObj, coeffs_iso::Map{GapObj}, s::Vector{<:VarName}; cached::Bool=true ) @req GAPWrap.IsLieObjectCollection(LG) "Input is not a linear Lie algebra." diff --git a/experimental/LieAlgebras/src/iso_oscar_gap.jl b/experimental/LieAlgebras/src/iso_oscar_gap.jl index a7d5ff2b194f..578d146d6f45 100644 --- a/experimental/LieAlgebras/src/iso_oscar_gap.jl +++ b/experimental/LieAlgebras/src/iso_oscar_gap.jl @@ -5,7 +5,7 @@ ############################################################################### function _iso_oscar_gap_lie_algebra_functions( - LO::LieAlgebra{C}, LG::GAP.GapObj, coeffs_iso::MapFromFunc + LO::LieAlgebra{C}, LG::GapObj, coeffs_iso::MapFromFunc ) where {C<:FieldElem} basis_LG = GAPWrap.Basis(LG) diff --git a/experimental/MatrixGroups/src/MatrixGroups.jl b/experimental/MatrixGroups/src/MatrixGroups.jl index 8d3d6f419395..4c34885c092f 100644 --- a/experimental/MatrixGroups/src/MatrixGroups.jl +++ b/experimental/MatrixGroups/src/MatrixGroups.jl @@ -10,7 +10,7 @@ export _wrap_for_gap # Initialize GAP function, i.e. GAP reads the file matrix.g # function __init__() - GAP.Globals.Read(GAP.GapObj(joinpath(@__DIR__, "matrix.g"))) + GAP.Globals.Read(GapObj(joinpath(@__DIR__, "matrix.g"))) end ################################################################################ diff --git a/experimental/NoExperimental_whitelist_.jl b/experimental/NoExperimental_whitelist_.jl index 89d4e2707125..a39efd1bab10 100644 --- a/experimental/NoExperimental_whitelist_.jl +++ b/experimental/NoExperimental_whitelist_.jl @@ -4,7 +4,7 @@ whitelist = String[ "DoubleAndHyperComplexes", # `MethodError: no method matching simplify(::SubquoModule{MPolyQuoRingElem{MPolyDecRingElem{FqMPolyRingElem, AbstractAlgebra.Generic.MPoly{FqMPolyRingElem}}}})` "ExteriorAlgebra", # `undefined binding 'exterior_algebra' in `@docs` block in src/NoncommutativeAlgebra/PBWAlgebras/quotients.md:40-42` and `Error During Test at /home/runner/work/Oscar.jl/Oscar.jl/test/Rings/PBWAlgebraQuo.jl:41` "GaloisGrp", # `no docs found for 'fixed_field(C::Oscar.GaloisGrp.GaloisCtx, s::Vector{PermGroup})' in `@docs` block in src/NumberTheory/galois.md:275-278` - "GModule", # many doctest failures and `MethodError: no method matching numerator(::QQPolyRingElem)` + "GModule", # `MethodError: no method matching (::FinGenAbGroup)(::FinGenAbGroupElem)` "InvariantTheory", # `undefined binding 'linearly_reductive_group' in `@docs` block in src/InvariantTheory/reductive_groups.md:53-55` and more docs errors` "ModStd", # `MethodError: no method matching monomial(::QQMPolyRing, ::Vector{Int64})` and many similar errors "Schemes", # TODO: untangle src/AlgebraicGeometry/Schemes/ and experimental/Schemes/ diff --git a/experimental/OrthogonalDiscriminants/src/theoretical.jl b/experimental/OrthogonalDiscriminants/src/theoretical.jl index 2b6ec191236f..64cc10c58c7b 100644 --- a/experimental/OrthogonalDiscriminants/src/theoretical.jl +++ b/experimental/OrthogonalDiscriminants/src/theoretical.jl @@ -240,7 +240,7 @@ function od_from_p_subgroup(chi::GAPGroupClassFunction, p::Int, end fus = filter(i -> pi[i] != 0, 1:number_of_conjugacy_classes(tbl)) - res = Oscar.GAPWrap.ELMS_LIST(chi.values, GAP.GapObj(fus)) + res = Oscar.GAPWrap.ELMS_LIST(GapObj(chi), GapObj(fus)) if l != 0 # possible shortcut: diff --git a/experimental/OrthogonalDiscriminants/src/utils.jl b/experimental/OrthogonalDiscriminants/src/utils.jl index 0d1c75fe9dec..62601f999593 100644 --- a/experimental/OrthogonalDiscriminants/src/utils.jl +++ b/experimental/OrthogonalDiscriminants/src/utils.jl @@ -150,10 +150,10 @@ function possible_permutation_characters_from_sylow_subgroup(tbl::Oscar.GAPGroup pos != nothing && return [induced_cyclic(tbl, [pos])[1]] # Do we have the table of marks? - tom = GAP.Globals.TableOfMarks(Oscar.GAPTable(tbl)) + tom = GAP.Globals.TableOfMarks(GapObj(tbl)) if tom != GAP.Globals.fail pos = findfirst(x -> x == q, Vector{Int}(GAP.Globals.OrdersTom(tom))) - return [Oscar.class_function(tbl, GAP.Globals.PermCharsTom(Oscar.GAPTable(tbl), tom)[pos])] + return [Oscar.class_function(tbl, GAP.Globals.PermCharsTom(GapObj(tbl), tom)[pos])] end # Does the table have a nontrivial 'p'-core @@ -193,7 +193,7 @@ function possible_permutation_characters_from_sylow_subgroup(tbl::Oscar.GAPGroup for i in npos pi[i] = index end - pi = GAP.Globals.ClassFunction(Oscar.GAPTable(s), GapObj(pi, true)) + pi = GAP.Globals.ClassFunction(GapObj(s), GapObj(pi, true)) return [Oscar.class_function(s, pi)^tbl] else # Try to recurse. @@ -216,11 +216,11 @@ function possible_permutation_characters_from_sylow_subgroup(tbl::Oscar.GAPGroup if cand != nothing extcand = [] for pi in cand - pi = GAP.Globals.CompositionMaps(pi.values, + pi = GAP.Globals.CompositionMaps(GapObj(pi), GAP.Globals.InverseMap(GapObj(fus))) union!(extcand, [Oscar.class_function(tbl, x) for x in - GAP.Globals.PermChars(Oscar.GAPTable(tbl), - GAP.GapObj(Dict(:torso => pi), true))]) + GAP.Globals.PermChars(GapObj(tbl), + GapObj(Dict(:torso => pi), true))]) end if candlist == nothing candlist = extcand diff --git a/experimental/PartitionedPermutations/README.md b/experimental/PartitionedPermutations/README.md index 14916db1ea6a..2622e3b3bb91 100644 --- a/experimental/PartitionedPermutations/README.md +++ b/experimental/PartitionedPermutations/README.md @@ -22,12 +22,12 @@ We implemented the type `PartitionedPermutation` together with the following met - functions `length`, `length2` for computing the number $n$ of underlying elements of a partitioned permutation $(V, \pi)$ and the number $|(V, \pi)|$, respectively - a function `enumerate_partitioned_permutations` that enumerates all partitioned permutations of a fixed length $n$ - a function `*` that returns the product of two partitioned permutations -- a function `factorization_partitioned_permutation` that determines the factorization of a given partitioned permutation +- a function `factor` that determines the factorization of a given partitioned permutation ## Contact Please direct questions about this part of OSCAR to the following people: -* Björn Schäfer (bjoern.schaefer@stud.tu-darmstadt.de) +* Björn Schäfer (bschaefer@math.uni-sb.de) * Sebastian Volz (s8sevolz@stud.uni-saarland.de) You can ask questions in the [OSCAR Slack](https://www.oscar-system.org/community/#slack). diff --git a/experimental/PartitionedPermutations/docs/doc.main b/experimental/PartitionedPermutations/docs/doc.main new file mode 100644 index 000000000000..2baf9a5bfec8 --- /dev/null +++ b/experimental/PartitionedPermutations/docs/doc.main @@ -0,0 +1,5 @@ +[ + "Partitioned Permutations" => [ + "introduction.md" + ] +] diff --git a/experimental/PartitionedPermutations/docs/src/introduction.md b/experimental/PartitionedPermutations/docs/src/introduction.md new file mode 100644 index 000000000000..7893dd848568 --- /dev/null +++ b/experimental/PartitionedPermutations/docs/src/introduction.md @@ -0,0 +1,35 @@ +```@meta +CurrentModule = Oscar +``` + +# Partitioned Permutations + +Partitioned Permutations are used in the context of Free Probability Theory to model higher order freeness and higher order free cumulants, see e.g. [CMS07](@cite). + +We provide basic functions for working with partitioned permutations. The focus is on factorizing partitioned permutations. + +# Basics + +Formally, a partitioned permutation $(V, \pi)$ consists of a permutation $\pi$ and a partition $V$ of the set $\{1, ..., n\}$ such that the partition dominates the permutation in the sense that every cycle of $\pi$ is contained in one block of $V$. We call $n$ the length of $(V, \pi)$. Mathematically, another length function is more important. It is given by +$$|(V, \pi)| := n - ( 2 \cdot \text{number of blocks of } V - \text{number of cycles of } \pi),$$ +and we call this the adjusted length of $(V, \pi)$. Note that this terminology is not used in the literature. + +```@docs +PartitionedPermutation +length(pp::PartitionedPermutation) +adjusted_length(pp::PartitionedPermutation) +``` + +# Products of Partitioned Permutations + +For two partitioned permutations $(V, \pi)$ and $(W, \sigma)$ one defines their product as +$$(V, \pi) \cdot (W, \sigma) = (V \vee W, \pi \sigma)$$ +if $|(V, \pi)| + |(W, \sigma)| = |(V \vee W, \pi \sigma)|$. Otherwise, one sets $(V, \pi) \cdot (W, \sigma) = (O, \mathrm{id})$. Here, $O$ is the partition where every block consists of exactly one element, and $V \vee W$ denotes the join of the partitions $V$ and $W$. + +A major problem is the factorization of a partitioned permutation $(V, \pi)$. This involves finding all pairs $(W_1, \sigma_1), (W_2, \sigma_2)$ of partitioned permutations with $(V, \pi) = (W_1, \sigma_1) \cdot (W_2, \sigma_2)$. + +```@docs +*(pp_1::PartitionedPermutation, pp_2::PartitionedPermutation) +enumerate_partitioned_permutations(n::Int) +factor(pp::PartitionedPermutation) +``` diff --git a/experimental/PartitionedPermutations/src/PartitionedPermutation.jl b/experimental/PartitionedPermutations/src/PartitionedPermutation.jl index 901625915dea..a9ba24f8c20e 100644 --- a/experimental/PartitionedPermutations/src/PartitionedPermutation.jl +++ b/experimental/PartitionedPermutations/src/PartitionedPermutation.jl @@ -91,7 +91,7 @@ end """ adjusted_length(pp::PartitionedPermutation) -Return the adjusted length of a partitioned permutation as described in [CMSS07](@cite) as `|(V, pi)|` +Return the adjusted length of a partitioned permutation as described in [CMS07](@cite) as `|(V, pi)|` for a partition `V` and a permutation `pi`. # Examples diff --git a/experimental/QuadFormAndIsom/src/hermitian_miranda_morrison.jl b/experimental/QuadFormAndIsom/src/hermitian_miranda_morrison.jl index 394aa4269a5f..630469a876f4 100644 --- a/experimental/QuadFormAndIsom/src/hermitian_miranda_morrison.jl +++ b/experimental/QuadFormAndIsom/src/hermitian_miranda_morrison.jl @@ -527,8 +527,6 @@ end # ############################################################################### -Oscar.canonical_unit(x::AbsSimpleNumFieldOrderQuoRingElem) = one(parent(x)) - # Once we have fixed good local basis matrices if D^{-1}H^# and H, at the prime # ideal p below P, we transfer any g\in O(D_L, D_f) via the trace construction # (fixed by res). The new map we obtain should be a local invertible map with diff --git a/experimental/QuadFormAndIsom/src/lattices_with_isometry.jl b/experimental/QuadFormAndIsom/src/lattices_with_isometry.jl index 1ab250740090..9266e0c81c4f 100644 --- a/experimental/QuadFormAndIsom/src/lattices_with_isometry.jl +++ b/experimental/QuadFormAndIsom/src/lattices_with_isometry.jl @@ -75,7 +75,7 @@ QuadSpaceWithIsom ambient_space(Lf::ZZLatWithIsom) = Lf.Vf @doc raw""" - ambient_isometry(Lf::ZZLatWithIsom) -> QQMatrix + ambient_isometry(Lf::ZZLatWithIsom) -> QQMatrix Given a lattice with isometry $(L, f)$, return an isometry of the ambient space of $L$ inducing $f$ on $L$. diff --git a/experimental/Schemes/BlowupMorphism.jl b/experimental/Schemes/src/BlowupMorphism.jl similarity index 96% rename from experimental/Schemes/BlowupMorphism.jl rename to experimental/Schemes/src/BlowupMorphism.jl index c2e10c7dca01..d2f828d30bf4 100644 --- a/experimental/Schemes/BlowupMorphism.jl +++ b/experimental/Schemes/src/BlowupMorphism.jl @@ -235,8 +235,8 @@ julia> bl = blow_up(A3, I) Blowup of scheme over QQ covered with 1 patch 1b: [x, y, z] affine 3-space - in prime ideal sheaf on scheme over QQ covered with 1 patch - 1b: [x, y, z] affine 3-space extended from ideal (x, y, z) on affine 3-space + in sheaf of ideals with restriction + 1b: Ideal (x, y, z) with domain scheme over QQ covered with 3 patches 1a: [(s1//s0), (s2//s0), x] scheme(0, 0, 0) @@ -262,7 +262,11 @@ defined by 3: Ideal (z) julia> Z = center(bl) -Prime ideal sheaf on Scheme over QQ covered with 1 patch extended from Ideal (x, y, z) on Affine 3-space +Sheaf of ideals + on scheme over QQ covered with 1 patch + 1: [x, y, z] affine 3-space +with restriction + 1: Ideal (x, y, z) ``` """ @attributes mutable struct BlowupMorphism{ @@ -542,6 +546,10 @@ For a `BlowupMorphism` ``p : Y → X`` and an `EffectiveCartierDivisor` ``C`` o strict transform of ``C`` on ``Y``. """ function strict_transform(p::AbsSimpleBlowdownMorphism, C::EffectiveCartierDivisor) + return strict_transform_with_multiplicity(p,C)[1] +end + +function strict_transform_with_multiplicity(p::AbsSimpleBlowdownMorphism, C::EffectiveCartierDivisor) X = scheme(C) Y = domain(p) X === codomain(p) || error("cartier divisor is not defined on the codomain of the morphism") @@ -597,7 +605,7 @@ function strict_transform(p::AbsSimpleBlowdownMorphism, C::EffectiveCartierDivis ## we are good to go now C_strict = EffectiveCartierDivisor(Y, ID, check=false) - return C_strict + return C_strict,multEInC end function strict_transform(p::AbsSimpleBlowdownMorphism, C::CartierDivisor) @@ -862,16 +870,17 @@ end # fields for caching, may be filled during computation ex_div::Vector{<:EffectiveCartierDivisor} # list of exc. divisors arising from individual steps # lives in domain(maps[end]) + control::Int # value of control for controlled transform ex_mult::Vector{Int} # multiplicities of exceptional divisors removed from - # controlled or weak transform, not set for is_embedded == false - # and transform_type == strict - controlled_transform::AbsIdealSheaf # holds weak or controlled transform according to transform_type + # total transform, not set for is_embedded == false + # or transform_type == strict + controlled_transform::AbsIdealSheaf # holds weak or controlled transform according to transform_type # fields for caching to be filled a posteriori (on demand, only if partial_res==false) underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} exceptional_divisor::CartierDivisor # exceptional divisor of composed_map exceptional_locus::WeilDivisor # exceptional locus of composed map - exceptional_divisor_on_X::WeilDivisor # exceptional divisor of composed_map + exceptional_divisor_on_X::CartierDivisor # exceptional divisor of composed_map # restricted to domain(embeddings[end]) function BlowUpSequence(maps::Vector{<:BlowupMorphism}) @@ -942,8 +951,13 @@ function produce_object_on_affine_chart(I::StrictTransformIdealSheaf, U::AbsAffi V = codomain(f_loc) IE_loc = IE(U) tot = pullback(f_loc)(J(V)) - result, _ = saturation_with_index(tot, IE_loc) - return result + #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) + result, _ = saturation_with_index(pullback(a)(tot), pullback(a)(IE_loc)) + return pullback(b)(result) end @attr Bool function is_prime(I::StrictTransformIdealSheaf) diff --git a/experimental/Schemes/src/CoveredProjectiveSchemes.jl b/experimental/Schemes/src/CoveredProjectiveSchemes.jl index 55db8c4ae602..e55c294bf58a 100644 --- a/experimental/Schemes/src/CoveredProjectiveSchemes.jl +++ b/experimental/Schemes/src/CoveredProjectiveSchemes.jl @@ -748,13 +748,9 @@ Return the blow-up morphism of blowing up ``X`` at ``I`` in ``OO(X)``. function blow_up( X::AbsAffineScheme{<:Any, <:MPolyAnyRing}, I::MPolyAnyIdeal) - R = OO(X) @req R == base_ring(I) "I must be an ideal in the coordinate ring of X" - ## prepare trivially covered scheme and ideal sheaf on it as input for blow_up(I::IdealSheaf) - C = Covering([X]) - XX = CoveredScheme(C) - Isheaf = ideal_sheaf(XX,X,gens(I)) + Isheaf = IdealSheaf(X, I) return blow_up(Isheaf) end diff --git a/experimental/Schemes/src/CoveredScheme.jl b/experimental/Schemes/src/CoveredScheme.jl index 8e863bc5ca5e..12a2a1961738 100644 --- a/experimental/Schemes/src/CoveredScheme.jl +++ b/experimental/Schemes/src/CoveredScheme.jl @@ -482,45 +482,6 @@ _compose_along_path(X::CoveredScheme, p::Vector{Int}) = _compose_along_path(X, [ ######################################################################## # Closed embeddings # ######################################################################## -@attributes mutable struct CoveredClosedEmbedding{ - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - BaseMorphismType - } <: AbsCoveredSchemeMorphism{ - DomainType, - CodomainType, - BaseMorphismType, - CoveredSchemeMorphism - } - f::CoveredSchemeMorphism - I::AbsIdealSheaf - - function CoveredClosedEmbedding( - X::DomainType, - Y::CodomainType, - f::CoveringMorphism{<:Any, <:Any, MorphismType, BaseMorType}; - check::Bool=true, - ideal_sheaf::AbsIdealSheaf=IdealSheaf(Y, f, check=check) - ) where { - DomainType<:AbsCoveredScheme, - CodomainType<:AbsCoveredScheme, - MorphismType<:ClosedEmbedding, - BaseMorType - } - ff = CoveredSchemeMorphism(X, Y, f; check) - if has_decomposition_info(codomain(f)) - for U in patches(domain(f)) - floc = f[U] - phi = pullback(floc) - V = codomain(floc) - g = Vector{elem_type(OO(V))}(decomposition_info(codomain(f))[V]) - set_decomposition_info!(domain(f), U, Vector{elem_type(OO(U))}(phi.(g))) - end - end - #all(x->(x isa ClosedEmbedding), values(morphisms(f))) || error("the morphisms on affine patches must be `ClosedEmbedding`s") - return new{DomainType, CodomainType, BaseMorType}(ff, ideal_sheaf) - end -end ### forwarding the essential getters underlying_morphism(phi::CoveredClosedEmbedding) = phi.f @@ -560,6 +521,17 @@ function CoveredClosedEmbedding(X::AbsCoveredScheme, I::AbsIdealSheaf; return CoveredClosedEmbedding(Z, X, cov_inc, ideal_sheaf=I, check=false) end +function base_change(phi::Any, f::CoveredClosedEmbedding; + domain_map::AbsCoveredSchemeMorphism=base_change(phi, domain(f))[2], + codomain_map::AbsCoveredSchemeMorphism=base_change(phi, codomain(f))[2] + ) + g = underlying_morphism(f) + _, bc_g, _ = base_change(phi, g; domain_map, codomain_map) + II = image_ideal(f) + bc_II = pullback(codomain_map, II) + return domain_map, CoveredClosedEmbedding(domain(bc_g), codomain(bc_g), covering_morphism(bc_g); check=false, ideal_sheaf=bc_II), codomain_map +end + ######################################################################## # Composite morphism of covered schemes ######################################################################## diff --git a/experimental/Schemes/src/IdealSheaves.jl b/experimental/Schemes/src/IdealSheaves.jl index 1f9ed35ccd07..2aef91102274 100644 --- a/experimental/Schemes/src/IdealSheaves.jl +++ b/experimental/Schemes/src/IdealSheaves.jl @@ -160,26 +160,32 @@ to the other charts of ``X`` is well defined. """ function IdealSheaf( X::AbsCoveredScheme, U::AbsAffineScheme, - I::Ideal; check::Bool=false + I::Ideal; check::Bool=true ) @assert base_ring(I) === OO(U) || error("ideal not defined in the correct ring") @check is_prime(I) "ideal must be prime" return PrimeIdealSheafFromChart(X, U, I) end -function IdealSheaf(X::AbsAffineScheme, I::Ideal; covered_scheme::AbsCoveredScheme=CoveredScheme(X)) - @assert base_ring(I) === OO(X) - @assert length(affine_charts(covered_scheme)) == 1 && X === first(affine_charts(covered_scheme)) - return IdealSheaf(covered_scheme, IdDict{AbsAffineScheme, Ideal}([X=>I]); check=false) -end - function IdealSheaf( X::AbsCoveredScheme, U::AbsAffineScheme, - g::Vector{RET}; check::Bool=false + g::Vector{RET}; check::Bool=true ) where {RET<:RingElem} return IdealSheaf(X, U, ideal(OO(U), g); check) end +@doc raw""" + IdealSheaf(X::AbsAffineScheme, I::Ideal; Xcoverd=covered_scheme(X)) -> IdealSheaf + +Return `I` as an ideal sheaf on the covered scheme with 1 affine patch `X`. +""" +function IdealSheaf(X::AbsAffineScheme, I::Ideal; covered_scheme::AbsCoveredScheme=covered_scheme(X), check::Bool=true) + @req base_ring(I) === OO(X) "ideal must lie in the coordinate ring of X" + @req length(affine_charts(covered_scheme)) == 1 && X === first(affine_charts(covered_scheme)) "covered_scheme must be the covered scheme with a single patch X" + return IdealSheaf(covered_scheme, IdDict{AbsAffineScheme, Ideal}([X=>I]); check=false) +end + + ideal_sheaf(X::AbsCoveredScheme, U::AbsAffineScheme, g::Vector{RET}; check::Bool=true) where {RET<:RingElem} = IdealSheaf(X, U, g; check) ideal_sheaf(X::AbsCoveredScheme, U::AbsAffineScheme, I::Ideal; check::Bool=true) = IdealSheaf(X, U, I; check) @@ -764,17 +770,47 @@ function smooth_lci_covering(I::AbsIdealSheaf) error("not implemented") end +### pushforward of ideal sheaves along closed embeddings function pushforward(inc::CoveredClosedEmbedding, I::AbsIdealSheaf) - Y = domain(inc) - scheme(I) === Y || error("ideal sheaf is not defined on the domain of the embedding") - X = codomain(inc) - phi = covering_morphism(inc) - ID = IdDict{AbsAffineScheme, Ideal}() - for U in patches(domain(phi)) - V = codomain(phi[U]) - ID[V] = pushforward(phi[U], I(U)) + return PushforwardIdealSheaf(inc, I) +end + +function pushforward(inc::CoveredClosedEmbedding, I::PrimeIdealSheafFromChart) + X = scheme(I) + @assert X === domain(inc) + U = original_chart(I) + inc_cov = covering_morphism(inc) + dom_cov = domain(inc_cov) + def_cov = default_covering(X) + V = __find_chart(U, def_cov) + for UU in patches(dom_cov) + if has_ancestor(x->x===V, UU) + if !is_one(I(V)) + J = pushforward(inc_cov[UU], I(UU)) + return PrimeIdealSheafFromChart(codomain(inc), codomain(inc_cov[UU]), J) + else + continue + end + end end - return IdealSheaf(X, ID, check=false) + error("no patch found") +end + +morphism(I::PushforwardIdealSheaf) = I.f +original_ideal_sheaf(I::PushforwardIdealSheaf) = I.orig +underlying_presheaf(I::PushforwardIdealSheaf) = I.Ipre + +function produce_object_on_affine_chart(II::PushforwardIdealSheaf, U::AbsAffineScheme) + f = morphism(II) + def_cov = default_covering(codomain(f)) + phi = covering_morphism(f) + g = [g for (V, g) in morphisms(phi) if has_ancestor(x->x===U, codomain(g))] + is_empty(g) && return ideal(OO(U), one(OO(U))) # the domain scheme is not visible in this chart + loc_id_dict = IdDict{AbsAffineScheme, Ideal}(codomain(inc)=>pushforward(inc, original_ideal_sheaf(II)(domain(inc))) for inc in g) + # Assemble the ideal on U from those in the patches covering it + loc_id = [pullback(inverse(_flatten_open_subscheme(V, def_cov)))(I) for (V, I) in loc_id_dict] + return ideal(OO(U), gens(reduce(intersect, saturated_ideal.(loc_id)))) + return pushforward(inc, original_ideal_sheaf(II)(domain(inc))) end function pushforward(inc::ClosedEmbedding, I::Ideal) diff --git a/experimental/Schemes/Resolution_structure.jl b/experimental/Schemes/src/Resolution_structure.jl similarity index 60% rename from experimental/Schemes/Resolution_structure.jl rename to experimental/Schemes/src/Resolution_structure.jl index 898cd3645a13..55cb5dff97a6 100644 --- a/experimental/Schemes/Resolution_structure.jl +++ b/experimental/Schemes/src/Resolution_structure.jl @@ -1,54 +1,148 @@ -## Warnung: show auf desingMor geht noch nicht!!! -export _desing_curve -export find_refinement_with_local_system_of_params +export find_refinement_with_local_system_of_params +export inclusion_morphisms +export embedded_desingularization +export desingularization +export desingularization_only_blowups +export exceptional_locus +export NormalizationMorphism +export locus_of_maximal_order + +############################################################################## +## Concrete Type for normalization +## very similar to CoveredSchemeMorphism, but allowing disjoint handling +## of disjoint components +############################################################################## +@doc raw""" + NormalizationMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + } <:AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + NormalizationMorphism, + } +A datastructure to encode normalizations of covered schemes. + +It is described as the morphism from the new scheme to the original one, containing +information on the decomposition of the new scheme into disjoint components. +(This is the type of the return value of `normalization(X::AbsCoveredScheme)`.) +""" +@attributes mutable struct NormalizationMorphism{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + } <:AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + Nothing, + NormalizationMorphism, + } + + underlying_morphism::CoveredSchemeMorphism + inclusions::Vector{<:AbsCoveredSchemeMorphism} + + function NormalizationMorphism( + f::CoveredSchemeMorphism, + inclusions::Vector{<:AbsCoveredSchemeMorphism}; + check::Bool=true + ) + @check is_normal(X) "not a normalization morphism" + @assert all(inc->codomain(inc) === domain(f), inclusions) "domains and codomains do not match" + ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,inclusions) + return ret_value + end + + function NormalizationMorphism( + f::CoveredSchemeMorphism; + check::Bool=true + ) + @check is_normal(X) "not a normalization morphism" + ret_value = new{typeof(domain(f)),typeof(codomain(f))}(f,[identity_map(X)]) + return ret_value + end + +end ##################################################################################################### # Desingularization morphism: birational map between covered schemes with smooth domain ##################################################################################################### +@doc raw""" + MixedBlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + }<:AbsDesingMor{ DomainType, + CodomainType, + MixedBlowUpSequence{DomainType, CodomainType} + } +A datastructure to encode sequences of blow-ups and normalizations of covered schemes +as needed for desingularization of non-embedded schemes by the approaches of Zariski and of +Lipman. +""" + +@attributes mutable struct MixedBlowUpSequence{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme + }<:AbsDesingMor{ DomainType, + CodomainType, + MixedBlowUpSequence{DomainType, CodomainType} + } + maps::Vector{Union{<:BlowupMorphism,<:NormalizationMorphism}} # count right to left: + # original scheme is codomain of map 1 + # boolean flags + resolves_sing::Bool # domain not smooth yet? + is_trivial::Bool # codomain already smooth? + + # fields for caching, to be filled during desingularization + # always carried along to domain(maps[end])) using strict_transform + ex_div::Vector{AbsIdealSheaf} # list of exc. divisors arising from individual steps + + # keep track of the normalization steps + normalization_steps::Vector{Int} + + # fields for caching to be filled a posteriori (on demand, only if partial_res==false) + underlying_morphism::CompositeCoveredSchemeMorphism{DomainType, CodomainType} + exceptional_divisor::AbsWeilDivisor + exceptional_locus::AbsAlgebraicCycle + + function MixedBlowUpSequence(maps::Vector{<:AbsCoveredSchemeMorphism}) + n = length(maps) + for i in 1:n + @assert all(x->((x isa BlowupMorphism) || (x isa NormalizationMorphism)), maps) "only blow-ups and normalizations allowed" + end + for i in 1:n-1 + @assert domain(maps[i]) === codomain(maps[i+1]) "not a sequence of morphisms" + end + resi = new{typeof(domain(maps[end])),typeof(codomain(first(maps)))}(maps) + resi.normalization_steps = [i for i in 1:n if maps[i] isa NormalizationMorphism] + return resi + end -# Fehlt: NormalizationMorphism fuer Schemata -- muessten wir haben, sobald wir Lipman machen wollen -# -#@attributes mutable struct LipmanStyleSequence{ -# DomainType<:AbsCoveredScheme, -# CodomainType<:AbsCoveredScheme -# } <: AbsDesingMor{ -# DomainType, -# CodomainType, -# } -# maps::Vector{: CartierDivisor + +Return a `CartierDivisor` on the `domain` of `f` which is the +exceptional divisor of the sequence of blow-ups `f`. + +# Example +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ,2); + +julia> I=ideal(R,[x^2-y^5]); + +julia> W = AffineScheme(R); + +julia> IS = IdealSheaf(W,I); + +julia> X = subscheme(IS); + +julia> U = first(affine_charts(X)); + +julia> phi = desingularization_only_blowups(X); + +julia> exceptional_divisor(phi) +Cartier divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +defined by the formal sum of + 1 * effective cartier divisor on scheme over QQ covered with 3 patches +``` +""" +function exceptional_divisor(f::BlowUpSequence) + f.is_embedded && return _exceptional_divisor_in_ambient(f) + return _exceptional_divisor_non_embedded(f) +end + +@doc raw""" + exceptional_locus(f::AbsBlowUpSequence) + +Return a `WeilDivisor` on the `domain` of `f` which is the +exceptional divisor of the sequence of blow-ups `f`. + +# Example +```jldoctest +julia> R,(x,y) = polynomial_ring(QQ,2); + +julia> I=ideal(R,[x^2-y^5]); + +julia> W = AffineScheme(R); + +julia> IS = IdealSheaf(W,I); + +julia> X = subscheme(IS); + +julia> U = first(affine_charts(X)); + +julia> phi = desingularization_only_blowups(X); + +julia> exceptional_locus(phi) +Effective weil divisor + on scheme over QQ covered with 3 patches +with coefficients in integer ring +given as the formal sum of + 1 * sheaf of ideals + +``` +""" +function exceptional_locus(phi::BlowUpSequence) + if !isdefined(phi, :exceptional_locus) + phi.exceptional_locus = weil_divisor(exceptional_divisor(phi)) + end + return phi.exceptional_locus +end + +@doc raw""" + exceptional_divisor_with_multiplicities(f::BlowUpSequence) --> CartierDivisor + +Return a `CartierDivisor` `C` on the `domain` of the emmbedded desingularization morphism `f` +which is the exceptional divisor of the sequence of blow-ups `f` in the ambient scheme. +""" +function exceptional_divisor_with_multiplicities(f::BlowUpSequence) + f.is_embedded || error("only available for embedded desingularization") + f.transform_type != :strict || error("only available for weak and controlled transforms") + + ex_div_list = exceptional_divisor_list(f) + C = sum(f.ex_mult[i] * cartier_divisor(ex_div_list[i]) for i in 1:length(ex_div_list)) + return C +end + +function _exceptional_divisor_in_ambient(f::BlowUpSequence) + f.is_embedded || error("only for embedded desingularization") + !isdefined(f,:exceptional_divisor) || return f.exceptional_divisor + f.exceptional_divisor = sum(f.ex_div; init=CartierDivisor(domain(f),ZZ)) + return f.exceptional_divisor +end + +function _exceptional_divisor_non_embedded(f::MixedBlowUpSequence) + !isdefined(f,:exceptional_divisor) || return f.exceptional_divisor + + ex_div_list = exceptional_divisor_list(f) + C = WeilDivisor(scheme(ex_div_list[1]),ZZ) + for i in 2:length(ex_div_list) + dim(ex_div_list[i])== -1 && continue # kick out empty ones + C = C + weil_divisor(ex_div_list[i]) + end + + f.exceptional_divisor = C + return C +end + +function _exceptional_divisor_non_embedded(f::BlowUpSequence) + !isdefined(f,:exceptional_divisor_on_X) || return f.exceptional_divisor_on_X + + ex_div_list = exceptional_divisor_list(f) + C = CartierDivisor(scheme(ex_div_list[1]),ZZ) + for i in 1:length(ex_div_list) +# do we want to introduce is_empty for divisors? + dim(ideal_sheaf(ex_div_list[i]))== -1 && continue # kick out empty ones + # caution: dim(CartierDivisor) is not computed, + # but inferred + # ==> need to pass to ideal_sheaf first + C = C + cartier_divisor(ex_div_list[i]) + end + + f.exceptional_divisor_on_X = C + return C +end + +function exceptional_divisor(f::MixedBlowUpSequence) + !isdefined(f,:exceptional_divisor_on_X) || return f.exceptional_divisor_on_X + f.resolves_sing || error("exceptional locus need not be a divisor for intermediate steps -- use exceptional_locus") + + return _exceptional_divisor_non_embedded(f) +end + +function exceptional_locus(f::MixedBlowUpSequence) + !isdefined(f,:exceptional_locus) || return f.exceptional_locus + + ex_div_list = exceptional_divisor_list(f) # these are IdealSheaves for MixedBlowUpSequences + # they might even have the wrong dimension + C = AlgebraicCycle(scheme(ex_div_list[1]),ZZ) # ==> we cannot expect to obtain a divisor, only a cycle + for E in ex_div_list + dim(E) != -1 || continue # kick out empty ones + C = C + algebraic_cycle(E) + end + + return C +end + ################################################################################################## # setting values in DesingMors -- Watch out: only place with direct access to fields!!! ################################################################################################## @@ -64,39 +306,87 @@ function add_map!(f::BlowUpSequence, phi::BlowupMorphism) ex_div = [strict_transform(phi,E) for E in f.ex_div[1:end]] push!(ex_div, exceptional_divisor(phi)) f.ex_div = ex_div + if isdefined(f, :underlying_morphism) + f.underlying_morphism = CompositeCoveredSchemeMorphism(reverse(morphisms(f))) + end return f end -function initialize_blow_up_sequence(phi::BlowupMorphism) - f = BlowUpSequence([phi]) - f.ex_div = [exceptional_divisor(phi)] - f.is_trivial = is_one(center(phi)) - f.resolves_sing = false # we have no information, wether we are done - # without further computation - f.is_embedded = false +function add_map!(f::MixedBlowUpSequence, phi::BlowupMorphism) + push!(f.maps, phi) + ex_div = (AbsIdealSheaf)[strict_transform(phi,E) for E in f.ex_div] + push!(ex_div, ideal_sheaf(exceptional_divisor(phi))) + f.ex_div = ex_div + if isdefined(f, :underlying_morphism) + f.underlying_morphism = CompositeCoveredSchemeMorphism(reverse(morphisms(f))) + end + return f +end + +function add_map!(f::MixedBlowUpSequence, phi::NormalizationMorphism) + push!(f.maps, phi) + sl = ideal_sheaf_of_singular_locus(codomain(phi)) + ex_div = (AbsIdealSheaf)[pullback(phi,E) for E in exceptional_divisorlist(f,true)] + push!(ex_div,pullback(phi, sl)) + f.ex_div = ex_div + push!(f.normalization_steps,length(f.maps)) + if isdefined(f, :underlying_morphism) + f.underlying_morphism = CompositeCoveredSchemeMorphism(reverse(morphisms(f))) + end return f end function add_map_embedded!(f::BlowUpSequence, phi::BlowupMorphism) push!(f.maps, phi) - ex_div = typeof(f.ex_div[1])[strict_transform(phi, E) for E in f.ex_div[1:end]] + + strict_list = [strict_transform_with_multiplicity(phi,E) for E in f.ex_div] + ex_div = [a[1] for a in strict_list] push!(ex_div, exceptional_divisor(phi)) f.ex_div = ex_div + + excess_mult = sum(a[2] for a in strict_list) if f.transform_type == :strict X_strict, inc_strict,_ = strict_transform(phi, f.embeddings[end]) push!(f.embeddings, inc_strict) elseif f.transform_type == :weak I_trans,b = weak_transform_with_multiplicity(phi, f.controlled_transform) - push!(f.ex_mult,b) + push!(f.ex_mult,b + excess_mult) f.controlled_transform = I_trans + f.control = b else - I_trans = controlled_transform(phi, f.controlled_transform, f.ex_mult[end]) + I_trans = controlled_transform(phi, f.controlled_transform, f.control) f.controlled_transform = I_trans push!(f.ex_mult, f.ex_mult[end]) end + if isdefined(f, :underlying_morphism) + f.underlying_morphism = CompositeCoveredSchemeMorphism(reverse(morphisms(f))) + end return f end +function initialize_blow_up_sequence(phi::BlowupMorphism) + f = BlowUpSequence([phi]) + f.ex_div = [exceptional_divisor(phi)] + f.is_trivial = is_one(center(phi)) + f.resolves_sing = false # we have no information, wether we are done + # without further computation + f.is_embedded = false + return f +end + +function initialize_mixed_blow_up_sequence(phi::NormalizationMorphism, I::AbsIdealSheaf) + f = MixedBlowUpSequence([phi]) + f.ex_div = [pullback(phi,I)] + f.is_trivial = is_one(I) + f.resolves_sing = false # we have no information, wether we are done + # without further computation + return f +end + +function initialize_mixed_blow_up_sequence(phi::BlowupMorphism) + return mixed_blow_up_sequence(initialize_blow_up_sequence(phi)) +end + function initialize_embedded_blowup_sequence(phi::BlowupMorphism, inc::CoveredClosedEmbedding) f = BlowUpSequence([phi]) f.ex_div = [exceptional_divisor(phi)] @@ -111,7 +401,7 @@ function initialize_embedded_blowup_sequence(phi::BlowupMorphism, inc::CoveredCl else f.is_trivial = true f.embeddings = [inc, inc] - f.resolves_sing = false + f.resolves_sing = false # should be set elsewhere end return f end @@ -129,8 +419,9 @@ function initialize_embedded_blowup_sequence(phi::BlowupMorphism, I::AbsIdealShe I_trans = controlled_transform(phi, I, b) f.transform_type = :controlled end - f.controlled_transform = I_trans # CAUTION: b is considered set once and for all + f.controlled_transform = I_trans f.ex_mult = [b] + f.control = b f.resolves_sing = false # we have no information, whether we are done # without further computation else @@ -138,16 +429,40 @@ function initialize_embedded_blowup_sequence(phi::BlowupMorphism, I::AbsIdealShe f.controlled_transform = I f.transform_type = :weak f.ex_mult = [0] - f.resolves_sing = false + f.resolves_sing = false # should be set elsewhere end return f end +function forget_embedding(f::BlowUpSequence) +## create new BlowUpSequence, where maps contains maps between the domains of f +## - set is_embedded to false +## - inherit resolves_sing, is_trivial +## - new ex_div is induced divisor arising from f.ex_div on domain(f.maps[end]) +## forget last blow-ups arising after the X has become smooth + error("not implemented yet") +end + +function mixed_blow_up_sequence(f::BlowUpSequence) + phi = MixedBlowUpSequence(morphisms(f)) + phi.resolves_sing = f.resolves_sing + phi.is_trivial = f.is_trivial + phi.ex_div = [ideal_sheaf(E) for E in f.ex_div] + phi.normalization_steps = Vector{Int}[] + if isdefined(f, :underlying_morphism) + phi.underlying_morphism = f.underlying_morphism + end + if isdefined(f, :exceptional_divisor) + phi.exceptional_divisor = f.exceptional_divisor + end + return phi +end + ################################################################################################## # desingularization workers ################################################################################################## -function embedded_desingularization(f::Oscar.CoveredClosedEmbedding; algorithm::Symbol=:BEV) +function embedded_desingularization(f::CoveredClosedEmbedding; algorithm::Symbol=:BEV) I_sl = ideal_sheaf_of_singular_locus(domain(f)) ## trivial case: domain(f) was already smooth @@ -191,27 +506,66 @@ function desingularization(X::AbsCoveredScheme; algorithm::Symbol=:Lipman) return_value = BlowUpSequence(maps) return_value.resolves_sing = true return_value.is_trivial = true - return return_value + return mixed_blow_up_sequence(return_value) end + Xnorm, phi = normalization(X) + incs = phi.inclusions + f = initialize_mixed_blow_up_sequence(phi,I_sl) + I_sl = ideal_sheaf_of_singular_locus(Xnorm) + ## I_sl non-empty, we need to do something # here the keyword algorithm ensures that the desired method is called dimX = dim(X) if dimX == 1 - return _desing_curve(X, I_sl) + return_value = f + return_value.resolves_sing = true + elseif ((dimX == 2) && (algorithm==:Lipman)) + return_value = _desing_lipman(Xnorm, I_sl, f) + elseif ((dimX == 2) && (algorithm==:Jung)) + error("not implemented yet") +# second_seq = _desing_jung(Xnorm,f) +# return_value = extend(phi, second_seq) + else + error("not implemented yet") +# second_seq = forget_embedding(_desing_BEV(Xnorm)) +# return_value = extend(phi, second_seq) end -# if ((dimX == 2) && (algorithm==:Lipman)) -# error("not implemented yet") -# return_value = _desing_lipman(X, I_sl) -# return return_value -# end -# if ((dimX == 2) && (algorithm==:Jung)) + + return return_value +end + +function desingularization_only_blowups(X::AbsCoveredScheme; algorithm::Symbol=:Lipman) + I_sl = ideal_sheaf_of_singular_locus(X) + + ## trivial case: X is already smooth + if is_one(I_sl) + id_X = identity_blow_up(X) + maps = [id_X] + return_value = BlowUpSequence(maps) + return_value.resolves_sing = true + return_value.is_trivial = true + return mixed_blow_up_sequence(return_value) + end + + + ## I_sl non-empty, we need to do something + # here the keyword :algorithm ensures that the desired method is called + dimX = dim(X) + if dimX == 1 + return_value = _desing_curve(X, I_sl) +# elseif ((dimX == 2) && (algorithm==:Jung)) # error("not implemented yet") # return_value = _desing_jung(X) -# end - error("not implemented yet") + else + error("not implemented yet") + return_value = forget_embedding(_desing_BEV(X)) + end + + return return_value end + function desingularization(X::AbsAffineScheme; algorithm::Symbol=:BEV) return desingularization(CoveredScheme(X); algorithm) end @@ -241,6 +595,40 @@ function _desing_curve(X::AbsCoveredScheme, I_sl::AbsIdealSheaf) return phi end +function _desing_lipman(X::AbsCoveredScheme, I_sl::AbsIdealSheaf, f::MixedBlowUpSequence) + dim(X) == 2 || error("Lipman's algorithm is not applicable") + + if dim(I_sl) == 1 # called for a non-normal X + Xnorm, phi = normalization(X) + incs = phi.inclusions + f = initialize_mixed_blow_up_sequence(phi,I_sl) + I_sl_temp = ideal_sheaf_of_singular_locus(Xnorm) + else + I_sl_temp = I_sl + end + + decomp = maximal_associated_points(I_sl_temp) + + while !is_one(I_sl_temp) + while length(decomp) > 0 + I = small_generating_set(pop!(decomp)) + f = _do_blow_up!(f,I) + if length(decomp)>0 + decomp = [strict_transform(last_map(f),J) for J in decomp] + end + end + I_sl_temp = ideal_sheaf_of_singular_locus(domain(last_map(f))) + if dim(I_sl_temp) == 1 + f = _do_normalization!(f) + I_sl_temp = ideal_sheaf_of_singular_locus(domain(last_map(f))) + end + decomp = maximal_associated_points(I_sl_temp) + end + + f.resolves_sing = true + return f +end + function _desing_emb_curve(f::CoveredClosedEmbedding, I_sl::AbsIdealSheaf) ## note: I_sl not unit_ideal_sheaf, because this has been caught before in embedded_desingularization(f) decomp = maximal_associated_points(pushforward(f)(I_sl)) @@ -318,7 +706,6 @@ function _ensure_ncr!(f::AbsDesingMor) return f end - function _do_blow_up!(f::AbsDesingMor, cent::AbsIdealSheaf) old_sequence = morphisms(f) X = domain(old_sequence[end]) @@ -328,6 +715,12 @@ function _do_blow_up!(f::AbsDesingMor, cent::AbsIdealSheaf) return(f) end +function _do_normalization!(f::MixedBlowUpSequence) + Xnorm, phi = normalization(domain(last_map(f))) + add_map!(f,phi) + return f +end + function _do_blow_up_embedded!(phi::AbsDesingMor,cent::AbsIdealSheaf) old_sequence = morphisms(phi) X = domain(old_sequence[end]) @@ -474,7 +867,7 @@ end # locus of order at least b and of maximal order ################################################################################################## -function max_order_locus(I::AbsIdealSheaf) +function locus_of_maximal_order(I::AbsIdealSheaf) return _delta_list(I)[end] end @@ -729,38 +1122,31 @@ is_graded(R::Ring) = false # experimental/Schemes/BlowupMorphism.jl ######################################################################## -function exceptional_divisor(phi::BlowUpSequence) - #TODO: Check whether the full resolution has already been computed? - if !isdefined(phi, :exceptional_divisor) - phi.exceptional_divisor = sum(phi.ex_div; init=CartierDivisor(domain(phi), ZZ)) - end - return phi.exceptional_divisor -end - -function exceptional_locus(phi::BlowUpSequence) - if !isdefined(phi, :exceptional_locus) - phi.exceptional_locus = weil_divisor(exceptional_divisor(phi)) - end - return phi.exceptional_locus -end - # The following two methods will be ambiguous in general # so we need to repeat the procedure for the specific types # of the second argument. -function strict_transform(phi::BlowUpSequence, a::Any) - for psi in morhpisms(phi) +function strict_transform(phi::Union{BlowUpSequence,MixedBlowUpSequence}, a::Any) + for psi in morphisms(phi) a = strict_transform(psi, a) end return a end -function total_transform(phi::BlowUpSequence, a::Any) +function total_transform(phi::Union{BlowUpSequence,MixedBlowUpSequence}, a::Any) for psi in morphisms(phi) a = total_transform(psi, a) end return a end +function total_transform(phi::NormalizationMorphism, a::Any) + return pullback(phi,a) +end + +function strict_transform(phi::NormalizationMorphism, a::Any) + return pullback(phi,a) +end + function strict_transform(phi::BlowUpSequence, a::AbsIdealSheaf) for psi in morphisms(phi) a = strict_transform(psi, a) @@ -775,7 +1161,12 @@ function total_transform(phi::BlowUpSequence, a::AbsIdealSheaf) return a end -function center(phi::BlowUpSequence) +function last_center(phi::BlowUpSequence) return center(last_map(phi)) end +function last_center(phi::MixedBlowUpSequence) + lm = last_map(phi) + lm isa BlowupMorphism || return unit_ideal_sheaf(codomain(lm)) + return center(lm) +end diff --git a/experimental/Schemes/src/Schemes.jl b/experimental/Schemes/src/Schemes.jl index ccf178ef9fe2..cc4c96f745fb 100644 --- a/experimental/Schemes/src/Schemes.jl +++ b/experimental/Schemes/src/Schemes.jl @@ -16,7 +16,7 @@ include("CoherentSheaves.jl") include("LazyGluing.jl") include("CartierDivisor.jl") include("Auxiliary.jl") -include("../BlowupMorphism.jl") # TODO: move this file +include("BlowupMorphism.jl") include("duValSing.jl") include("elliptic_surface.jl") include("MorphismFromRationalFunctions.jl") @@ -38,7 +38,7 @@ include("ToricBlowups/attributes.jl") include("ToricBlowups/methods.jl") include("DerivedPushforward.jl") -include("../Resolution_structure.jl") # TODO: move this file +include("Resolution_structure.jl") # Exports export CompleteIntersectionGerm diff --git a/experimental/Schemes/src/Sheaves.jl b/experimental/Schemes/src/Sheaves.jl index 5edc24fca55f..7b80f3386197 100644 --- a/experimental/Schemes/src/Sheaves.jl +++ b/experimental/Schemes/src/Sheaves.jl @@ -190,10 +190,10 @@ function restriction_map(F::PreSheafOnScheme{<:Any, OpenType, OutputType, Restri end @doc raw""" - add_incoming_restriction!(F::AbsPreSheaf{<:Any, OpenType, <:Any, RestrictionType}, - U::OpenType, - rho::RestrictionType - ) where {OpenType, RestrictionType} + add_incoming_restriction!(F::AbsPreSheaf{<:Any, OpenType, <:Any, RestrictionType}, + U::OpenType, + rho::RestrictionType + ) where {OpenType, RestrictionType} **Note:** This is a method for internal use! diff --git a/experimental/Schemes/src/SpaceGerms.jl b/experimental/Schemes/src/SpaceGerms.jl index 6ec9e45c0667..f0b6927de753 100644 --- a/experimental/Schemes/src/SpaceGerms.jl +++ b/experimental/Schemes/src/SpaceGerms.jl @@ -1004,7 +1004,7 @@ end # We want the singular locus of an `AbsSpaceGerm` to be a `SpaceGerm` again and # not a plain `AffineScheme`. @doc raw""" - singular_locus(X::AbsSpaceGerm) --> SpaceGerm, ClosedEmbedding + singular_locus(X::AbsSpaceGerm) --> SpaceGerm, ClosedEmbedding Return the space germ (Y,p) for a given germ (X,p) and the closed embedding of (Y,p) into (X,p), where Y is the singular locus of X. diff --git a/experimental/Schemes/src/ToricIdealSheaves/constructors.jl b/experimental/Schemes/src/ToricIdealSheaves/constructors.jl index 50e0ae2b625e..6b043f367260 100644 --- a/experimental/Schemes/src/ToricIdealSheaves/constructors.jl +++ b/experimental/Schemes/src/ToricIdealSheaves/constructors.jl @@ -104,7 +104,7 @@ function IdealSheaf(X::NormalToricVariety, I::MPolyIdeal) indices = [k for k in row(IM, k)] help_ring, x_rho = polynomial_ring(QQ, ["x_$j" for j in indices]) imgs_phi_star = [j in indices ? x_rho[findfirst(k->k==j, indices)] : one(help_ring) for j in 1:n_rays(X)] - phi_s_star = hom(cox_ring(X), help_ring, imgs_phi_star) + phi_s_star = hom(cox_ring(X), help_ring, imgs_phi_star; check=false) # Now we need to create the inverse of alpha*. imgs_alpha_star = elem_type(help_ring)[] @@ -117,7 +117,7 @@ function IdealSheaf(X::NormalToricVariety, I::MPolyIdeal) end push!(imgs_alpha_star, img) end - alpha_star = hom(OO(U), help_ring, imgs_alpha_star) + alpha_star = hom(OO(U), help_ring, imgs_alpha_star; check=false) # TODO: There should be better ways to create this map! # Presumably, one can invert the matrix with the `expo`s as entries? @@ -245,7 +245,7 @@ function _dehomogenize_to_chart(X::NormalToricVariety, I::MPolyIdeal, k::Int) indices = [k for k in row(IM, k)] help_ring, x_rho = polynomial_ring(QQ, ["x_$j" for j in indices]) imgs_phi_star = [j in indices ? x_rho[findfirst(k->k==j, indices)] : one(help_ring) for j in 1:n_rays(X)] - phi_s_star = hom(S_loc, help_ring, imgs_phi_star) + phi_s_star = hom(S_loc, help_ring, imgs_phi_star; check=false) # Compute the preimage of I_loc under beta. # However, preimage is not implemented for these maps: diff --git a/experimental/Schemes/src/Types.jl b/experimental/Schemes/src/Types.jl index 165cb1605e29..fb53fff66709 100644 --- a/experimental/Schemes/src/Types.jl +++ b/experimental/Schemes/src/Types.jl @@ -503,6 +503,81 @@ end underlying_presheaf(I::SingularLocusIdealSheaf) = I.underlying_presheaf focus(I::SingularLocusIdealSheaf) = I.focus +######################################################################## +# Closed embeddings # +######################################################################## + +@attributes mutable struct CoveredClosedEmbedding{ + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + BaseMorphismType + } <: AbsCoveredSchemeMorphism{ + DomainType, + CodomainType, + BaseMorphismType, + CoveredSchemeMorphism + } + f::CoveredSchemeMorphism + I::AbsIdealSheaf + + function CoveredClosedEmbedding( + X::DomainType, + Y::CodomainType, + f::CoveringMorphism{<:Any, <:Any, MorphismType, BaseMorType}; + check::Bool=true, + ideal_sheaf::AbsIdealSheaf=IdealSheaf(Y, f, check=check) + ) where { + DomainType<:AbsCoveredScheme, + CodomainType<:AbsCoveredScheme, + MorphismType<:ClosedEmbedding, + BaseMorType + } + ff = CoveredSchemeMorphism(X, Y, f; check) + if has_decomposition_info(codomain(f)) + for U in patches(domain(f)) + floc = f[U] + phi = pullback(floc) + V = codomain(floc) + g = Vector{elem_type(OO(V))}(decomposition_info(codomain(f))[V]) + set_decomposition_info!(domain(f), U, Vector{elem_type(OO(U))}(phi.(g))) + end + end + #all(x->(x isa ClosedEmbedding), values(morphisms(f))) || error("the morphisms on affine patches must be `ClosedEmbedding`s") + return new{DomainType, CodomainType, BaseMorType}(ff, ideal_sheaf) + end +end + +######################################################################## +# PushforwardIdealSheaf # +######################################################################## +@attributes mutable struct PushforwardIdealSheaf{SpaceType, OpenType, OutputType, + RestrictionType + } <: AbsIdealSheaf{ + SpaceType, OpenType, + OutputType, RestrictionType + } + f::CoveredClosedEmbedding + orig::AbsIdealSheaf + Ipre::PreSheafOnScheme + + function PushforwardIdealSheaf( + f::CoveredClosedEmbedding, + orig::AbsIdealSheaf + ) + X = domain(f) + Y = codomain(f) + @assert X === scheme(orig) + + Ipre = PreSheafOnScheme(Y, + OpenType=AbsAffineScheme, OutputType=Ideal, + RestrictionType=Map, + is_open_func=_is_open_func_for_schemes_without_affine_scheme_open_subscheme(X) + ) + I = new{typeof(Y), AbsAffineScheme, Ideal, Map}(f, orig, Ipre) + return I + end +end + ######################################################################## # Morphisms from rational functions # ######################################################################## diff --git a/experimental/Schemes/src/elliptic_surface.jl b/experimental/Schemes/src/elliptic_surface.jl index a406afc0cca8..7a8533ecf6e0 100644 --- a/experimental/Schemes/src/elliptic_surface.jl +++ b/experimental/Schemes/src/elliptic_surface.jl @@ -1407,7 +1407,7 @@ function horizontal_decomposition(X::EllipticSurface, F::Vector{QQFieldElem}) end @doc raw""" - elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) -> LinearSystem + elliptic_parameter(X::EllipticSurface, F::Vector{QQFieldElem}) -> LinearSystem Return the elliptic parameter ``u`` of the divisor class `F`. @@ -1445,7 +1445,7 @@ end @doc raw""" - extended_ade(ADE::Symbol, n::Int) + 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). diff --git a/experimental/StandardFiniteFields/docs/src/introduction.md b/experimental/StandardFiniteFields/docs/src/introduction.md index 93cb7591c360..9f851996e4f8 100644 --- a/experimental/StandardFiniteFields/docs/src/introduction.md +++ b/experimental/StandardFiniteFields/docs/src/introduction.md @@ -7,5 +7,5 @@ end ```@docs - standard_finite_field(p::IntegerUnion, n::IntegerUnion) +standard_finite_field(p::IntegerUnion, n::IntegerUnion) ``` diff --git a/experimental/SymmetricIntersections/src/representations.jl b/experimental/SymmetricIntersections/src/representations.jl index 2f4982e702b1..db61a903ce0f 100644 --- a/experimental/SymmetricIntersections/src/representations.jl +++ b/experimental/SymmetricIntersections/src/representations.jl @@ -234,7 +234,7 @@ end Given a class function `chi` on a group `G`, return whether `chi` defines a character of `G` (over its codomain). """ -is_character(chi::Oscar.GAPGroupClassFunction) = GG.IsCharacter(GAPTable(parent(chi)), chi.values)::Bool +is_character(chi::Oscar.GAPGroupClassFunction) = GG.IsCharacter(GapObj(parent(chi)), GapObj(chi))::Bool @doc raw""" is_constituent(chi::T, nu::T) where T <: Oscar.GAPGroupClassFunction -> Bool @@ -502,12 +502,12 @@ function irreducible_affording_representation(RR::RepRing{S, T}, chi::Oscar.GAPG H = generators_underlying_group(RR) # the GAP function which we rely on - rep = GG.IrreducibleAffordingRepresentation(chi.values)::GAP.GapObj + rep = GG.IrreducibleAffordingRepresentation(GapObj(chi))::GapObj # we compute certain images than we will convert then. # we use them then to construct the mapping in # `_linear_representation` - _Mat = GAP.GapObj[GG.Image(rep, h.X) for h in H] + _Mat = GapObj[GG.Image(rep, h.X) for h in H] Mat = AbstractAlgebra.Generic.MatSpaceElem{elem_type(F)}[] for _M in _Mat rM = eltype(Mat)[transpose(matrix(F.(m))) for m in _M] @@ -630,7 +630,7 @@ function is_faithful(chi::Oscar.GAPGroupClassFunction, p::GAPGroupHomomorphism{T E = group(parent(chi))::T @req E === domain(p) "Incompatible underlying group of chi and domain of the cover p" @req is_projective(chi, p) "chi is not afforded by a p-projective representation" - Z = center(chi)[1]::T + Z = center(chi)[1] Q = kernel(p)[1] return Q.X == Z.X end @@ -1060,18 +1060,18 @@ end function _has_pfr(G::Oscar.GAPGroup, dim::Int) # we start by computing a Schur cover and we turn it into an Oscar object G_gap = G.X - f_gap = GG.EpimorphismSchurCover(G_gap)::GAP.GapObj - H_gap = GG.Source(f_gap)::GAP.GapObj + f_gap = GG.EpimorphismSchurCover(G_gap)::GapObj + H_gap = GG.Source(f_gap)::GapObj n, p = is_power(GG.Size(H_gap))::Tuple{Int, Int} if is_prime(p) - fff_gap = GG.EpimorphismPGroup(H_gap, p)::GAP.GapObj - E_gap = fff_gap(H_gap)::GAP.GapObj + fff_gap = GG.EpimorphismPGroup(H_gap, p)::GapObj + E_gap = fff_gap(H_gap)::GapObj else - fff_gap = GG.IsomorphismPermGroup(H_gap)::GAP.GapObj - E_gap = fff_gap(H_gap)::GAP.GapObj + fff_gap = GG.IsomorphismPermGroup(H_gap)::GapObj + E_gap = fff_gap(H_gap)::GapObj end - E = Oscar._get_type(E_gap)(E_gap) - H = Oscar._get_type(H_gap)(H_gap) + E = Oscar._oscar_group(E_gap) + H = Oscar._oscar_group(H_gap) fff = inv(GAPGroupHomomorphism(H, E, fff_gap)) f = GAPGroupHomomorphism(H, G, f_gap) pschur = compose(fff, f) diff --git a/gap/OscarInterface/gap/OscarInterface.gd b/gap/OscarInterface/gap/OscarInterface.gd index 28b994284bce..22561e85104f 100644 --- a/gap/OscarInterface/gap/OscarInterface.gd +++ b/gap/OscarInterface/gap/OscarInterface.gd @@ -18,6 +18,12 @@ BindGlobal("IsPcGroupOrPcpGroup", IsGroup and CategoryCollections(IsPcElementOrP ############################################################################ +# Use a GAP property for caching whether a fp/pc/pcp group is a full group +# and its stored generators are the generators for the defining presentation. +DeclareProperty( "GroupGeneratorsDefinePresentation", IsGroup ); + +############################################################################ + # Use GAP operations for the serialization of GAP objects. # (The methods will be Julia functions.) DeclareOperation( "SerializeInOscar", [ IsObject, IsObject ] ); diff --git a/gap/OscarInterface/gap/OscarInterface.gi b/gap/OscarInterface/gap/OscarInterface.gi index 75cd8e3ccc66..de92461e95ac 100644 --- a/gap/OscarInterface/gap/OscarInterface.gi +++ b/gap/OscarInterface/gap/OscarInterface.gi @@ -101,6 +101,32 @@ InstallMethod( IsGeneratorsOfMagmaWithInverses, ############################################################################ +InstallMethod( GroupGeneratorsDefinePresentation, + [ "IsPcGroup" ], + G -> GeneratorsOfGroup(G) = FamilyPcgs(G) ); + +InstallMethod( GroupGeneratorsDefinePresentation, + [ "IsPcpGroup" ], + function( G ) + local n, Ggens, i, w; + + n:= One( G )!.collector![ PC_NUMBER_OF_GENERATORS ]; + Ggens:= GeneratorsOfGroup( G ); + if Length( Ggens ) <> n then + return false; + fi; + for i in [ 1 .. n ] do + w:= Ggens[i]!.word; + if not ( Length(w) = 2 and w[1] = i and w[2] = 1 ) then + return false; + fi; + od; + return true; + end ); + +############################################################################ + + Perform( Oscar._GAP_serializations, function( entry ) InstallMethod( SerializeInOscar, diff --git a/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.jl b/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.jl index 02137a723a39..c9b33daecdcb 100644 --- a/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.jl +++ b/src/AlgebraicGeometry/Curves/ParametrizationPlaneCurves.jl @@ -28,8 +28,7 @@ end function _tosingular_ideal(C::ProjectiveCurve) I = vanishing_ideal(C) # computes a radical. Do we want this? - singular_assure(I) - return I.gens.S + return singular_generators(I) end @doc raw""" @@ -358,8 +357,7 @@ representatives of elements in `R/image`, where `R` is the basering. function invert_birational_map(phi::Vector{T}, C::ProjectivePlaneCurve) where {T <: MPolyRingElem} S = parent(phi[1]) I = ideal(S, phi) - singular_assure(I) - L = Singular.LibParaplanecurves.invertBirMap(I.gens.S, _tosingular(C)) + L = Singular.LibParaplanecurves.invertBirMap(singular_generators(I), _tosingular(C)) R = _fromsingular_ring(L[1]) J = L[2][:J] psi = L[2][:psi] diff --git a/src/AlgebraicGeometry/Curves/ProjectiveCurve.jl b/src/AlgebraicGeometry/Curves/ProjectiveCurve.jl index ad36089fef7d..5a55947870dd 100644 --- a/src/AlgebraicGeometry/Curves/ProjectiveCurve.jl +++ b/src/AlgebraicGeometry/Curves/ProjectiveCurve.jl @@ -73,10 +73,8 @@ representatives of elements in `R/image`, where `R` is the basering. function invert_birational_map(phi::Vector{T}, C::ProjectiveCurve) where {T <: MPolyRingElem} s = parent(phi[1]) I = ideal(s, phi) - singular_assure(I) IC = defining_ideal(C) - singular_assure(IC) - L = Singular.LibParaplanecurves.invertBirMap(I.gens.S, IC.gens.S) + L = Singular.LibParaplanecurves.invertBirMap(singular_generators(I), singular_generators(IC)) R = _fromsingular_ring(L[1]) J = L[2][:J] psi = L[2][:psi] diff --git a/src/AlgebraicGeometry/Curves/ProjectivePlaneCurve.jl b/src/AlgebraicGeometry/Curves/ProjectivePlaneCurve.jl index fb6c4ae56d9d..f78bf8d709ac 100644 --- a/src/AlgebraicGeometry/Curves/ProjectivePlaneCurve.jl +++ b/src/AlgebraicGeometry/Curves/ProjectivePlaneCurve.jl @@ -122,7 +122,7 @@ end # multiplicity @doc raw""" - multiplicity(C::ProjectivePlaneCurve{S}, P::AbsProjectiveRationalPoint) + multiplicity(C::ProjectivePlaneCurve{S}, P::AbsProjectiveRationalPoint) Return the multiplicity of `C` at `P`. """ @@ -155,8 +155,8 @@ end ################################################################################ # tangent lines - @doc raw""" - tangent_lines(C::ProjectivePlaneCurve{S}, P::AbsProjectiveRationalPoint) where S <: FieldElem +@doc raw""" + tangent_lines(C::ProjectivePlaneCurve{S}, P::AbsProjectiveRationalPoint) where S <: FieldElem Return the tangent lines at `P` to `C` with their multiplicity. """ @@ -177,7 +177,7 @@ function tangent_lines(C::ProjectivePlaneCurve, P::AbsProjectiveRationalPoint) end @doc raw""" - intersection_multiplicity(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: ProjectivePlaneCurve + intersection_multiplicity(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: ProjectivePlaneCurve Return the intersection multiplicity of `C` and `D` at `P`. """ @@ -193,7 +193,7 @@ end @doc raw""" - is_transverse_intersection(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: ProjectivePlaneCurve + is_transverse_intersection(C::S, D::S, P::AbsProjectiveRationalPoint) where S <: ProjectivePlaneCurve Return `true` if `C` and `D` intersect transversally at `P` and `false` otherwise. """ diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Morphisms/Methods.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Morphisms/Methods.jl index 33b2ab895606..3d1eeac5fe2a 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Morphisms/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Morphisms/Methods.jl @@ -191,9 +191,11 @@ end ### Some helper functions function _restrict_domain(f::AbsAffineSchemeMor, D::PrincipalOpenSubset; check::Bool=true) D === domain(f) && return f - ambient_scheme(D) === domain(f) && return morphism(D, codomain(f), OO(D).(pullback(f).(gens(OO(codomain(f)))); check=false), check=false) + !_has_coefficient_map(pullback(f)) && ambient_scheme(D) === domain(f) && return morphism(D, codomain(f), OO(D).(pullback(f).(gens(OO(codomain(f)))); check=false), check=false) + @check is_subscheme(D, domain(f)) "domain incompatible" - return morphism(D, codomain(f), [OO(D)(x; check) for x in pullback(f).(gens(OO(codomain(f))))], check=check) + !_has_coefficient_map(pullback(f)) && return morphism(D, codomain(f), [OO(D)(x; check) for x in pullback(f).(gens(OO(codomain(f))))], check=check) + return morphism(D, codomain(f), coefficient_map(pullback(f)), [OO(D)(x; check) for x in pullback(f).(gens(OO(codomain(f))))], check=check) end function _restrict_domain(f::AbsAffineSchemeMor, D::AbsAffineScheme; check::Bool=true) @@ -212,7 +214,22 @@ function _restrict_codomain(f::AbsAffineSchemeMor, D::PrincipalOpenSubset; check @check is_subscheme(D, codomain(f)) "codomain incompatible" @check is_subscheme(domain(f), preimage(f, D)) !_has_coefficient_map(pullback(f)) && return morphism(domain(f), D, OO(domain(f)).(pullback(f).(gens(OO(codomain(f)))); check=false), check=check) - return morphism(domain(f), D, [OO(domain(f))(x; check) for x in pullback(f).(gens(OO(codomain(f))))], check=check) + return morphism(domain(f), D, coefficient_map(pullback(f)), [OO(domain(f))(x; check) for x in pullback(f).(gens(OO(codomain(f))))], check=check) +end + +# Some missing constructors +morphism(A::AbsAffineScheme, B::AbsAffineScheme, coeff_map::Any, img_gens::Vector; check::Bool=true) = morphism(A, B, hom(OO(B), OO(A), coeff_map, img_gens; check); check) + +function hom(L::MPolyLocRing, P::NCRing, coeff_map::Any, img_gens::Vector; check::Bool=true) + R = base_ring(L) + phi = hom(R, P, coeff_map, img_gens; check) + return MPolyLocalizedRingHom(L, P, phi; check) +end + +function hom(L::MPolyQuoLocRing, P::NCRing, coeff_map::Any, img_gens::Vector; check::Bool=true) + R = base_ring(L) + phi = hom(R, P, coeff_map, img_gens; check) + return MPolyQuoLocalizedRingHom(L, P, phi; check) end function _restrict_codomain(f::AbsAffineSchemeMor, D::AbsAffineScheme; check::Bool=true) @@ -483,6 +500,24 @@ function base_change(phi::Any, f::AbsAffineSchemeMor; return domain_map, morphism(XX, YY, pbF, check=false), codomain_map end +function base_change(phi::Any, f::ClosedEmbedding; + domain_map::AbsAffineSchemeMor=base_change(phi, domain(f))[2], + codomain_map::AbsAffineSchemeMor=base_change(phi, codomain(f))[2] + ) + @assert codomain(codomain_map) === codomain(f) + @assert codomain(domain_map) === domain(f) + g = underlying_morphism(f) + _, bc_g, _ = base_change(phi, g; domain_map, codomain_map) + I = image_ideal(f) + @assert base_ring(I) === OO(codomain(f)) + @assert base_ring(I) === OO(codomain(f)) + #bc_I = ideal(OO(codomain(bc_g)), pullback(codomain_map).(gens(I))) + bc_I = pullback(codomain_map)(I) + @assert domain(bc_g) === domain(domain_map) + @assert codomain(bc_g) === domain(codomain_map) + return domain_map, ClosedEmbedding(bc_g, bc_I; check=false), codomain_map +end + function _register_birationality!(f::AbsAffineSchemeMor, g::AbsAffineSchemeMor, ginv::AbsAffineSchemeMor) set_attribute!(g, :inverse, ginv) diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl index 105df2b1ae0e..412bac214619 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Attributes.jl @@ -527,7 +527,7 @@ end # TODO: projective schemes, covered schemes @doc raw""" - reduced_scheme(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) + reduced_scheme(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) Return the induced reduced scheme of `X`. diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl index 432b9e540150..51d51eeb2371 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Methods.jl @@ -418,7 +418,7 @@ function base_change(phi::Any, X::AbsAffineScheme) R = OO(X) R_red, Phi = _change_base_ring(phi, R) Y = spec(R_red) - return Y, morphism(Y, X, Phi) + return Y, morphism(Y, X, Phi; check=false) end ### Some helper functions @@ -426,7 +426,7 @@ function _change_base_ring(phi::Any, R::MPolyRing) K = coefficient_ring(R) kk = parent(phi(zero(K))) P, _ = polynomial_ring(kk, symbols(R)) - Phi = hom(R, P, phi, gens(P)) + Phi = hom(R, P, phi, gens(P); check=false) return P, Phi end diff --git a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Properties.jl b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Properties.jl index bfd93ca8c8f0..18b8713dc646 100644 --- a/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Properties.jl +++ b/src/AlgebraicGeometry/Schemes/AffineSchemes/Objects/Properties.jl @@ -527,7 +527,7 @@ end # TODO: projective schemes, covered schemes @doc raw""" - is_equidimensional(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) + is_equidimensional(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) Check whether the scheme `X` is equidimensional. @@ -593,7 +593,7 @@ end # TODO: projective schemes @doc raw""" - is_reduced(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) + is_reduced(X::AbsAffineScheme{<:Field, <:MPolyAnyRing}) Check whether the affine scheme `X` is reduced. """ @@ -726,7 +726,7 @@ is_smooth(X::AbsAffineScheme{<:Field, <:MPolyLocRing}) = true # irreducible = nilradical of OO(X) is prime # ################################################################### @doc raw""" - is_irreducible(X::AbsAffineScheme) + is_irreducible(X::AbsAffineScheme) Check whether the affine scheme `X` is irreducible. @@ -746,7 +746,7 @@ is_irreducible(X::AbsAffineScheme{<:Field,<:MPolyRing}) = true is_irreducible(X::AbsAffineScheme{<:Field,<:MPolyLocRing}) = true @doc raw""" - is_integral(X::AbsAffineScheme) + is_integral(X::AbsAffineScheme) Check whether the affine scheme `X` is integral, i.e. irreducible and reduced. """ @@ -782,7 +782,7 @@ end # Connectedness # ################################################################### @doc raw""" - is_connected(X::AbsAffineScheme) + is_connected(X::AbsAffineScheme) Check whether the affine scheme `X` is connected. """ diff --git a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Methods.jl b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Methods.jl index b215a33aa684..15a095752157 100644 --- a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Methods.jl @@ -221,7 +221,7 @@ with default covering 2: [(x//y), (z//y)] 3: [(x//z), (y//z)] -julia> Y, pr_mor, injs = normalization(X); +julia> Y, pr_mor = normalization(X); julia> Y Scheme @@ -259,7 +259,7 @@ given by the pullback functions (x//z) -> x (y//z) -> y -julia> injs +julia> inclusion_morphisms(pr_mor) 1-element Vector{CoveredSchemeMorphism{CoveredScheme{QQField}, CoveredScheme{QQField}, AbsAffineSchemeMor}}: Hom: scheme over QQ covered with 3 patches -> scheme over QQ covered with 3 patches ``` @@ -280,7 +280,8 @@ function normalization(X::AbsCoveredScheme; check::Bool=true) merge!(pr_dict, pr_dicts...) pr_covering_mor = CoveringMorphism(default_covering(Y), default_covering(X), pr_dict; check=false) pr_mor = CoveredSchemeMorphism(Y, X, pr_covering_mor; check=false) - return Y, pr_mor, injs + pr_mor = Oscar.NormalizationMorphism(pr_mor,injs; check=false) + return Y, pr_mor end function _normalization_integral(X::AbsCoveredScheme; check::Bool=true) diff --git a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Properties.jl b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Properties.jl index da8429dafbc0..679bd696e94a 100644 --- a/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Properties.jl +++ b/src/AlgebraicGeometry/Schemes/CoveredSchemes/Objects/Properties.jl @@ -8,7 +8,7 @@ # (1) Check and store, whether a covered scheme is empty # ######################################################################## @doc raw""" - is_empty(X::AbsCoveredScheme) + is_empty(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is empty. @@ -25,7 +25,7 @@ end # (2) Check and store, whether a covered scheme is smooth # ######################################################################## @doc raw""" - is_smooth(X::AbsCoveredScheme) + is_smooth(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is smooth. """ @@ -70,7 +70,7 @@ end # i.e. irreducible and reduced # ######################################################################## @doc raw""" - is_integral(X::AbsCoveredScheme) + is_integral(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is integral. @@ -85,7 +85,7 @@ end # Note: This does not work with gluing_graph, because empty patches # need to be ignored without changing the covering @doc raw""" - is_connected_gluing(X::AbsCoveredScheme) + is_connected_gluing(X::AbsCoveredScheme) Return the boolean value whether the gluing graph of the default covering of the scheme X is connected. @@ -102,7 +102,7 @@ end # (4) Check and store, whether a covered scheme is connected # ######################################################################## @doc raw""" - is_connected(X::AbsCoveredScheme) + is_connected(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is connected. @@ -119,7 +119,7 @@ end # (5) Check and store, whether a scheme is reduced ############################################################################## @doc raw""" - is_reduced(X::AbsCoveredScheme) + is_reduced(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is reduced. @@ -132,7 +132,7 @@ end # (6) Check and store, whether a covered scheme is irreducible ############################################################################## @doc raw""" - is_irreducible(X::AbsCoveredScheme) + is_irreducible(X::AbsCoveredScheme) Return the boolean value whether a covered scheme `X` is irreducible. diff --git a/src/AlgebraicGeometry/Schemes/Covering/Morphisms/Methods.jl b/src/AlgebraicGeometry/Schemes/Covering/Morphisms/Methods.jl index 8437b3758b14..5dd01e140b5b 100644 --- a/src/AlgebraicGeometry/Schemes/Covering/Morphisms/Methods.jl +++ b/src/AlgebraicGeometry/Schemes/Covering/Morphisms/Methods.jl @@ -62,15 +62,15 @@ end ######################################################################## # Base change ######################################################################## -function base_change(phi::Any, f::CoveringMorphism; +function base_change(phi::Any, f::CoveringMorphism{<:Any, <:Any, MorphismType, BaseMorType}; domain_map::CoveringMorphism=base_change(phi, domain(f))[2], codomain_map::CoveringMorphism=base_change(phi, codomain(f))[2] - ) + ) where {MorphismType, BaseMorType} D = domain(f) C = codomain(f) DD = domain(domain_map) CC = domain(codomain_map) - mor_dict = IdDict{AbsAffineScheme, AbsAffineSchemeMor}() + mor_dict = IdDict{AbsAffineScheme, MorphismType}() for UU in patches(DD) U = codomain(domain_map[UU]) V = codomain(f[U]) diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Attributes.jl b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Attributes.jl index e23ce611e07b..427549125234 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Attributes.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Attributes.jl @@ -12,7 +12,7 @@ underlying_morphism(f::AbsProjectiveSchemeMorphism) = error("no underlying morph domain(phi::ProjectiveSchemeMor) = phi.domain codomain(phi::ProjectiveSchemeMor) = phi.codomain @doc raw""" - pullback(phi::ProjectiveSchemeMor) + pullback(phi::ProjectiveSchemeMor) For a morphism `phi` of projective schemes, this returns the associated morphism of graded affine algebras. @@ -175,3 +175,40 @@ end underlying_morphism(f::ProjectiveClosedEmbedding) = f.underlying_morphism image_ideal(f::ProjectiveClosedEmbedding) = f.ideal_of_image +######################################################################## +# Rational maps +######################################################################## + +### generic getters +domain(f::AbsRationalMap) = domain(underlying_rational_map(f)) +codomain(f::AbsRationalMap) = codomain(underlying_rational_map(f)) +pullback(f::AbsRationalMap) = pullback(underlying_rational_map(f)) +graph_ring(f::AbsRationalMap) = graph_ring(underlying_rational_map(f)) + +underlying_rational_map(f::AbsRationalMap) = error("no underlying rational map for rational map of type $(typeof(f))") + +### getters for the minimal concrete type +domain(phi::RationalMap) = phi.domain +codomain(phi::RationalMap) = phi.codomain +@doc raw""" + pullback(phi::RationalMap) + +For a rational map `phi` of projective varieties, this returns the associated +morphism of graded affine algebras. +""" +pullback(phi::RationalMap) = phi.pullback + +function graph_ring(f::RationalMap) + if !isdefined(f, :graph_ring) + pbf = pullback(f) + SY = domain(pbf) + SX = codomain(pbf) + S, inc_SX, inc_SY = tensor_product(SX, SY) + IG = ideal(S, elem_type(S)[inc_SY(y) - inc_SX(pbf(y)) for y in gens(SY)]) + SG, pr = quo(S, IG) + f.graph_ring = SG, hom(SX, SG, pr.(inc_SX.(gens(SX))); check=false), hom(SY, SG, pr.(inc_SY.(gens(SY))); check=false) + end + return f.graph_ring +end + + diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Constructors.jl b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Constructors.jl index 4bc72409e3e3..7df09ebca199 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Constructors.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Constructors.jl @@ -201,3 +201,31 @@ function base_change(phi::Any, IP::AbsProjectiveScheme) return result, psi end +######################################################################## +# Rational maps +######################################################################## +@doc raw""" + rational_map(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map; check::Bool=true ) + +Given a homomorphism ``f : T → S`` of the `homogeneous_coordinate_ring`s of `Q` and `P`, respectively, +construct the associated rational map of projective varieties. +""" +function rational_map(P::AbsProjectiveScheme, Q::AbsProjectiveScheme, f::Map; check::Bool=true ) + return RationalMap(P, Q, f, check=check) +end + +@doc raw""" + rational_map(X::AbsProjectiveScheme, Y::AbsProjectiveScheme, a::Vector{<:RingElem}) + +Suppose ``X ⊂ ℙʳ`` and ``Y ⊂ ℙˢ`` are projective varieties. +Construct the rational map associated to the morphism of graded rings +which takes the generators of the `homogeneous_coordinate_ring` of ``Y`` to the elements +in `a` of the `homogeneous_coordinate_ring` of ``X``. +""" +function rational_map(X::AbsProjectiveScheme, Y::AbsProjectiveScheme, a::Vector{<:RingElem}; check::Bool=true) + SX = homogeneous_coordinate_ring(X) + SY = homogeneous_coordinate_ring(Y) + f = hom(SY, SX, a; check) + return RationalMap(X, Y, f) +end + diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Types.jl b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Types.jl index 91ecc761de8f..eb6aab48882a 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Types.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveSchemes/Morphisms/Types.jl @@ -151,3 +151,74 @@ end end +######################################################################## +# Abstract type for rational maps of projective varieties +######################################################################## +abstract type AbsRationalMap{ + DomainType<:AbsProjectiveScheme, + CodomainType<:AbsProjectiveScheme, + SelfType, # The concrete type itself as required by the generic `Map` implementation + } <: SchemeMor{DomainType, CodomainType, + SelfType, + Nothing + } +end + +######################################################################## +# Concrete rational maps of projective varieties # +######################################################################## +@doc raw""" + RationalMap + +A rational map of projective varieties over a field 𝕜 +``` + ℙˢ ℙʳ + ∪ ∪ + P → Q +``` +given by means of a commutative diagram of homomorphisms of +their `homogeneous_coordinate_rings` +``` + 𝕜[u₀,…,uₛ]/I ← 𝕜[v₀,…,vᵣ]/J +``` +""" +@attributes mutable struct RationalMap{ + DomainType<:AbsProjectiveScheme, + CodomainType<:AbsProjectiveScheme, + PullbackType<:Map, + } <: AbsRationalMap{ + DomainType, CodomainType, + RationalMap + } + domain::DomainType + codomain::CodomainType + pullback::PullbackType + + # Fields for caching + graph_ring::Tuple{<:MPolyQuoRing, <:Map, <:Map} + + ### Simple morphism of projective schemes over the same base scheme + function RationalMap( + P::DomainType, + Q::CodomainType, + f::PullbackType; + check::Bool=true + ) where {DomainType<:AbsProjectiveScheme, + CodomainType<:AbsProjectiveScheme, + PullbackType<:Map + } + T = homogeneous_coordinate_ring(P) + S = homogeneous_coordinate_ring(Q) + (S === domain(f) && T === codomain(f)) || error("pullback map incompatible") + @check begin + is_irreducible(P) || error("domain must be irreducible") + is_irreducible(Q) || error("codomain must be irreducible") + is_reduced(P) || error("domain must be reduced") + is_reduced(Q) || error("codomain must be reduced") + true + end + return new{DomainType, CodomainType, PullbackType}(P, Q, f) + end +end + + diff --git a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl index 68e0f7ad8530..7db8c20fca6a 100644 --- a/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl +++ b/src/AlgebraicGeometry/Schemes/ProjectiveVariety/Objects/Constructors.jl @@ -13,7 +13,7 @@ end @doc raw""" - variety(I::MPolyIdeal{<:MPolyDecRingElem}; check::Bool=true, is_radical::Bool=false) -> ProjectiveVariety + variety(I::MPolyIdeal{<:MPolyDecRingElem}; check::Bool=true, is_radical::Bool=false) -> ProjectiveVariety Return the projective variety defined by the homogeneous prime ideal ``I``. diff --git a/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl b/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl index 72076138a756..9f7b4b88c227 100644 --- a/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl +++ b/src/AlgebraicGeometry/Surfaces/AdjunctionProcess/AdjunctionProcess.jl @@ -204,14 +204,13 @@ function canonical_bundle(X::AbsProjectiveVariety) n = ngens(Pn)-1 c = codim(X) FA = free_resolution(A, algorithm = :fres) - C = Oscar.SimpleComplexWrapper(FA.C[0:first(range(FA.C))]) - C_simp = simplify(C) + C_simp = simplify(FA) C_shift = shift(C_simp, c) OmegaPn = graded_free_module(Pn, [n+1]) D = hom(C_shift, OmegaPn) D_simp = simplify(D) Z, inc = kernel(D_simp, 0) - B, inc_B = Oscar.boundary(D_simp, 0) + B, inc_B = boundary(D_simp, 0) return prune_with_map(SubquoModule(D_simp[0], ambient_representatives_generators(Z), ambient_representatives_generators(B)))[1] end @@ -303,6 +302,7 @@ function adjunction_process(X::AbsProjectiveVariety, steps::Int = 0) @assert steps >= 0 Pn = ambient_coordinate_ring(X) I = defining_ideal(X) + Y = X @req dim(I) == 3 "The given variety is not a surface." @req is_linearly_normal(X) "The given variety is not linearly normal." # TODO If X is not linear normal, embed it as a linearly normal variety first. @@ -343,19 +343,24 @@ function adjunction_process(X::AbsProjectiveVariety, steps::Int = 0) else l = zero(ZZ) end - dummy = (ZZ(ngens(Pn)-1), degree(I), sectional_genus(variety(I, check = false, is_radical = false))) + Y = variety(I, check = false, is_radical = false) + dY = degree(Y) + piY = sectional_genus(Y) + dummy = (ZZ(ngens(Pn)-1), dY, piY) if l==0 && dummy == numlist[count][1:3] # Enriques surface - return (numlist, adjlist, ptslist, variety(I, check = false, is_radical = false)) - else - push!(numlist, (ZZ(ngens(Pn)-1), degree(I), sectional_genus(variety(I, check = false, is_radical = false)), l)) + return (numlist, adjlist, ptslist, Y) + else + push!(numlist, (ZZ(ngens(Pn)-1), dY, piY, l)) push!(adjlist, adj) push!(ptslist, pts) - Omega = canonical_bundle(variety(I, check = false, is_radical = false)) + Omega = canonical_bundle(Y) FOmega = free_resolution(Omega, length = 1, algorithm = :mres) D = matrix(map(FOmega,1)) end count = count+1 end - return (numlist, adjlist, ptslist, variety(I, check = false, is_radical = false)) + return (numlist, adjlist, ptslist, Y) end + + diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/standard_constructions.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/standard_constructions.jl index 3c137a1b49a6..10cfbfcb02ed 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/standard_constructions.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/standard_constructions.jl @@ -107,12 +107,12 @@ function del_pezzo_surface(::Type{NormalToricVariety}, b::Int) # check for valid input @req b >= 0 "Number of blowups for construction of del Pezzo surfaces must be non-negative" @req b <= 3 "Del Pezzo surfaces with more than three blowups are realized as subvarieties of toric ambient spaces. This is currently not supported" - + # special case of projective space if b == 0 return projective_space(NormalToricVariety, 2) end - + # construct the "true" toric del Pezzo surfaces if b == 1 fan_rays = [1 0; 0 1; -1 -1; 1 1] @@ -276,29 +276,29 @@ Ideal generated by x2*x3 ``` """ -function normal_toric_varieties_from_star_triangulations(P::Polyhedron) +function normal_toric_varieties_from_star_triangulations(P::Polyhedron) # find position of origin in the lattices points of the polyhedron P pts = matrix(ZZ, lattice_points(P)) zero = [0 for i in 1:ambient_dim(P)] indices = findall(k -> pts[k:k,:] == matrix(ZZ, [zero]), 1:nrows(pts)) @req length(indices) == 1 "Polyhedron must contain origin (exactly once)" - + # change order of lattice points s.t. zero is the first point tmp = pts[1,:] pts[1,:] = pts[indices[1],:] pts[indices[1],:] = tmp - + # triangulate the points - note that we enforce full, i.e. want all points to be rays trias = star_triangulations(pts; full = true) - + # rays are all but the zero vector at the first position of pts integral_rays = reduce(vcat, [pts[k:k,:] for k in 2:nrows(pts)]) - + # trias contains the max_cones as list of lists # (a) needs to be converted to incidence matrix # (b) one has to remove origin from list of indices (as removed above) max_cones = [IncidenceMatrix([[c[i]-1 for i in 2:length(c)] for c in t]) for t in trias] - + # construct the varieties return [normal_toric_variety(cones, integral_rays; non_redundant = true) for cones in max_cones] end @@ -306,7 +306,7 @@ end @doc raw""" - normal_toric_variety_from_glsm(charges::ZZMatrix) + normal_toric_variety_from_glsm(charges::ZZMatrix) Return one toric variety with the desired GLSM charges. This can be particularly useful provided that @@ -323,8 +323,8 @@ Normal toric variety ``` For convenience, we also support: -- normal_toric_variety_from_glsm(charges::Vector{Vector{Int}}) -- normal_toric_variety_from_glsm(charges::Vector{Vector{ZZRingElem}}) +- `normal_toric_variety_from_glsm(charges::Vector{Vector{Int}})` +- `normal_toric_variety_from_glsm(charges::Vector{Vector{ZZRingElem}})` """ function normal_toric_variety_from_glsm(charges::ZZMatrix) @@ -377,7 +377,7 @@ julia> varieties = normal_toric_varieties_from_glsm(matrix(ZZ, [1 2 3 4 6 0; -1 Normal toric variety julia> cox_ring(varieties[1]) -Multivariate polynomial ring in 6 variables over QQ graded by +Multivariate polynomial ring in 6 variables over QQ graded by x1 -> [1 -1] x2 -> [2 -1] x3 -> [3 -2] @@ -387,8 +387,8 @@ Multivariate polynomial ring in 6 variables over QQ graded by ``` For convenience, we also support: -- normal_toric_varieties_from_glsm(charges::Vector{Vector{Int}}) -- normal_toric_varieties_from_glsm(charges::Vector{Vector{ZZRingElem}}) +- `normal_toric_varieties_from_glsm(charges::Vector{Vector{Int}})` +- `normal_toric_varieties_from_glsm(charges::Vector{Vector{ZZRingElem}})` """ function normal_toric_varieties_from_glsm(charges::ZZMatrix) # find the ray generators @@ -398,7 +398,7 @@ function normal_toric_varieties_from_glsm(charges::ZZMatrix) ker = kernel(map) embedding = snf(ker[1])[2] * ker[2] rays = transpose(embedding.map) - + # identify the points to be triangulated pts = zeros(QQ, nrows(rays), ncols(charges)-nrows(charges)) for i in 1:nrows(rays) @@ -406,19 +406,19 @@ function normal_toric_varieties_from_glsm(charges::ZZMatrix) end zero = [0 for i in 1:ncols(charges)-nrows(charges)] pts = vcat(matrix(QQ, transpose(zero)), matrix(QQ, pts)) - + # construct varieties integral_rays = reduce(vcat, [pts[k:k,:] for k in 2:nrows(pts)]) max_cones = [IncidenceMatrix([[c[i]-1 for i in 2:length(c)] for c in t]) for t in star_triangulations(pts; full = true)] varieties = [normal_toric_variety(cones, integral_rays; non_redundant = true) for cones in max_cones] - + # set the map from Div_T -> Cl to the desired matrix for v in varieties set_attribute!(v, :torusinvariant_weil_divisor_group, G1) set_attribute!(v, :class_group, G2) set_attribute!(v, :map_from_torusinvariant_weil_divisor_group_to_class_group, map) end - + # return the varieties return varieties end diff --git a/src/AlgebraicGeometry/ToricVarieties/ToricSchemes/attributes.jl b/src/AlgebraicGeometry/ToricVarieties/ToricSchemes/attributes.jl index 641315809f4c..e7fb40cdb96c 100644 --- a/src/AlgebraicGeometry/ToricVarieties/ToricSchemes/attributes.jl +++ b/src/AlgebraicGeometry/ToricVarieties/ToricSchemes/attributes.jl @@ -172,12 +172,12 @@ function _compute_toric_gluing(gd::ToricGluingData) xx = gens(OO(UV)) yy = gens(OO(VU)) - f = morphism(UV, VU, [prod((e[i] >= 0 ? u^e[i] : inv(u)^-e[i]) for (i, u) in enumerate(xx); init=one(OO(UV))) for e in y_to_x], check=true) - g = morphism(VU, UV, [prod((e[i] >= 0 ? v^e[i] : inv(v)^-e[i]) for (i, v) in enumerate(yy); init=one(OO(VU))) for e in x_to_y], check=true) + f = morphism(UV, VU, [prod((e[i] >= 0 ? u^e[i] : inv(u)^-e[i]) for (i, u) in enumerate(xx); init=one(OO(UV))) for e in y_to_x], check=false) + g = morphism(VU, UV, [prod((e[i] >= 0 ? v^e[i] : inv(v)^-e[i]) for (i, v) in enumerate(yy); init=one(OO(VU))) for e in x_to_y], check=false) set_attribute!(f, :inverse, g) set_attribute!(g, :inverse, f) - result = Gluing(U, V, f, g, check=true) + result = Gluing(U, V, f, g, check=false) return result end diff --git a/src/Combinatorics/EnumerativeCombinatorics/compositions.jl b/src/Combinatorics/EnumerativeCombinatorics/compositions.jl index a72ad256ea3b..cc7f530d2c1b 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/compositions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/compositions.jl @@ -152,12 +152,7 @@ parts(C::CompositionsFixedNumParts) = C.k Base.eltype(C::CompositionsFixedNumParts{T}) where T = Composition{T} function Base.show(io::IO, C::CompositionsFixedNumParts) - if get(io, :supercompact, false) - print(io, "Iterator") - else - io = pretty(io) - print(io, "Iterator over the compositions of $(base(C)) into ", ItemQuantity(parts(C), "part")) - end + print(pretty(io), "Iterator over the compositions of $(base(C)) into ", ItemQuantity(parts(C), "part")) end Base.length(C::CompositionsFixedNumParts) = BigInt(number_of_compositions(base(C), parts(C))) @@ -218,12 +213,7 @@ base(C::Compositions) = C.n Base.eltype(C::Compositions{T}) where T = Composition{T} function Base.show(io::IO, C::Compositions) - if get(io, :supercompact, false) - print(io, "Iterator") - else - io = pretty(io) - print(io, "Iterator over the compositions of $(base(C))") - end + print(pretty(io), "Iterator over the compositions of $(base(C))") end Base.length(C::Compositions) = BigInt(number_of_compositions(base(C))) @@ -304,12 +294,7 @@ base(C::AscendingCompositions) = C.n Base.eltype(C::AscendingCompositions{T}) where T = Composition{T} function Base.show(io::IO, C::AscendingCompositions) - if get(io, :supercompact, false) - print(io, "Iterator") - else - io = pretty(io) - print(io, "Iterator over the ascending compositions of $(base(C))") - end + print(pretty(io), "Iterator over the ascending compositions of $(base(C))") end # Ascending compositions are basically partitions turned around diff --git a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl index b4568ee3269b..11a8fd7557ff 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl @@ -75,6 +75,8 @@ function Base.show(io::IO, ::MIME"text/plain", P::Partition) print(io, data(P)) end + + ################################################################################ # # Array-like functionality @@ -123,6 +125,33 @@ function getindex_safe(P::Partition{T}, i::IntegerUnion) where T return (i > length(data(P)) ? zero(T) : getindex(data(P), Int(i))) end +base(P::Partitions) = P.n + +Base.eltype(::Partitions{T}) where T = Partition{T} + +function Base.show(io::IO, ::MIME"text/plain", P::Partitions) + print(pretty(io), "Iterator over the partitions of $(base(P))") +end + +Base.length(P::Partitions) = BigInt(number_of_partitions(P.n)) + + + +base(P::PartitionsFixedNumParts) = P.n + +Base.eltype(::PartitionsFixedNumParts{T}) where T = Partition{T} + +function Base.show(io::IO, ::MIME"text/plain", P::PartitionsFixedNumParts) + print(pretty(io), "Iterator over the partitions of $(base(P)) into ", + ItemQuantity(P.k, "part")) +end + +# NOTE this will not be accurate in many cases, +# in particular if upper/lower bounds are given, +# or if `only_distinct_parts == true`. +# Base.length(P::PartitionsFixedNumParts) = BigInt(number_of_partitions(P.n, P.k)) + +Base.IteratorSize(::Type{PartitionsFixedNumParts{T}}) where T = Base.SizeUnknown() ################################################################################ # # Generating and counting unrestricted partitions @@ -151,6 +180,7 @@ function number_of_partitions(n::IntegerUnion) return Nemo.number_of_partitions(ZZ(n)) end + @doc raw""" partitions(n::IntegerUnion) @@ -185,54 +215,56 @@ julia> collect(partitions(Int8(4))) # using less memory Int8[1, 1, 1, 1] ``` """ -function partitions(n::T) where T <: IntegerUnion +function partitions(n::IntegerUnion) + return Partitions(n) +end - #Argument checking - @req n >= 0 "n >= 0 required" - # Some trivial cases +function Base.iterate(P::Partitions{T}) where T + n = base(P) + if n == 0 - return (p for p in Partition{T}[ partition(T[], check = false) ]) - elseif n == 1 - return (p for p in Partition{T}[ partition(T[1], check = false) ]) + return partition(T[], check=false), (T[], 0, 0) + elseif P.n == 1 + return partition(T[1], check=false), (T[1], 1, 0) end - # Now, the algorithm starts - P = Partition{T}[] #this will be the array of all partitions - k = 1 - q = 1 d = fill( T(1), n ) d[1] = n - push!(P, partition(d[1:1], check = false)) - while q != 0 - if d[q] == 2 - k += 1 - d[q] = 1 - q -= 1 - else - m = d[q] - 1 - np = k - q + 1 + return partition(d[1:1], check=false), (d, 1, 1) + +end + +function Base.iterate(P::Partitions{T}, state::Tuple{Vector{T}, Int, Int}) where T + d, k, q = state + q==0 && return nothing + if d[q] == 2 + d[q] = 1 + k += 1 + q -= 1 + else + m = d[q] - 1 + np = k - q +1 + d[q] = m + while np >= m + q += 1 d[q] = m - while np >= m + np -= m + end + if np == 0 + k = q + else + k = q + 1 + if np > 1 q += 1 - d[q] = m - np = np - m - end - if np == 0 - k = q - else - k = q + 1 - if np > 1 - q += 1 - d[q] = np - end + d[q] = np end end - push!(P, partition(d[1:k], check = false)) end - return (p for p in P) + return partition(d[1:k], check=false), (d, k, q) end + ################################################################################ # # Generating and counting restricted partitions @@ -263,11 +295,11 @@ function number_of_partitions(n::IntegerUnion, k::IntegerUnion) return ZZ(1) # See https://oeis.org/A008284 - elseif n < 2*k +elseif n < 2*k return number_of_partitions(n - k) #n - k >= 0 holds since the case n= 0 holds since the case n= 0` parts. Optionally, a lower bound `l1 >= 0` and an upper bound `l2` for +Return an iterator over all partitions of a non-negative integer `n` into +`k >= 0` parts. Optionally, a lower bound `lb >= 0` and an upper bound `ub` for the parts can be supplied. In this case, the partitions are produced in *decreasing* order. @@ -332,121 +364,250 @@ julia> collect(partitions(7, 3, 1, 4; only_distinct_parts = true)) [4, 2, 1] ``` """ -function partitions(m::T, n::IntegerUnion, l1::IntegerUnion, l2::IntegerUnion; only_distinct_parts::Bool = false) where T <: IntegerUnion - # Algorithm "parta" in [RJ76](@cite), de-gotoed from old ALGOL 60 code by E. Thiel. +function partitions(n::IntegerUnion, k::IntegerUnion, lb::IntegerUnion, ub::IntegerUnion; only_distinct_parts::Bool = false) + return PartitionsFixedNumParts(n, k, lb, ub; only_distinct_parts = only_distinct_parts) +end - # Note that we are considering partitions of m here. I would switch m and n - # but the algorithm was given like that and I would otherwise confuse myself - # implementing it. +function partitions(n::IntegerUnion, k::IntegerUnion; only_distinct_parts::Bool = false) + return PartitionsFixedNumParts(n, k; only_distinct_parts = only_distinct_parts) +end - #Argument checking - @req m >= 0 "m >= 0 required" - @req n >= 0 "n >= 0 required" - @req l1 >= 0 "l1 >= 0 required" - # Use type of m - n = convert(T, n) - # Some trivial cases - if m == 0 && n == 0 - return (p for p in Partition{T}[ partition(T[], check = false) ]) +# Algorithm "parta" in [RJ76](@cite), de-gotoed from old ALGOL 60 code by E. Thiel. + +# Note that the algorithm is given as partitioning m into n parts, +# but we have refactored to align more closely with standard terminology. +function Base.iterate(P::PartitionsFixedNumParts{T}) where T + n = P.n + k = P.k + lb = P.lb + ub = P.ub + only_distinct_parts = P.distinct_parts + + if n == 0 && k == 0 + return partition(T[], check=false), (T[], T[], 0, 0, 1, false) end - if n == 0 || n > m - return (p for p in Partition{T}[]) + # This iterator should be empty + if k == 0 || k > n || ub < lb + return nothing end - if l2 < l1 - return (p for p in Partition{T}[]) + if n == k && lb == 1 + only_distinct_parts && k > 1 && return nothing + return partition(T[1 for i in 1:n], check=false), (T[], T[], 0, 0, 1, false) end - # If l1 == 0 the algorithm parta will actually create lists containing the - # entry zero, e.g. partitions(2, 2, 0, 2) will contain [2, 0]. - # This is nonsense, so we set l1 = 1 in this case. - if l1 == 0 - l1 = 1 + if k == 1 && lb <= n <= ub + return partition(T[n], check=false), (T[], T[], 0, 0, 1, false) end - #Algorithm starts here - P = Partition{T}[] #this will be the array of all partitions - x = zeros(T, n) - y = zeros(T, n) - j = only_distinct_parts*n*(n - 1) - m = m - n*l1 - div(j, 2) - l2 = l2 - l1 - if 0 <= m <= n*l2 - j + x = zeros(T,k) + y = zeros(T,k) + jj = only_distinct_parts*k*(k-1) + N = n - k*lb - div(jj,2) + L2 = ub-lb + 0 <= N <= k*L2 - jj || return nothing - for i = 1:n - y[i] = x[i] = l1 + only_distinct_parts*(n - i) - end + for i in 1:k + y[i] = x[i] = lb + only_distinct_parts*(k-i) + end - i = 1 - l2 = l2 - only_distinct_parts*(n - 1) + i = 1 + L2 = L2 - only_distinct_parts*(k-1) - while true - while m > l2 - m -= l2 - x[i] = y[i] + l2 - i += 1 - end + while N > L2 + N -= L2 + x[i] = y[i] + L2 + i += 1 + end + x[i] = y[i] + N + return partition(x[1:k], check = false), (x, y, N, L2, i, true) +end - x[i] = y[i] + m - push!(P, partition(x[1:n], check = false)) +function Base.iterate(P::PartitionsFixedNumParts{T}, state::Tuple{Vector{T}, Vector{T}, T, IntegerUnion, Int, Bool}) where T + k = P.k + x, y, N, L2, i, flag = state - if i < n && m > 1 - m = 1 - x[i] = x[i] - 1 - i += 1 - x[i] = y[i] + 1 - push!(P, partition(x[1:n], check = false)) - end + N == 0 && return nothing - lcycle = false - for j = i - 1:-1:1 - l2 = x[j] - y[j] - 1 - m = m + 1 - if m <= (n - j)*l2 - x[j] = y[j] + l2 - lcycle = true - break - end - m = m + l2 - x[i] = y[i] - i = j - end + if flag + if i < k && N > 1 + N = 1 + x[i] = x[i] - 1 + i += 1 + x[i] = y[i] + 1 + return partition(x[1:k], check = false), (x,y,N,L2,i,false) + end + end - if !lcycle - break - end + lcycle = false + for j in i - 1:-1:1 + L2 = x[j] - y[j] - 1 + N = N + 1 + if N <= (k-j)*L2 + x[j] = y[j] + L2 + lcycle = true + break end + N = N + L2 + x[i] = y[i] + i = j end - return (p for p in P) + lcycle || return nothing + while N > L2 + N -= L2 + x[i] = y[i] + L2 + i += 1 + end + x[i] = y[i] + N + return partition(x[1:k], check = false), (x,y,N,L2,i,true) end -function partitions(m::T, n::IntegerUnion; only_distinct_parts::Bool = false) where T <: IntegerUnion - @req m >= 0 "m >= 0 required" + +@doc raw""" + partitions(n::T, v::Vector{T}) where T <: IntegerUnion + partitions(n::T, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion + partitions(n::T, k::IntegerUnion, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion + +Return an iterator over all partitions of a non-negative integer `n` where each +part is an element in the vector `v` of positive integers. +It is assumed that the entries in `v` are strictly increasing. + +If the optional vector `mu` is supplied, then each `v[i]` occurs a maximum of +`mu[i] > 0` times per partition. + +If the optional integer `k >= 0` is supplied, the partitions will be into `k` +parts. In this case, the partitions are produced in lexicographically *decreasing* +order. + +The implemented algorithm is "partb" in [RJ76](@cite). + +# Example +The number of partitions of 100 where the parts are from {1, 2, 5, 10, 20, 50}: +```jldoctest +julia> length(partitions(100, [1, 2, 5, 10, 20, 50])) +4562 +``` +All partitions of 100 where the parts are from {1, 2, 5, 10, 20, 50} and each +part is allowed to occur at most twice: +```jldoctest +julia> collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) +6-element Vector{Partition{Int64}}: + [50, 50] + [50, 20, 20, 10] + [50, 20, 20, 5, 5] + [50, 20, 10, 10, 5, 5] + [50, 20, 20, 5, 2, 2, 1] + [50, 20, 10, 10, 5, 2, 2, 1] +``` +The partitions of 100 into seven parts, where the parts are required to be +elements from {1, 2, 5, 10, 20, 50} and each part is allowed to occur at most twice. +```jldoctest +julia> collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) +1-element Vector{Partition{Int64}}: + [50, 20, 20, 5, 2, 2, 1] +``` +""" +function partitions(n::T, v::Vector{T}) where T <: IntegerUnion @req n >= 0 "n >= 0 required" + @req all([v[i] < v[i + 1] for i in 1:length(v) - 1]) "v must be strictly increasing" + @req all(>(0), v) "Entries of v must be positive" - # Special cases - if m == n - return (p for p in [ partition(T[ 1 for i in 1:m], check = false) ]) - elseif m < n || n == 0 - return (p for p in Partition{T}[]) - elseif n == 1 - return (p for p in [ partition(T[m], check = false) ]) + res = Partition{T}[] + + if isempty(v) + return (p for p in res) end - return (p for p in partitions(m, n, 1, m; only_distinct_parts = only_distinct_parts)) + if n == 0 + # TODO: I don't understand this return (and it is type instable) + return (p for p in [ Partition{T}[] ]) + end + + # We will loop over the number of parts. + # We first determine the minimal and maximal number of parts. + r = length(v) + kmin = div(n, v[r]) + kmax = div(n, v[1]) + + # Set the maximum multiplicity (equal to kmax above) + mu = [ kmax for i in 1:r ] + + for k = kmin:kmax + append!(res, partitions(n, k, v, mu)) + end + + return (p for p in res) end -function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} + +function partitions(n::T, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} + + @req n >= 0 "n >= 0 required" + @req length(mu) == length(v) "mu and v should have the same length" + + # Algorithm partb assumes that v is strictly increasing. + # Added (and noticed) on Mar 22, 2023. + @req all([v[i] < v[i + 1] for i in 1:length(v) - 1]) "v must be strictly increasing" + + # Parta allows v[1] = 0 but this is nonsense for entries of a partition + @req all(>(0), v) "Entries of v must be positive" + + # For safety + @req all(>(0), mu) "Entries of mu must be positive" + res = Partition{T}[] + + if isempty(v) + return (p for p in res) + end + + if n == 0 + # TODO: I don't understand this return (and it is type instable) + return (p for p in [ Partition{T}[] ]) + end + + # We will loop over the number of parts. + # We first determine the minimal and maximal number of parts. + r = length(v) + + kmax = 0 + cursum = 0 + for i in 1:r, j in 1:mu[i] + kmax += 1 + cursum += v[i] + if cursum >= n + break + end + end + + kmin = 0 + cursum = 0 + for i in r:-1:1, j in 1:mu[i] + kmin += 1 + cursum += v[i] + if cursum >= n + break + end + end + + for k = kmin:kmax + append!(res, partitions(n, k, v, mu)) + end + + return (p for p in res) +end + + +function partitions(n::T, k::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} # Algorithm "partb" in [RJ76](@cite), de-gotoed from old ALGOL 60 code by E. Thiel. # The algorithm as published in the paper has several issues and we hope to have fixed # them all, see below for details. Some initial fixing was done by T. Schmit. - @req m >= 0 "m >= 0 required" @req n >= 0 "n >= 0 required" + @req k >= 0 "k >= 0 required" @req length(mu) == length(v) "mu and v should have the same length" # Algorithm partb assumes that v is strictly increasing. @@ -460,10 +621,10 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T @req all(>(0), mu) "Entries of mu must be positive" # Special cases - if n == 0 + if k == 0 # TODO: I don't understand this distinction here # (it also makes the function tabe instable) - if m == 0 + if n == 0 return (p for p in [ Partition{T}[] ]) else return (p for p in Partition{T}[]) @@ -481,19 +642,19 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # published code has several issues. # The original ALGOL 60 code is electronically available at # https://gist.github.com/ulthiel/99de02994fc31fe614586ed0c930f744. - # First, there were some issues with termination and indices for the arrays - # x, y, ii getting out of bounds. We had to introduce some additional checks + # First, there were some issues with termination and indices for the arrays + # x, y, ii getting out of bounds. We had to introduce some additional checks # and breaks to take care of this. Some initial fixing was done by T. Schmit. # An example showing the problem is 17, 3, [1, 4], [1, 4]. # Initialize variables r = length(v) j = 1 - k = mu[1] + m = mu[1] ll = v[1] - x = zeros(T, n) - y = zeros(T, n) - ii = zeros(T, n) + x = zeros(T, k) + y = zeros(T, k) + ii = zeros(T, k) # The algorithm has three goto labels b1, b2, b3. # b3 terminates the algorithm. @@ -502,16 +663,16 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T gotob1 = true # The first step in the algorithm is to initialize the arrays x and y - # for the backtrack search. - # This fills the array x from right with the values from v from the left + # for the backtrack search. + # This fills the array x from right with the values from v from the left # (the smallest) up to their specified multiplicity. - # If this is larger than m, there cannot be a partition. - for i = n:-1:1 + # If this is larger than n, there cannot be a partition. + for i = k:-1:1 x[i] = ll y[i] = ll - k = k - 1 - m = m - ll - if k == 0 + m = m - 1 + n = n - ll + if m == 0 if j == r # In the original algorithm there's a goto b3 here which means # the program will terminate, and thus return an empty list. @@ -520,7 +681,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T break end j = j + 1 - k = mu[j] + m = mu[j] ll = v[j] end if i == 1 @@ -534,40 +695,40 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T ll = v[1] # This is a necessary condition for existence of a partition - if m < 0 || m > n * (lr - ll) + if n < 0 || n > k * (lr - ll) return (p for p in P) #goto b3 end # The following is a condition for when only a single partition # exists. We added the || i == 1 condition because without it the example - # partitions(17, 7, [1, 4], [1, 4]) returns [0, 0, 4, 4, 4, 4, 1], which is + # partitions(17, 7, [1, 4], [1, 4]) returns [0, 0, 4, 4, 4, 4, 1], which is # nonsense. So, we have to make sure that all entries of x were modified in # the initial backtracking, which means i was counted down to 1. # Noticed on Mar 23, 2023. - if m == 0 && x[1] != 0 + if n == 0 && x[1] != 0 push!(P, partition(copy(x), check = false)) return (p for p in P) end # Now, the actual algorithm starts i = 1 - m = m + y[1] + n = n + y[1] # label b1 while gotob1 == true if !gotob2 for j = mu[r]:-1:1 - if m <= lr + if n <= lr gotob2 = true break end x[i] = lr ii[i] = r - 1 - if i == n # Added, otherwise get out of bounds + if i == k # Added, otherwise get out of bounds break end i = i + 1 - m = m - lr + y[i] + n = n - lr + y[i] end #for j if !gotob2 @@ -579,7 +740,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # label b2 if gotob2 - while r > 0 && v[r] > m # Added additional r > 0, otherwise get out of bounds + while r > 0 && v[r] > n # Added additional r > 0, otherwise get out of bounds r = r - 1 end @@ -588,9 +749,9 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T end lr = v[r] - if m == lr + if n == lr x[i] = lr - if i <= n # Added, otherwise get out of bounds + if i <= k # Added, otherwise get out of bounds push!(P, partition(copy(x), check = false)) #need copy here! else break @@ -603,7 +764,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T lr = v[r] end #if - k = y[i] + m = y[i] # Here comes the most intricate mistake. # On "Knuth's" problem # 100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2] @@ -612,23 +773,23 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # But when we replace lr > k by lr >= k, everything works. # Finding this was a wild guess! # Found on Mar 27, 2023. - if lr >= k && m - lr <= (n - i)*(lr - ll) + if lr >= m && n - lr <= (k - i)*(lr - ll) gotob2 = false continue else - x[i] = k + x[i] = m end #if for i_0 = i - 1:-1:1 #this is to replace the for i = i - 1 in ALGOL code i = i_0 r = ii[i] lr = v[r] - m = m + x[i] - k - k = y[i] - if lr >= k && m - lr <= (n - i)*(lr - ll) # >= here as well (probably...) + n = n + x[i] - m + m = y[i] + if lr >= m && n - lr <= (k - i)*(lr - ll) # >= here as well (probably...) gotob2 = false break else - x[i] = k + x[i] = m end #if end #for if gotob2 @@ -640,138 +801,6 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T return (p for p in P) end -function partitions(m::T, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} - - @req m >= 0 "m >= 0 required" - @req length(mu) == length(v) "mu and v should have the same length" - - # Algorithm partb assumes that v is strictly increasing. - # Added (and noticed) on Mar 22, 2023. - @req all([v[i] < v[i + 1] for i in 1:length(v) - 1]) "v must be strictly increasing" - - # Parta allows v[1] = 0 but this is nonsense for entries of a partition - @req all(>(0), v) "Entries of v must be positive" - - # For safety - @req all(>(0), mu) "Entries of mu must be positive" - res = Partition{T}[] - - if isempty(v) - return (p for p in res) - end - - if m == 0 - # TODO: I don't understand this return (and it is type instable) - return (p for p in [ Partition{T}[] ]) - end - - # We will loop over the number of parts. - # We first determine the minimal and maximal number of parts. - r = length(v) - - nmax = 0 - cursum = 0 - for i in 1:r, j in 1:mu[i] - nmax += 1 - cursum += v[i] - if cursum >= m - break - end - end - - nmin = 0 - cursum = 0 - for i in r:-1:1, j in 1:mu[i] - nmin += 1 - cursum += v[i] - if cursum >= m - break - end - end - - for n = nmin:nmax - append!(res, partitions(m, n, v, mu)) - end - - return (p for p in res) -end - -@doc raw""" - partitions(m::T, v::Vector{T}) where T <: IntegerUnion - partitions(m::T, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion - partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion - -Return an iterator over all partitions of a non-negative integer `m` where each -part is an element in the vector `v` of positive integers. -It is assumed that the entries in `v` are strictly increasing. - -If the optional vector `mu` is supplied, then each `v[i]` occurs a maximum of -`mu[i] > 0` times per partition. - -If the optional integer `n >= 0` is supplied, the partitions will be into `n` -parts. In this case, the partitions are produced in lexicographically *decreasing* -order. - -The implemented algorithm is "partb" in [RJ76](@cite). - -# Example -The number of partitions of 100 where the parts are from {1, 2, 5, 10, 20, 50}: -```jldoctest -julia> length(partitions(100, [1, 2, 5, 10, 20, 50])) -4562 -``` -All partitions of 100 where the parts are from {1, 2, 5, 10, 20, 50} and each -part is allowed to occur at most twice: -```jldoctest -julia> collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) -6-element Vector{Partition{Int64}}: - [50, 50] - [50, 20, 20, 10] - [50, 20, 20, 5, 5] - [50, 20, 10, 10, 5, 5] - [50, 20, 20, 5, 2, 2, 1] - [50, 20, 10, 10, 5, 2, 2, 1] -``` -The partitions of 100 into seven parts, where the parts are required to be -elements from {1, 2, 5, 10, 20, 50} and each part is allowed to occur at most twice. -```jldoctest -julia> collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) -1-element Vector{Partition{Int64}}: - [50, 20, 20, 5, 2, 2, 1] -``` -""" -function partitions(m::T, v::Vector{T}) where T <: IntegerUnion - @req m >= 0 "m >= 0 required" - @req all([v[i] < v[i + 1] for i in 1:length(v) - 1]) "v must be strictly increasing" - @req all(>(0), v) "Entries of v must be positive" - - res = Partition{T}[] - - if isempty(v) - return (p for p in res) - end - - if m == 0 - # TODO: I don't understand this return (and it is type instable) - return (p for p in [ Partition{T}[] ]) - end - - # We will loop over the number of parts. - # We first determine the minimal and maximal number of parts. - r = length(v) - nmin = div(m, v[r]) - nmax = div(m, v[1]) - - # Set the maximum multiplicity (equal to nmax above) - mu = [ nmax for i in 1:r ] - - for n = nmin:nmax - append!(res, partitions(m, n, v, mu)) - end - - return (p for p in res) -end - ################################################################################ # # Relations diff --git a/src/Combinatorics/EnumerativeCombinatorics/types.jl b/src/Combinatorics/EnumerativeCombinatorics/types.jl index fab763281fa5..7888f21c3f1a 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/types.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/types.jl @@ -134,6 +134,52 @@ struct Partition{T<:IntegerUnion} <: AbstractVector{T} p::Vector{T} end +# Iterator type: all partitions of an integer n +struct Partitions{T<:IntegerUnion} + n::T + + function Partitions(n::T) where T<:IntegerUnion + @req n >= 0 "n >= 0 required" + return new{T}(n) + end + +end + +# Iterator type: partitions of n into k parts, with optional lower/upper bounds on the parts +# If distinct_parts == true, then all parts have distinct values. +struct PartitionsFixedNumParts{T<:IntegerUnion} + n::T + k::Int + + lb::T + ub::T + distinct_parts::Bool + + function PartitionsFixedNumParts(n::T, k::IntegerUnion, lb::IntegerUnion, ub::IntegerUnion, only_distinct_parts::Bool) where T<:IntegerUnion + @req n >= 0 "n >= 0 required" + @req k >= 0 "k >= 0 required" + @req lb >= 0 "lb >=0 required" + # If lb == 0 the algorithm will actually create lists containing the + # entry zero, e.g. partitions(2, 2, 0, 2) will contain [2, 0]. + # This is nonsense, so we set lb = 1 in this case. + if lb == 0 + lb = 1 + end + return new{T}(n, convert(T, k), T(lb), T(ub), only_distinct_parts) + end + +end + +function PartitionsFixedNumParts(n::T, k::IntegerUnion; only_distinct_parts::Bool = false) where T<:IntegerUnion + return PartitionsFixedNumParts(n, k, 1, n, only_distinct_parts) +end + +function PartitionsFixedNumParts(n::T, k::IntegerUnion, lb::IntegerUnion, ub::IntegerUnion; only_distinct_parts::Bool = false) where T<:IntegerUnion + return PartitionsFixedNumParts(n, k, lb, ub, only_distinct_parts) +end + + + ################################################################################ # # Young Tableaux diff --git a/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl b/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl index c8782443a5e2..fad29bda8dfe 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/weak_compositions.jl @@ -164,10 +164,5 @@ function Base.iterate(W::WeakCompositions{T}, s::Vector{T}) where T end function Base.show(io::IO, W::WeakCompositions) - if is_terse(io) - print(io, "Iterator") - else - io = pretty(io) - print(io, "Iterator over the weak compositions of $(base(W)) into ", ItemQuantity(parts(W), "part")) - end + print(pretty(io), "Iterator over the weak compositions of $(base(W)) into ", ItemQuantity(parts(W), "part")) end diff --git a/src/Combinatorics/PhylogeneticTrees.jl b/src/Combinatorics/PhylogeneticTrees.jl index f667b9674a98..4d3ecf1aabfc 100644 --- a/src/Combinatorics/PhylogeneticTrees.jl +++ b/src/Combinatorics/PhylogeneticTrees.jl @@ -43,7 +43,7 @@ function phylogenetic_tree(T::Type{<:Union{Float64, QQFieldElem}}, newick::Strin end @doc raw""" - phylogenetic_tree(M::Matrix{T}, taxa::Vector{String}) where T <: Union{Float64, QQFieldElem} + phylogenetic_tree(M::Matrix{T}, taxa::Vector{String}) where T <: Union{Float64, QQFieldElem} Constructs a phylogenetic tree with cophenetic matrix `M` and taxa `taxa`. The matrix `M` must be ultrametric, otherwise an error will be thrown. diff --git a/src/Combinatorics/SimplicialComplexes.jl b/src/Combinatorics/SimplicialComplexes.jl index e00d6a293045..c161df76ada0 100644 --- a/src/Combinatorics/SimplicialComplexes.jl +++ b/src/Combinatorics/SimplicialComplexes.jl @@ -121,7 +121,7 @@ julia> n_vertices(torus()) n_vertices(K::SimplicialComplex) = pm_object(K).N_VERTICES::Int @doc raw""" - n_facets(K::SimplicialComplex) + n_facets(K::SimplicialComplex) Return the number of facets of the abstract simplicial complex `K`. """ @@ -562,7 +562,7 @@ end ############################################################################### @doc raw""" - is_isomorphic(K1::SimplicialComplex, K2::SimplicialComplex) + is_isomorphic(K1::SimplicialComplex, K2::SimplicialComplex) Checks if the given simplicial complexes are isomorphic. diff --git a/src/GAP/customize.jl b/src/GAP/customize.jl index 0d0892caacca..aa37b339efb6 100644 --- a/src/GAP/customize.jl +++ b/src/GAP/customize.jl @@ -2,12 +2,12 @@ # when GAP is used by Oscar. # More generally, `GAP_info_messages_off` can set all info levels either # to zero or to those values that were valid when Oscar was started. -const __GAP_info_levels_default = Pair{GAP.GapObj, Int}[] +const __GAP_info_levels_default = Pair{GapObj, Int}[] function __GAP_info_messages_off(off::Bool = true) if length(__GAP_info_levels_default) == 0 # Initialize the info about default levels. - for c in Vector{GAP.GapObj}(GAP.Globals.INFO_CLASSES) + for c in Vector{GapObj}(GAP.Globals.INFO_CLASSES) push!(__GAP_info_levels_default, c => GAP.Globals.InfoLevel(c)) end end diff --git a/src/GAP/gap_to_oscar.jl b/src/GAP/gap_to_oscar.jl index c763bf79268f..802b059eb217 100644 --- a/src/GAP/gap_to_oscar.jl +++ b/src/GAP/gap_to_oscar.jl @@ -162,7 +162,7 @@ end GAP.gap_to_julia(::Type{QQAbElem}, a::GapInt) = QQAbElem(a) -(::QQAbField)(a::GAP.GapObj) = GAP.gap_to_julia(QQAbElem, a) +(::QQAbField)(a::GapObj) = GAP.gap_to_julia(QQAbElem, a) ## nonempty list of GAP matrices over a given cyclotomic field function matrices_over_cyclotomic_field(F::AbsSimpleNumField, gapmats::GapObj) diff --git a/src/GAP/iso_gap_oscar.jl b/src/GAP/iso_gap_oscar.jl index d170bd1141cd..52b089260aba 100644 --- a/src/GAP/iso_gap_oscar.jl +++ b/src/GAP/iso_gap_oscar.jl @@ -29,7 +29,7 @@ end # ################################################################################ -function _iso_gap_oscar_field_of_cyclotomics(F::GAP.GapObj) +function _iso_gap_oscar_field_of_cyclotomics(F::GapObj) GAPWrap.IsCyclotomicField(F) && return _iso_gap_oscar_field_cyclotomic(F) F === GAP.Globals.Cyclotomics && return _iso_gap_oscar_abelian_closure(F) GAPWrap.DegreeOverPrimeField(F) == 2 && return _iso_gap_oscar_field_quadratic(F) @@ -37,9 +37,9 @@ function _iso_gap_oscar_field_of_cyclotomics(F::GAP.GapObj) error("no method found") end -function _iso_gap_oscar_residue_ring(RG::GAP.GapObj) +function _iso_gap_oscar_residue_ring(RG::GapObj) n = GAPWrap.Size(RG) - if n isa GAP.GapObj + if n isa GapObj n = ZZRingElem(n) end RO = residue_ring(ZZ, n)[1] @@ -49,7 +49,7 @@ function _iso_gap_oscar_residue_ring(RG::GAP.GapObj) return MapFromFunc(RG, RO, f, finv) end -function _iso_gap_oscar_field_finite(FG::GAP.GapObj) +function _iso_gap_oscar_field_finite(FG::GapObj) FO = GF(characteristic(FG), GAPWrap.DegreeOverPrimeField(FG)) finv, f = _iso_oscar_gap_field_finite_functions(FO, FG) @@ -57,28 +57,28 @@ function _iso_gap_oscar_field_finite(FG::GAP.GapObj) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_field_rationals(FG::GAP.GapObj) +function _iso_gap_oscar_field_rationals(FG::GapObj) FO = QQ finv, f = _iso_oscar_gap_field_rationals_functions(FO, FG) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_ring_integers(FG::GAP.GapObj) +function _iso_gap_oscar_ring_integers(FG::GapObj) FO = ZZ finv, f = _iso_oscar_gap_ring_integers_functions(FO, FG) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_field_cyclotomic(FG::GAP.GapObj) +function _iso_gap_oscar_field_cyclotomic(FG::GapObj) FO = cyclotomic_field(GAPWrap.Conductor(FG))[1] finv, f = _iso_oscar_gap_field_cyclotomic_functions(FO, FG) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_field_quadratic(FG::GAP.GapObj) +function _iso_gap_oscar_field_quadratic(FG::GapObj) if GAPWrap.IsCyclotomicCollection(FG) # Determine the root that is contained in `FG`. N = GAPWrap.Conductor(FG) @@ -127,7 +127,7 @@ function _iso_gap_oscar_number_field(FG::GapObj) B = GAPWrap.Basis(FG, powers) finv = function(x::Nemo.AbsSimpleNumFieldElem) - coeffs = GAP.GapObj(coefficients(x), recursive = true)::GapObj + coeffs = GapObj(coefficients(x), recursive = true)::GapObj return (coeffs * powers)::GAP.Obj end @@ -145,7 +145,7 @@ function _iso_gap_oscar_number_field(FG::GapObj) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) finv = function(x::Nemo.AbsSimpleNumFieldElem) - coeffs = GAP.GapObj(coefficients(x), recursive = true)::GapObj + coeffs = GapObj(coefficients(x), recursive = true)::GapObj return GAPWrap.ObjByExtRep(fam, coeffs) end @@ -184,7 +184,7 @@ function _iso_gap_oscar_number_field(FG::GapObj) Mpowers = GapObj(Mpowers) finv = function(x::Nemo.AbsSimpleNumFieldElem) - coeffs = GAP.GapObj(coefficients(x), recursive = true)::GapObj + coeffs = GapObj(coefficients(x), recursive = true)::GapObj return GAPWrap.LinearCombination(coeffs, Mpowers) end @@ -200,14 +200,14 @@ function _iso_gap_oscar_number_field(FG::GapObj) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_abelian_closure(FG::GAP.GapObj) +function _iso_gap_oscar_abelian_closure(FG::GapObj) FO, _ = abelian_closure(QQ) finv, f = _iso_oscar_gap_abelian_closure_functions(FO, FG) return MapFromFunc(FG, FO, f, finv) end -function _iso_gap_oscar_univariate_polynomial_ring(RG::GAP.GapObj) +function _iso_gap_oscar_univariate_polynomial_ring(RG::GapObj) coeffs_iso = iso_gap_oscar(GAPWrap.LeftActingDomain(RG)) RO, x = polynomial_ring(codomain(coeffs_iso), "x", cached = false) finv, f = _iso_oscar_gap_polynomial_ring_functions(RO, RG, inv(coeffs_iso)) @@ -215,7 +215,7 @@ function _iso_gap_oscar_univariate_polynomial_ring(RG::GAP.GapObj) return MapFromFunc(RG, RO, f, finv) end -function _iso_gap_oscar_multivariate_polynomial_ring(RG::GAP.GapObj) +function _iso_gap_oscar_multivariate_polynomial_ring(RG::GapObj) coeffs_iso = iso_gap_oscar(GAPWrap.LeftActingDomain(RG)) nams = [string(x) for x in GAPWrap.IndeterminatesOfPolynomialRing(RG)] RO, x = polynomial_ring(codomain(coeffs_iso), nams, cached = false) @@ -226,7 +226,7 @@ end """ - Oscar.iso_gap_oscar(R) -> Map{GAP.GapObj, T} + Oscar.iso_gap_oscar(R) -> Map{GapObj, T} Return an isomorphism `f` with `domain` the GAP object `R` and `codomain` an Oscar object `S`. @@ -297,7 +297,7 @@ true but that the codomain of this map is not identical with or even not equal to the given `R`. """ -iso_gap_oscar(F::GAP.GapObj) = GAP.Globals.IsoGapOscar(F) +iso_gap_oscar(F::GapObj) = GAP.Globals.IsoGapOscar(F) # Compute the isomorphism between a GAP domain diff --git a/src/GAP/iso_oscar_gap.jl b/src/GAP/iso_oscar_gap.jl index 7b17cfa31e29..c7eb8acfb72b 100644 --- a/src/GAP/iso_oscar_gap.jl +++ b/src/GAP/iso_oscar_gap.jl @@ -32,7 +32,7 @@ end # Assume that `RO` and `RG` are residue rings of the same size # in Oscar and GAP, respectively. -function _iso_oscar_gap_residue_ring_functions(RO::Union{Nemo.zzModRing, Nemo.ZZModRing}, RG::GAP.GapObj) +function _iso_oscar_gap_residue_ring_functions(RO::Union{Nemo.zzModRing, Nemo.ZZModRing}, RG::GapObj) e = GAPWrap.One(RG) f(x) = GAP.Obj(lift(x))*e @@ -78,11 +78,11 @@ end # Assume that `FO` and `FG` are finite fields of the same order # in Oscar and GAP, respectively. -function _iso_oscar_gap_field_finite_functions(FO::Union{Nemo.fpField, Nemo.FpField}, FG::GAP.GapObj) +function _iso_oscar_gap_field_finite_functions(FO::Union{Nemo.fpField, Nemo.FpField}, FG::GapObj) return _make_prime_field_functions(FO, FG) end -function _iso_oscar_gap_field_finite_functions(FO::Union{FqPolyRepField, FqField, fqPolyRepField}, FG::GAP.GapObj) +function _iso_oscar_gap_field_finite_functions(FO::Union{FqPolyRepField, FqField, fqPolyRepField}, FG::GapObj) p = characteristic(FO) d = degree(FO) @@ -201,7 +201,7 @@ function _iso_oscar_gap(FO::FinField) e = one(GAPWrap.Z(p)) fam = GAPWrap.FamilyObj(e) - coeffsFG = GAP.GapObj([GAP.Obj(lift(x))*e for x in coeffsFO]) + coeffsFG = GapObj([GAP.Obj(lift(x))*e for x in coeffsFO]) polFG = GAPWrap.UnivariatePolynomialByCoefficients(fam, coeffsFG, 1) FG = GAPWrap.GF(p, polFG) end @@ -240,14 +240,14 @@ end # Assume that `FO` and `FG` are cyclotomic fields with the same conductor # in Oscar and GAP, respectively. # (Cyclotomic fields are easier to handle than general number fields.) -function _iso_oscar_gap_field_cyclotomic_functions(FO::AbsSimpleNumField, FG::GAP.GapObj) +function _iso_oscar_gap_field_cyclotomic_functions(FO::AbsSimpleNumField, FG::GapObj) N = conductor(FO) cycpol = GAPWrap.CyclotomicPol(N) dim = length(cycpol)-1 f = function(x::Nemo.AbsSimpleNumFieldElem) coeffs = [Nemo.coeff(x, i) for i in 0:(N-1)] - return GAPWrap.CycList(GAP.GapObj(coeffs; recursive=true)) + return GAPWrap.CycList(GapObj(coeffs; recursive=true)) end finv = function(x) @@ -268,7 +268,7 @@ end # Assume that `FO` and `FG` are quadratic fields with the same square root # in Oscar and GAP, respectively. # (Quadratic fields are easier to handle than general number fields.) -function _iso_oscar_gap_field_quadratic_functions(FO::AbsSimpleNumField, FG::GAP.GapObj) +function _iso_oscar_gap_field_quadratic_functions(FO::AbsSimpleNumField, FG::GapObj) flag, N = Hecke.is_quadratic_type(FO) @assert flag @@ -310,13 +310,13 @@ function _iso_oscar_gap(FO::SimpleNumField{QQFieldElem}) polFO = defining_polynomial(FO) coeffs_polFO = collect(coefficients(polFO)) fam = GAP.Globals.CyclotomicsFamily::GapObj - cfs = GAP.GapObj(coeffs_polFO, recursive = true)::GapObj + cfs = GapObj(coeffs_polFO, recursive = true)::GapObj polFG = GAPWrap.UnivariatePolynomialByCoefficients(fam, cfs, 1) FG = GAPWrap.AlgebraicExtension(GAP.Globals.Rationals::GapObj, polFG) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) f = function(x::SimpleNumFieldElem{QQFieldElem}) - coeffs = GAP.GapObj(coefficients(x), recursive = true)::GapObj + coeffs = GapObj(coefficients(x), recursive = true)::GapObj return GAPWrap.AlgExtElm(fam, coeffs) end @@ -338,13 +338,13 @@ function _iso_oscar_gap(FO::SimpleNumField{T}) where T <: FieldElem polFO = defining_polynomial(FO) coeffs_polFO = collect(coefficients(polFO)) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(BG)) - cfs = GAP.GapObj([isoB(x) for x in coeffs_polFO])::GapObj + cfs = GapObj([isoB(x) for x in coeffs_polFO])::GapObj polFG = GAPWrap.UnivariatePolynomialByCoefficients(fam, cfs, 1) FG = GAPWrap.AlgebraicExtension(BG, polFG) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) f = function(x::SimpleNumFieldElem{T}) - coeffs = GAP.GapObj([isoB(x) for x in coefficients(x)])::GapObj + coeffs = GapObj([isoB(x) for x in coefficients(x)])::GapObj return GAPWrap.AlgExtElm(fam, coeffs) end @@ -371,7 +371,7 @@ function _iso_oscar_gap(FO::NumField) isoB = iso_oscar_gap(B) f = function(x::NumFieldElem) - coeffs = GAP.GapObj([isoB(x) for x in coefficients(preimage(emb, x))])::GapObj + coeffs = GapObj([isoB(x) for x in coefficients(preimage(emb, x))])::GapObj return GAPWrap.AlgExtElm(fam, coeffs) end @@ -392,7 +392,7 @@ end # Assume that `FO` is a `QQAbField` and `FG` is `GAP.Globals.Cyclotomics`. -function _iso_oscar_gap_abelian_closure_functions(FO::QQAbField, FG::GAP.GapObj) +function _iso_oscar_gap_abelian_closure_functions(FO::QQAbField, FG::GapObj) return (GAP.julia_to_gap, QQAbElem) end @@ -404,7 +404,7 @@ function _iso_oscar_gap(FO::QQAbField) end """ - Oscar.iso_oscar_gap(R) -> Map{T, GAP.GapObj} + Oscar.iso_oscar_gap(R) -> Map{T, GapObj} Return an isomorphism `f` with domain `R` and `codomain` a GAP object `S`. @@ -492,13 +492,13 @@ end # # Univariate polynomial rings # -function _iso_oscar_gap_polynomial_ring_functions(RO::PolyRing{T}, RG::GAP.GapObj, coeffs_iso::MapFromFunc) where T +function _iso_oscar_gap_polynomial_ring_functions(RO::PolyRing{T}, RG::GapObj, coeffs_iso::MapFromFunc) where T fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(codomain(coeffs_iso))) ind = GAPWrap.IndeterminateNumberOfUnivariateRationalFunction( GAPWrap.IndeterminatesOfPolynomialRing(RG)[1]) f = function(x::PolyRingElem{T}) - cfs = GAP.GapObj([coeffs_iso(x) for x in coefficients(x)]) + cfs = GapObj([coeffs_iso(x) for x in coefficients(x)]) return GAPWrap.UnivariatePolynomialByCoefficients(fam, cfs, ind) end @@ -525,7 +525,7 @@ end # # Multivariate polynomial rings # -function _iso_oscar_gap_polynomial_ring_functions(RO::MPolyRing{T}, RG::GAP.GapObj, coeffs_iso::MapFromFunc) where T +function _iso_oscar_gap_polynomial_ring_functions(RO::MPolyRing{T}, RG::GapObj, coeffs_iso::MapFromFunc) where T fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(RG)) n = nvars(RO) indets = GAPWrap.IndeterminatesOfPolynomialRing(RG) @@ -541,10 +541,10 @@ function _iso_oscar_gap_polynomial_ring_functions(RO::MPolyRing{T}, RG::GAP.GapO append!(v, [i, l[i]]) end end - push!(extrep, GAP.GapObj(v)) + push!(extrep, GapObj(v)) push!(extrep, coeffs_iso(c)) end - return GAPWrap.PolynomialByExtRep(fam, GAP.GapObj(extrep)) + return GAPWrap.PolynomialByExtRep(fam, GapObj(extrep)) end finv = function(x) diff --git a/src/GAP/oscar_to_gap.jl b/src/GAP/oscar_to_gap.jl index d0816fa71d1c..9a6749ccdc15 100644 --- a/src/GAP/oscar_to_gap.jl +++ b/src/GAP/oscar_to_gap.jl @@ -37,7 +37,7 @@ end ## `QQAbElem` to GAP cyclotomic function GAP.julia_to_gap(elm::QQAbElem) coeffs = [Nemo.coeff(elm.data, i) for i in 0:(elm.c-1)] # QQFieldElem - return GAPWrap.CycList(GAP.GapObj(coeffs; recursive=true)) + return GAPWrap.CycList(GapObj(coeffs; recursive=true)) end ## matrix of elements of cyclotomic field to GAP matrix of cyclotomics @@ -72,3 +72,17 @@ function GAP.julia_to_gap( return gapset end + +## TODO: remove the following once GAP.jl has it +## (This will be the case when the change from +## https://github.com/oscar-system/GAP.jl/pull/989 +## will be available.) +using JSON3 + +function GAP.julia_to_gap( + obj::JSON3.Array, + recursion_dict::IdDict{Any,Any} = IdDict(); + recursive::Bool = false) + + return GAP.julia_to_gap(copy(obj), recursion_dict; recursive) +end diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index e6277f4461c8..27085e85099c 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -83,6 +83,7 @@ GAP.@wrap Elements(x::GapObj)::GapObj GAP.@wrap ElementsFamily(x::GapObj)::GapObj GAP.@wrap ELMS_LIST(x::GapObj, y::GapObj)::GapObj GAP.@wrap Embedding(x::GapObj, y::Int)::GapObj +GAP.@wrap EpimorphismSchurCover(x::GapObj)::GapObj GAP.@wrap ExtRepOfObj(x::GapObj)::GapObj GAP.@wrap ExtRepPolynomialRatFun(x::GapObj)::GapObj GAP.@wrap FamilyObj(x::GAP.Obj)::GapObj @@ -109,6 +110,7 @@ GAP.@wrap Identity(x::GapObj)::GapObj GAP.@wrap Image(x::Any)::GapObj GAP.@wrap Image(x::Any, y::Any)::GapObj GAP.@wrap ImagesRepresentative(x::GapObj, y::Any)::GAP.Obj +GAP.@wrap ImagesSource(x::GapObj)::GapObj GAP.@wrap ImmutableMatrix(x::GapObj, y::GapObj, z::Bool)::GapObj GAP.@wrap IndependentGeneratorExponents(x::Any, y::Any)::GapObj GAP.@wrap Indeterminate(x::GapObj)::GapObj @@ -192,6 +194,7 @@ GAP.@wrap IsomorphismFpGroupByGenerators(x::GapObj, y::GapObj)::GapObj GAP.@wrap IsomorphismFpGroupByPcgs(x::GapObj, y::GapObj)::GapObj GAP.@wrap IsOne(x::Any)::Bool GAP.@wrap IsPcGroup(x::Any)::Bool +GAP.@wrap IsPcpGroup(x::Any)::Bool GAP.@wrap IsPerfectGroup(x::Any)::Bool GAP.@wrap IsPermGroup(x::Any)::Bool GAP.@wrap IsPGroup(x::Any)::Bool diff --git a/src/Groups/GAPGroups.jl b/src/Groups/GAPGroups.jl index 8cd077a09e38..a03e88470a44 100644 --- a/src/Groups/GAPGroups.jl +++ b/src/Groups/GAPGroups.jl @@ -186,7 +186,7 @@ function Base.rand(rng::Random.AbstractRNG, G::GAPGroup) return group_element(G, s) end -function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{Gr}) where Gr<:Oscar.GAPGroup +function Base.rand(rng::Random.AbstractRNG, rs::Random.SamplerTrivial{Gr}) where Gr<:GAPGroup return rand(rng, rs[]) end @@ -234,6 +234,9 @@ end #We need a lattice of groups to implement this properly function _prod(x::T, y::T) where T <: GAPGroupElem +#T not nec. same type, +#T and for pc subgroups may need to go to the big group +#T (write tests that model this situation) G = _common_parent_group(parent(x), parent(y)) return group_element(G, GapObj(x)*GapObj(y)) end @@ -242,7 +245,7 @@ Base.:*(x::GAPGroupElem, y::GAPGroupElem) = _prod(x, y) ==(x::GAPGroup, y::GAPGroup) = GapObj(x) == GapObj(y) -==(x::T, y::T) where T <: BasicGAPGroupElem = GapObj(x) == GapObj(y) +==(x::BasicGAPGroupElem, y::BasicGAPGroupElem ) = GapObj(x) == GapObj(y) """ one(G::GAPGroup) -> elem_type(G) @@ -286,6 +289,7 @@ function Base.show(io::IO, G::FPGroup) end else print(io, "Finitely presented group") # FIXME: actually some of these groups are *not* finitely presented +#T introduce SubFPGroup if !is_terse(io) if has_order(G) if is_finite(G) @@ -330,10 +334,11 @@ function Base.show(io::IO, G::PermGroup) end end -function Base.show(io::IO, G::PcGroup) +function Base.show(io::IO, G::Union{PcGroup,SubPcGroup}) @show_name(io, G) @show_special(io, G) - print(io, "Pc group") + T = typeof(G) == PcGroup ? "Pc group" : "Sub-pc group" + print(io, T) if !is_terse(io) if isfinite(G) print(io, " of order ", order(G)) @@ -489,7 +494,7 @@ in general the length of this vector is not minimal. # Examples ```jldoctest -julia> length(small_generating_set(abelian_group(PcGroup, [2,3,4]))) +julia> length(small_generating_set(abelian_group(SubPcGroup, [2,3,4]))) 2 julia> length(small_generating_set(abelian_group(PermGroup, [2,3,4]))) @@ -512,7 +517,7 @@ Return a vector of minimal length of elements in `G` that generate `G`. # Examples ```jldoctest -julia> length(minimal_generating_set(abelian_group(PcGroup, [2,3,4]))) +julia> length(minimal_generating_set(abelian_group(SubPcGroup, [2,3,4]))) 2 julia> length(minimal_generating_set(abelian_group(PermGroup, [2,3,4]))) @@ -760,12 +765,13 @@ end # START subgroups conjugation """ - conjugacy_class(G::T, H::T) where T<:Group -> GroupConjClass + conjugacy_class(G::Group, H::Group) -> GroupConjClass Return the subgroup conjugacy class `cc` of `H` in `G`, where `H` = `representative`(`cc`). """ -function conjugacy_class(G::T, g::T) where T<:GAPGroup - return GAPGroupConjClass(G, g, GAPWrap.ConjugacyClassSubgroups(GapObj(G),GapObj(g))) +function conjugacy_class(G::GAPGroup, H::GAPGroup) +#T _check_compatible + return GAPGroupConjClass(G, H, GAPWrap.ConjugacyClassSubgroups(GapObj(G),GapObj(H))) end function Base.rand(C::GroupConjClass{S,T}) where S where T<:GAPGroup @@ -773,7 +779,7 @@ function Base.rand(C::GroupConjClass{S,T}) where S where T<:GAPGroup end function Base.rand(rng::Random.AbstractRNG, C::GroupConjClass{S,T}) where S where T<:GAPGroup - return _oscar_group(GAP.Globals.Random(GAP.wrap_rng(rng), C.CC), acting_group(C)) + return _oscar_subgroup(GAP.Globals.Random(GAP.wrap_rng(rng), C.CC), acting_group(C)) end """ @@ -842,9 +848,10 @@ julia> maximal_subgroup_classes(G) """ @gapattribute function maximal_subgroup_classes(G::GAPGroup) L = Vector{GapObj}(GAP.Globals.ConjugacyClassesMaximalSubgroups(GapObj(G))::GapObj) - T = typeof(G) + TG = typeof(G) + TS = sub_type(TG) LL = [GAPGroupConjClass(G, _as_subgroup_bare(G, GAPWrap.Representative(cc)), cc) for cc in L] - return Vector{GAPGroupConjClass{T, T}}(LL) + return Vector{GAPGroupConjClass{TG, TS}}(LL) end """ @@ -921,7 +928,7 @@ Permutation group of degree 4 and order 3 """ function conjugate_group(G::T, x::GAPGroupElem) where T <: GAPGroup @req check_parent(G, x) "G and x are not compatible" - return _oscar_group(GAPWrap.ConjugateSubgroup(GapObj(G), GapObj(x)), G) + return _oscar_subgroup(GAPWrap.ConjugateSubgroup(GapObj(G), GapObj(x)), G) end Base.:^(H::GAPGroup, y::GAPGroupElem) = conjugate_group(H, y) @@ -1152,7 +1159,7 @@ Return `N, f`, where `N` is the normalizer of `H` in `G`, i.e., the largest subgroup of `G` in which `H` is normal, and `f` is the embedding morphism of `N` into `G`. """ -normalizer(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.Normalizer(GapObj(G), GapObj(H))) +normalizer(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.Normalizer(GapObj(G), GapObj(H))) """ normalizer(G::Group, x::GAPGroupElem) @@ -1169,7 +1176,7 @@ Return `C, f`, where `C` is the normal core of `H` in `G`, that is, the largest normal subgroup of `G` that is contained in `H`, and `f` is the embedding morphism of `C` into `G`. """ -core(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.Core(GapObj(G), GapObj(H))) +core(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.Core(GapObj(G), GapObj(H))) """ normal_closure(G::Group, H::Group) @@ -1180,7 +1187,7 @@ and `f` is the embedding morphism of `N` into `G`. Note that `H` must be a subgroup of `G`. """ -normal_closure(G::T, H::T) where T<:GAPGroup = _as_subgroup(G, GAPWrap.NormalClosure(GapObj(G), GapObj(H))) +normal_closure(G::GAPGroup, H::GAPGroup) = _as_subgroup(G, GAPWrap.NormalClosure(GapObj(G), GapObj(H))) # Note: # GAP admits `NormalClosure` also when `H` is not a subgroup of `G`, @@ -1361,7 +1368,7 @@ an exception is thrown if `G` is not solvable. end @doc raw""" - complement_classes(G::T, N::T) where T <: GAPGroup + complement_classes(G::GAPGroup, N::GAPGroup) Return a vector of the conjugacy classes of complements of the normal subgroup `N` in `G`. @@ -1383,20 +1390,20 @@ julia> G = dihedral_group(8) Pc group of order 8 julia> complement_classes(G, center(G)[1]) -GAPGroupConjClass{PcGroup, PcGroup}[] +GAPGroupConjClass{PcGroup, SubPcGroup}[] ``` """ -function complement_classes(G::T, N::T) where T <: GAPGroup +function complement_classes(G::T, N::GAPGroup) where T <: GAPGroup res_gap = GAP.Globals.ComplementClassesRepresentatives(GapObj(G), GapObj(N))::GapObj if length(res_gap) == 0 - return GAPGroupConjClass{T, T}[] + return GAPGroupConjClass{T, sub_type(T)}[] else return [conjugacy_class(G, H) for H in _as_subgroups(G, res_gap)] end end @doc raw""" - complements(G::T, N::T) where T <: GAPGroup + complements(G::GAPGroup, N::GAPGroup) Return an iterator over the complements of the normal subgroup `N` in `G`. Very likely it is better to use [`complement_classes`](@ref) instead. @@ -1409,7 +1416,7 @@ julia> describe(first(complements(G, derived_subgroup(G)[1]))) "C2" ``` """ -complements(G::T, N::T) where T <: GAPGroup = Iterators.flatten(complement_classes(G, N)) +complements(G::GAPGroup, N::GAPGroup) = Iterators.flatten(complement_classes(G, N)) @doc raw""" complement_system(G::Group) @@ -1910,7 +1917,7 @@ function map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector( end gX = GapObj(g) - if GAP.Globals.IsPcGroup(GapObj(G)) + if GAPWrap.IsPcGroup(GapObj(G)) l = GAP.Globals.ExponentsOfPcElement(GAP.Globals.FamilyPcgs(GapObj(G)), gX) else # GAP.Globals.IsPcpGroup(GapObj(G)) l = GAP.Globals.Exponents(gX) @@ -2269,7 +2276,12 @@ function describe(G::FPGroup) if !has_is_finite(G) # try to obtain an isomorphic permutation group, but don't try too hard - iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G), 100000)::GapObj +#TODO: With GAP 4.13.0, the prescribed bound 100000 will cause a test failure. +# This regression will hopefully be fixed in GAP 4.13.1, +# see https://github.com/gap-system/gap/issues/5697 +# and https://github.com/gap-system/gap/pull/5698. +# iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G), 100000)::GapObj + iso = GAP.Globals.IsomorphismPermGroupOrFailFpGroup(GapObj(G))::GapObj iso != GAP.Globals.fail && return describe(PermGroup(GAPWrap.Range(iso))) elseif is_finite(G) return describe(PermGroup(G)) diff --git a/src/Groups/abelian_aut.jl b/src/Groups/abelian_aut.jl index 6f51e8f8674a..0ca2ef10fad8 100644 --- a/src/Groups/abelian_aut.jl +++ b/src/Groups/abelian_aut.jl @@ -2,7 +2,8 @@ const AutGrpAbTor = Union{AutomorphismGroup{FinGenAbGroup},AutomorphismGroup{Tor const AutGrpAbTorElem = Union{AutomorphismGroupElem{FinGenAbGroup},AutomorphismGroupElem{TorQuadModule}} const AbTorElem = Union{FinGenAbGroupElem,TorQuadModuleElem} -function _isomorphic_gap_group(A::FinGenAbGroup; T=PcGroup) +# function _isomorphic_gap_group(A::FinGenAbGroup; T=PcGroup) +function _isomorphic_gap_group(A::FinGenAbGroup; T=SubPcGroup) iso = isomorphism(T, A) iso2 = inv(iso) return codomain(iso), iso, iso2 diff --git a/src/Groups/action.jl b/src/Groups/action.jl index c94124aca1d0..d14f95d48b02 100644 --- a/src/Groups/action.jl +++ b/src/Groups/action.jl @@ -31,7 +31,7 @@ The following ones are commonly used. ## common actions of group elements ## ## The idea is to delegate the action of `GAPGroupElem` objects -## on `GAP.GapObj` objects to the corresponding GAP action, +## on `GapObj` objects to the corresponding GAP action, ## and to implement the action on native Julia objects case by case. """ @@ -66,7 +66,7 @@ GAP: [ Z(3)^0, 0*Z(3) ] """ - on_tuples(tuple::GAP.GapObj, x::GAPGroupElem) + on_tuples(tuple::GapObj, x::GAPGroupElem) on_tuples(tuple::Vector, x::GAPGroupElem) on_tuples(tuple::T, x::GAPGroupElem) where T <: Tuple @@ -81,7 +81,7 @@ one can also call `^` instead of `on_tuples`. julia> g = symmetric_group(3); g[1] (1,2,3) -julia> l = GAP.GapObj([1, 2, 4]) +julia> l = GapObj([1, 2, 4]) GAP: [ 1, 2, 4 ] julia> on_tuples(l, g[1]) @@ -110,7 +110,7 @@ on_tuples(tuple::T, x::GAPGroupElem) where T <: Tuple = T(pnt^x for pnt in tuple """ - on_sets(set::GAP.GapObj, x::GAPGroupElem) + on_sets(set::GapObj, x::GAPGroupElem) on_sets(set::Vector, x::GAPGroupElem) on_sets(set::Tuple, x::GAPGroupElem) on_sets(set::AbstractSet, x::GAPGroupElem) @@ -127,7 +127,7 @@ For `Set` objects, one can also call `^` instead of `on_sets`. julia> g = symmetric_group(3); g[1] (1,2,3) -julia> l = GAP.GapObj([1, 3]) +julia> l = GapObj([1, 3]) GAP: [ 1, 3 ] julia> on_sets(l, g[1]) @@ -171,7 +171,7 @@ end ^(set::AbstractSet, x::GAPGroupElem) = on_sets(set, x) """ - on_sets_sets(set::GAP.GapObj, x::GAPGroupElem) + on_sets_sets(set::GapObj, x::GAPGroupElem) on_sets_sets(set::Vector, x::GAPGroupElem) on_sets_sets(set::Tuple, x::GAPGroupElem) on_sets_sets(set::AbstractSet, x::GAPGroupElem) @@ -186,7 +186,7 @@ respectively. julia> g = symmetric_group(3); g[1] (1,2,3) -julia> l = GAP.GapObj([[1, 2], [3, 4]], recursive = true) +julia> l = GapObj([[1, 2], [3, 4]], recursive = true) GAP: [ [ 1, 2 ], [ 3, 4 ] ] julia> on_sets_sets(l, g[1]) @@ -234,7 +234,7 @@ end """ - permuted(pnt::GAP.GapObj, x::PermGroupElem) + permuted(pnt::GapObj, x::PermGroupElem) permuted(pnt::Vector, x::PermGroupElem) permuted(pnt::Tuple, x::PermGroupElem) @@ -261,7 +261,7 @@ julia> permuted(a, g[1]) julia> permuted(("a", "b", "c"), g[1]) ("c", "a", "b") -julia> l = GAP.GapObj(a, recursive = true) +julia> l = GapObj(a, recursive = true) GAP: [ "a", "b", "c" ] julia> permuted(l, g[1]) @@ -282,7 +282,7 @@ end @doc raw""" - on_indeterminates(f::GAP.GapObj, p::PermGroupElem) + on_indeterminates(f::GapObj, p::PermGroupElem) on_indeterminates(f::MPolyRingElem, p::PermGroupElem) on_indeterminates(f::FreeAssAlgElem, p::PermGroupElem) on_indeterminates(f::MPolyIdeal, p::PermGroupElem) @@ -343,7 +343,7 @@ function on_indeterminates(f::FreeAssAlgElem{T}, s::PermGroupElem) where T end @doc raw""" - on_indeterminates(f::GAP.GapObj, p::MatrixGroupElem) + on_indeterminates(f::GapObj, p::MatrixGroupElem) on_indeterminates(f::MPolyRingElem{T}, p::MatrixGroupElem{T}) where T on_indeterminates(f::MPolyIdeal, p::MatrixGroupElem) @@ -415,7 +415,7 @@ end # because for example `*(::fpFieldElem, ::Vector{fpFieldElem})` # is not supported. """ - on_lines(line::GAP.GapObj, x::GAPGroupElem) + on_lines(line::GapObj, x::GAPGroupElem) on_lines(line::AbstractAlgebra.Generic.FreeModuleElem, x::GAPGroupElem) Return the image of the nonzero vector `line` under `x`, @@ -469,7 +469,7 @@ julia> S = automorphism_group(C) Aut( ) julia> H, _ = sub(C, [gens(C)[1]^4]) -(Pc group of order 5, Hom: H -> C) +(Sub-pc group of order 5, Hom: H -> C) julia> all(g -> on_subgroups(H, g) == H, S) true @@ -535,7 +535,7 @@ stabilizer(G::MatrixGroup{ET,MT}, pnt::AbstractSet{AbstractAlgebra.Generic.FreeM """ - right_coset_action(G::T, U::T) where T <: GAPGroup + right_coset_action(G::GAPGroup, U::GAPGroup) Compute the action of `G` on the right cosets of its subgroup `U`. @@ -555,7 +555,8 @@ julia> degree(codomain(act)) == index(G, H) true ``` """ -function right_coset_action(G::T, U::T) where T <: GAPGroup +function right_coset_action(G::GAPGroup, U::GAPGroup) + _check_compatible(G, U) mp = GAP.Globals.FactorCosetAction(G.X, U.X) @req mp !== GAP.Globals.fail "Invalid input" H = PermGroup(GAPWrap.Range(mp)) diff --git a/src/Groups/cosets.jl b/src/Groups/cosets.jl index 02e135e62ebf..048b5418071d 100644 --- a/src/Groups/cosets.jl +++ b/src/Groups/cosets.jl @@ -8,7 +8,7 @@ and they contain the same elements. """ struct GroupCoset{T<: GAPGroup, S <: GAPGroupElem} G::T # big group containing the subgroup and the element - H::T # subgroup + 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) @@ -73,7 +73,6 @@ Right coset of Sym(3) ``` """ function right_coset(H::GAPGroup, g::GAPGroupElem) - @assert elem_type(H) == typeof(g) @req GAPWrap.IsSubset(parent(g).X, H.X) "H is not a subgroup of parent(g)" return _group_coset(parent(g), H, g, :right, GAP.Globals.RightCoset(H.X,g.X)) end @@ -102,7 +101,6 @@ Left coset of Sym(3) ``` """ function left_coset(H::GAPGroup, g::GAPGroupElem) - @assert elem_type(H) == typeof(g) @req GAPWrap.IsSubset(parent(g).X, H.X) "H is not a subgroup of parent(g)" return _group_coset(parent(g), H, g, :left, GAP.Globals.RightCoset(GAP.Globals.ConjugateSubgroup(H.X,GAP.Globals.Inverse(g.X)),g.X)) end @@ -243,7 +241,7 @@ true is_bicoset(C::GroupCoset) = GAPWrap.IsBiCoset(C.X) """ - right_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup + right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) Return the G-set that describes the right cosets of `H` in `G`. @@ -270,12 +268,13 @@ julia> collect(rc) Right coset of H with representative (1,4,3) ``` """ -function right_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup +function right_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) +#T _check_compatible(G, H) ? return GSetBySubgroupTransversal(G, H, :right, check = check) end """ - left_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup + left_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) Return the G-set that describes the left cosets of `H` in `G`. @@ -295,7 +294,8 @@ Left cosets of Sym(4) ``` """ -function left_cosets(G::T, H::T; check::Bool=true) where T<: GAPGroup +function left_cosets(G::GAPGroup, H::GAPGroup; check::Bool=true) +#T _check_compatible(G, H) ? return GSetBySubgroupTransversal(G, H, :left, check = check) end @@ -361,7 +361,7 @@ end """ - right_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup + right_transversal(G::GAPGroup, H::GAPGroup; check::Bool=true) Return a vector containing a complete set of representatives for the right cosets of `H` in `G`. @@ -391,14 +391,17 @@ julia> collect(T) (1,4,3) ``` """ -function right_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup - @req (!check || GAPWrap.IsSubset(G.X, H.X)) "H is not a subgroup of G" - return SubgroupTransversal{T, T, eltype(T)}(G, H, :right, +function right_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup + if check + @req GAPWrap.IsSubset(GapObj(G), GapObj(H)) "H is not a subgroup of G" + _check_compatible(G, H) + end + return SubgroupTransversal{T1, T2, eltype(T1)}(G, H, :right, GAP.Globals.RightTransversal(G.X, H.X)) end """ - left_transversal(G::T, H::T; check::Bool=true) where T<: Group + left_transversal(G::GAPGroup, H::GAPGroup; check::Bool=true) Return a vector containing a complete set of representatives for the left cosets for `H` in `G`. @@ -428,9 +431,12 @@ julia> collect(T) (1,3,4) ``` """ -function left_transversal(G::T, H::T; check::Bool=true) where T<: GAPGroup - @req (!check || GAPWrap.IsSubset(G.X, H.X)) "H is not a subgroup of G" - return SubgroupTransversal{T, T, eltype(T)}(G, H, :left, +function left_transversal(G::T1, H::T2; check::Bool=true) where T1 <: GAPGroup where T2 <: GAPGroup + if check + @req GAPWrap.IsSubset(GapObj(G), GapObj(H)) "H is not a subgroup of G" + _check_compatible(G, H) + end + return SubgroupTransversal{T1, T2, eltype(T1)}(G, H, :left, GAP.Globals.RightTransversal(G.X, H.X)) end @@ -454,8 +460,8 @@ 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 G::T - H::T - K::T + H::GAPGroup + K::GAPGroup repr::S X::GapObj end @@ -518,7 +524,8 @@ Double coset of Sym(3) in Sym(5) ``` """ -function double_coset(G::T, g::GAPGroupElem{T}, H::T) where T<: GAPGroup +function double_coset(G::GAPGroup, g::GAPGroupElem, H::GAPGroup) +#T what if g is in some subgroup of a group of which G, H are also a subgroup? @req GAPWrap.IsSubset(parent(g).X,G.X) "G is not a subgroup of parent(g)" @req GAPWrap.IsSubset(parent(g).X,H.X) "H is not a subgroup of parent(g)" return GroupDoubleCoset(parent(g),G,H,g,GAP.Globals.DoubleCoset(G.X,g.X,H.X)) @@ -527,7 +534,7 @@ end Base.:*(H::GAPGroup, g::GAPGroupElem, K::GAPGroup) = double_coset(H,g,K) """ - double_cosets(G::T, H::T, K::T; check::Bool=true) where T<: GAPGroup + double_cosets(G::GAPGroup, H::GAPGroup, K::GAPGroup; check::Bool=true) Return a vector of all the double cosets `HxK` for `x` in `G`. If `check == false`, do not check whether `H` and `K` are subgroups of `G`. @@ -550,7 +557,7 @@ julia> double_cosets(G,H,K) Double coset of H and K with representative (1,4,3) ``` """ -function double_cosets(G::T, H::T, K::T; check::Bool=true) where T<: GAPGroup +function double_cosets(G::T, H::GAPGroup, K::GAPGroup; check::Bool=true) where T <: GAPGroup if !check dcs = GAP.Globals.DoubleCosetsNC(G.X,H.X,K.X) else diff --git a/src/Groups/directproducts.jl b/src/Groups/directproducts.jl index df8f48083015..213d70ce7427 100644 --- a/src/Groups/directproducts.jl +++ b/src/Groups/directproducts.jl @@ -68,7 +68,7 @@ end Return a direct product of groups of the same type `T` as a group of type `T`. It works for `T` of the following types: -- `PermGroup`, `PcGroup`, `FPGroup`. +- `PermGroup`, `PcGroup`, `SubPcGroup`, `FPGroup`. The keyword argument `morphisms` is `false` by default. If it is set `true`, then the output is a triple (`G`, `emb`, `proj`), where `emb` and `proj` are the @@ -76,7 +76,7 @@ vectors of the embeddings (resp. projections) of the direct product `G`. """ function inner_direct_product( L::AbstractVector{T}; morphisms::Bool=false -) where {T<:Union{PcGroup,FPGroup}} +) where {T<:Union{PcGroup,SubPcGroup,FPGroup}} @req length(L) > 0 "the collection of groups must be non-empty" P = GAP.Globals.DirectProduct(GapObj(L; recursive=true)) DP = T(P) @@ -110,7 +110,7 @@ end function inner_direct_product( L::T, Ls::T...; morphisms::Bool=false -) where {T<:Union{PcGroup,PermGroup,FPGroup}} +) where {T<:Union{PcGroup,SubPcGroup,PermGroup,FPGroup}} return inner_direct_product([L, Ls...]; morphisms=morphisms) end @@ -471,8 +471,8 @@ case, `n` is NOT the number of moved points, but the degree of `H`. If `W` is a wreath product of `G` and `H`, {`g_1`, ..., `g_n`} are elements of `G` and `h` in `H`, the element `(g_1, ..., h)` of `W` can be obtained by typing -``` - W(g_1,...,g_n, h). +```julia +W(g_1,...,g_n, h). ``` # Examples diff --git a/src/Groups/group_characters.jl b/src/Groups/group_characters.jl index 427a3ed3ff24..da4a8668a90b 100644 --- a/src/Groups/group_characters.jl +++ b/src/Groups/group_characters.jl @@ -136,7 +136,7 @@ in a `p`-modular table. end # access to field values via functions -GAPTable(tbl::GAPGroupCharacterTable) = tbl.GAPTable +GapObj(tbl::GAPGroupCharacterTable) = tbl.GAPTable @doc raw""" characteristic(::Type{T} = Int, tbl::GAPGroupCharacterTable) where T <: IntegerUnion @@ -185,7 +185,8 @@ end function isomorphism_to_GAP_group(G::FinGenAbGroup) @req isfinite(G) "the group is not finite" - iso = isomorphism(PcGroup, G) +# iso = isomorphism(PcGroup, G) + iso = isomorphism(SubPcGroup, G) C = codomain(iso) @assert C isa GAPGroup f = function(x) return iso(x).X; end @@ -243,10 +244,10 @@ true @attr function conjugacy_classes(tbl::GAPGroupCharacterTable) G = group(tbl) - # If `GAPTable(tbl)` does not yet store conjugacy classes + # If `GapObj(tbl)` does not yet store conjugacy classes # then compute them. if characteristic(tbl) == 0 - ccl = GAPWrap.ConjugacyClasses(GAPTable(tbl))::GapObj + ccl = GAPWrap.ConjugacyClasses(GapObj(tbl))::GapObj ccl = [conjugacy_class(G, preimage(isomorphism_to_GAP_group(tbl), GAPWrap.Representative(x))) @@ -446,7 +447,7 @@ julia> is_duplicate_table(character_table("A6M2")) true ``` """ -@gapattribute is_duplicate_table(tbl::GAPGroupCharacterTable) = GAP.Globals.IsDuplicateTable(GAPTable(tbl))::Bool +@gapattribute is_duplicate_table(tbl::GAPGroupCharacterTable) = GAP.Globals.IsDuplicateTable(GapObj(tbl))::Bool """ all_character_table_names(L...; ordered_by = nothing) @@ -793,7 +794,7 @@ $ """ function Base.show(io::IO, ::MIME"text/plain", tbl::GAPGroupCharacterTable) n = nrows(tbl) - gaptbl = GAPTable(tbl) + gaptbl = GapObj(tbl) size = order(ZZRingElem, tbl) primes = [x[1] for x in collect(factor(size))] sort!(primes) @@ -937,10 +938,10 @@ end ############################################################################## # -length(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GAPTable(tbl))::Int -number_of_rows(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GAPTable(tbl))::Int -number_of_columns(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GAPTable(tbl))::Int -number_of_conjugacy_classes(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GAPTable(tbl))::Int +length(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GapObj(tbl))::Int +number_of_rows(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GapObj(tbl))::Int +number_of_columns(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GapObj(tbl))::Int +number_of_conjugacy_classes(tbl::GAPGroupCharacterTable) = GAPWrap.NrConjugacyClasses(GapObj(tbl))::Int @doc raw""" order(::Type{T} = ZZRingElem, tbl::GAPGroupCharacterTable) where T <: IntegerUnion @@ -957,7 +958,7 @@ julia> order(character_table(symmetric_group(4))) order(tbl::GAPGroupCharacterTable) = order(ZZRingElem, tbl) function order(::Type{T}, tbl::GAPGroupCharacterTable) where T <: IntegerUnion - return T(GAPWrap.Size(GAPTable(tbl))) + return T(GAPWrap.Size(GapObj(tbl))) end @doc raw""" @@ -972,7 +973,7 @@ julia> println(orders_class_representatives(character_table("A5"))) [1, 2, 3, 5, 5] ``` """ -@gapattribute orders_class_representatives(tbl::GAPGroupCharacterTable) = Vector{Int}(GAP.Globals.OrdersClassRepresentatives(GAPTable(tbl))::GapObj) +@gapattribute orders_class_representatives(tbl::GAPGroupCharacterTable) = Vector{Int}(GAP.Globals.OrdersClassRepresentatives(GapObj(tbl))::GapObj) @doc raw""" orders_centralizers(tbl::GAPGroupCharacterTable) @@ -987,7 +988,7 @@ julia> println(orders_centralizers(character_table("A5"))) ZZRingElem[60, 4, 3, 5, 5] ``` """ -@gapattribute orders_centralizers(tbl::GAPGroupCharacterTable) = Vector{ZZRingElem}(GAP.Globals.SizesCentralizers(GAPTable(tbl))::GAP.Obj) +@gapattribute orders_centralizers(tbl::GAPGroupCharacterTable) = Vector{ZZRingElem}(GAP.Globals.SizesCentralizers(GapObj(tbl))::GAP.Obj) @doc raw""" class_lengths(tbl::GAPGroupCharacterTable) @@ -998,7 +999,7 @@ julia> println(class_lengths(character_table("A5"))) ZZRingElem[1, 15, 20, 12, 12] ``` """ -@gapattribute class_lengths(tbl::GAPGroupCharacterTable) = Vector{ZZRingElem}(GAP.Globals.SizesConjugacyClasses(GAPTable(tbl))::GapObj) +@gapattribute class_lengths(tbl::GAPGroupCharacterTable) = Vector{ZZRingElem}(GAP.Globals.SizesConjugacyClasses(GapObj(tbl))::GapObj) @doc raw""" maxes(tbl::GAPGroupCharacterTable) @@ -1023,8 +1024,8 @@ true ``` """ function maxes(tbl::GAPGroupCharacterTable) - if GAPWrap.HasMaxes(GAPTable(tbl)) - return Vector{String}(GAPWrap.Maxes(GAPTable(tbl))) + if GAPWrap.HasMaxes(GapObj(tbl)) + return Vector{String}(GAPWrap.Maxes(GapObj(tbl))) end return nothing end @@ -1041,7 +1042,7 @@ julia> identifier(character_table("A5")) "A5" ``` """ -@gapattribute identifier(tbl::GAPGroupCharacterTable) = string(GAP.Globals.Identifier(GAPTable(tbl))::GapObj) +@gapattribute identifier(tbl::GAPGroupCharacterTable) = string(GAP.Globals.Identifier(GapObj(tbl))::GapObj) @doc raw""" @@ -1064,7 +1065,7 @@ julia> class_positions_of_normal_subgroups(t) ``` """ function class_positions_of_normal_subgroups(tbl::GAPGroupCharacterTable) - return Vector{Vector{Int}}(GAPWrap.ClassPositionsOfNormalSubgroups(GAPTable(tbl))) + return Vector{Vector{Int}}(GAPWrap.ClassPositionsOfNormalSubgroups(GapObj(tbl))) end @@ -1084,7 +1085,7 @@ julia> println(class_positions_of_center(tbl)) ``` """ function class_positions_of_center(tbl::GAPGroupCharacterTable) - return Vector{Int}(GAPWrap.ClassPositionsOfCentre(GAPTable(tbl))) + return Vector{Int}(GAPWrap.ClassPositionsOfCentre(GapObj(tbl))) end @@ -1103,7 +1104,7 @@ julia> println(class_positions_of_derived_subgroup(tbl)) ``` """ function class_positions_of_derived_subgroup(tbl::GAPGroupCharacterTable) - return Vector{Int}(GAPWrap.ClassPositionsOfDerivedSubgroup(GAPTable(tbl))) + return Vector{Int}(GAPWrap.ClassPositionsOfDerivedSubgroup(GapObj(tbl))) end @@ -1126,7 +1127,7 @@ julia> println(class_positions_of_solvable_residuum(tbl)) ``` """ function class_positions_of_solvable_residuum(tbl::GAPGroupCharacterTable) - return Vector{Int}(GAPWrap.ClassPositionsOfSolvableResiduum(GAPTable(tbl))) + return Vector{Int}(GAPWrap.ClassPositionsOfSolvableResiduum(GapObj(tbl))) end @@ -1143,7 +1144,7 @@ julia> println(class_positions_of_pcore(character_table("2.A5"), 2)) [1, 2] ``` """ -class_positions_of_pcore(tbl::GAPGroupCharacterTable, p::IntegerUnion) = Vector{Int}(GAPWrap.ClassPositionsOfPCore(GAPTable(tbl), GAP.Obj(p))) +class_positions_of_pcore(tbl::GAPGroupCharacterTable, p::IntegerUnion) = Vector{Int}(GAPWrap.ClassPositionsOfPCore(GapObj(tbl), GAP.Obj(p))) @doc raw""" pcore(tbl::GAPGroupCharacterTable, p::IntegerUnion) @@ -1160,7 +1161,7 @@ julia> order(pcore(character_table(symmetric_group(4)), 2)[1]) """ function pcore(tbl::GAPGroupCharacterTable, p::IntegerUnion) iso = isomorphism_to_GAP_group(tbl) - t = GAPTable(tbl) + t = GapObj(tbl) pcorepos = GAPWrap.ClassPositionsOfPCore(t, GAP.Obj(p)) P = GAPWrap.NormalSubgroupClasses(t, pcorepos) return preimages(iso, P) @@ -1171,7 +1172,7 @@ function class_positions_of_kernel(fus::Vector{Int}) end function Base.getindex(tbl::GAPGroupCharacterTable, i::Int) - irr = GAPWrap.Irr(GAPTable(tbl)) + irr = GAPWrap.Irr(GapObj(tbl)) return class_function(tbl, irr[i]) end #TODO: cache the irreducibles in the table @@ -1185,7 +1186,7 @@ function Base.keys(tbl::GAPGroupCharacterTable) end function Base.getindex(tbl::GAPGroupCharacterTable, i::Int, j::Int) - irr = GAPWrap.Irr(GAPTable(tbl)) + irr = GAPWrap.Irr(GapObj(tbl)) val = irr[i, j] return QQAbElem(val) end @@ -1216,7 +1217,7 @@ function Base.mod(tbl::GAPGroupCharacterTable, p::T) where T <: IntegerUnion modtbls = get_attribute!(() -> Dict{Int,Any}(), tbl, :brauer_tables) if ! haskey(modtbls, p) - modtblgap = mod(GAPTable(tbl), GAP.Obj(p))::GapObj + modtblgap = mod(GapObj(tbl), GAP.Obj(p))::GapObj if modtblgap === GAP.Globals.fail modtbls[p] = nothing elseif isdefined(tbl, :group) @@ -1257,7 +1258,7 @@ julia> decomposition_matrix(t2) """ function decomposition_matrix(modtbl::GAPGroupCharacterTable) @req is_prime(characteristic(modtbl)) "characteristic of tbl must be a prime integer" - return matrix(ZZ, GAPWrap.DecompositionMatrix(GAPTable(modtbl))) + return matrix(ZZ, GAPWrap.DecompositionMatrix(GapObj(modtbl))) end @@ -1287,7 +1288,7 @@ julia> println(proj) """ function quo(tbl::GAPGroupCharacterTable, nclasses::Vector{Int}) @req characteristic(tbl) == 0 "supported only for ordinary character tables" - gap_fact = GAPWrap.CharacterTableFactorGroup(GAPTable(tbl), GapObj(nclasses)) + gap_fact = GAPWrap.CharacterTableFactorGroup(GapObj(tbl), GapObj(nclasses)) fact = GAPGroupCharacterTable(gap_fact, 0) flag, fus = known_class_fusion(tbl, fact) @assert flag @@ -1335,7 +1336,7 @@ julia> class_multiplication_coefficient(character_table("A5"), 2, 4, 4) ``` """ function class_multiplication_coefficient(::Type{T}, tbl::GAPGroupCharacterTable, i::Int, j::Int, k::Int) where T <: IntegerUnion - return T(GAPWrap.ClassMultiplicationCoefficient(GAPTable(tbl), i, j, k)) + return T(GAPWrap.ClassMultiplicationCoefficient(GapObj(tbl), i, j, k)) end class_multiplication_coefficient(tbl::GAPGroupCharacterTable, i::Int, j::Int, k::Int) = class_multiplication_coefficient(ZZRingElem, tbl, i, j, k) @@ -1364,7 +1365,7 @@ Union{Int64, Vector{Int64}}[1, 2, [3, 4], [6, 7], [6, 7]] """ function approximate_class_fusion(subtbl::GAPGroupCharacterTable, tbl::GAPGroupCharacterTable) - fus = GAPWrap.InitFusion(GAPTable(subtbl), GAPTable(tbl)) + fus = GAPWrap.InitFusion(GapObj(subtbl), GapObj(tbl)) res = Union{Int, Vector{Int}}[] fus == GAP.Globals.fail && return res for i in 1:length(fus) @@ -1417,7 +1418,7 @@ function possible_class_fusions(subtbl::GAPGroupCharacterTable, if length(fusionmap) != 0 cond[:fusionmap] = GapObj(fusionmap, recursive = true) end - fus = GAPWrap.PossibleClassFusions(GAPTable(subtbl), GAPTable(tbl), + fus = GAPWrap.PossibleClassFusions(GapObj(subtbl), GapObj(tbl), GapObj(cond)) return [Vector{Int}(x::GapObj) for x in fus] end @@ -1444,7 +1445,7 @@ Dict{Symbol, Vector{Int64}} with 2 entries: """ function block_distribution(tbl::GAPGroupCharacterTable, p::IntegerUnion) @req characteristic(tbl) == 0 "character table must be ordinary" - blocks = GAP.Globals.PrimeBlocks(GAPTable(tbl), GAP.Obj(p)) + blocks = GAP.Globals.PrimeBlocks(GapObj(tbl), GAP.Obj(p)) return Dict(:defect => Vector{Int}(blocks.defect), :block => Vector{Int}(blocks.block)) end @@ -1508,7 +1509,7 @@ julia> character_parameters(character_table("M11")) """ function character_parameters(tbl::GAPGroupCharacterTable) return get_attribute!(tbl, :character_parameters) do - GAPt = GAPTable(tbl) + GAPt = GapObj(tbl) GAPWrap.HasCharacterParameters(GAPt) || return nothing paras = Vector{GAP.Obj}(GAPWrap.CharacterParameters(GAPt)) return _translate_parameter_list(paras) @@ -1538,7 +1539,7 @@ julia> class_parameters(character_table("M11")) """ function class_parameters(tbl::GAPGroupCharacterTable) return get_attribute!(tbl, :class_parameters) do - GAPt = GAPTable(tbl) + GAPt = GapObj(tbl) GAPWrap.HasClassParameters(GAPt) || return nothing paras = Vector{GAP.Obj}(GAPWrap.ClassParameters(GAPt)) return _translate_parameter_list(paras) @@ -1563,7 +1564,7 @@ julia> println(class_names(character_table("S5"))) """ function class_names(tbl::GAPGroupCharacterTable) return get_attribute!(tbl, :class_names) do - return Vector{String}(GAPWrap.ClassNames(GAPTable(tbl))) + return Vector{String}(GAPWrap.ClassNames(GapObj(tbl))) end end @@ -1587,7 +1588,7 @@ true ``` """ function names_of_fusion_sources(tbl::GAPGroupCharacterTable) - return [string(name) for name in GAPWrap.NamesOfFusionSources(GAPTable(tbl))] + return [string(name) for name in GAPWrap.NamesOfFusionSources(GapObj(tbl))] end @doc raw""" @@ -1620,7 +1621,7 @@ julia> known_class_fusion(t2, t1) ``` """ function known_class_fusion(subtbl::GAPGroupCharacterTable, tbl::GAPGroupCharacterTable) - map = GAPWrap.GetFusionMap(GAPTable(subtbl), GAPTable(tbl)) + map = GAPWrap.GetFusionMap(GapObj(subtbl), GapObj(tbl)) if map === GAP.Globals.fail return (false, Int[]) else @@ -1638,7 +1639,7 @@ see [`known_class_fusion`](@ref). """ function known_class_fusions(tbl::GAPGroupCharacterTable) return Tuple{String, Vector{Int64}}[(String(r.name), Vector{Int}(r.map)) - for r in GAPWrap.ComputedClassFusions(GAPTable(tbl))] + for r in GAPWrap.ComputedClassFusions(GapObj(tbl))] end @@ -1663,7 +1664,7 @@ julia> is_abelian(character_table("C2")) true ``` """ -@gapattribute is_abelian(tbl::GAPGroupCharacterTable) = GAP.Globals.IsAbelian(GAPTable(tbl))::Bool +@gapattribute is_abelian(tbl::GAPGroupCharacterTable) = GAP.Globals.IsAbelian(GapObj(tbl))::Bool """ @@ -1681,7 +1682,7 @@ julia> is_almost_simple(character_table("S4")) false ``` """ -@gapattribute is_almost_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsAlmostSimple(GAPTable(tbl))::Bool +@gapattribute is_almost_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsAlmostSimple(GapObj(tbl))::Bool """ @@ -1699,7 +1700,7 @@ julia> is_cyclic(character_table("S4")) false ``` """ -@gapattribute is_cyclic(tbl::GAPGroupCharacterTable) = GAP.Globals.IsCyclic(GAPTable(tbl))::Bool +@gapattribute is_cyclic(tbl::GAPGroupCharacterTable) = GAP.Globals.IsCyclic(GapObj(tbl))::Bool """ @@ -1718,7 +1719,7 @@ julia> is_elementary_abelian(character_table("S4")) false ``` """ -@gapattribute is_elementary_abelian(tbl::GAPGroupCharacterTable) = GAP.Globals.IsElementaryAbelian(GAPTable(tbl))::Bool +@gapattribute is_elementary_abelian(tbl::GAPGroupCharacterTable) = GAP.Globals.IsElementaryAbelian(GapObj(tbl))::Bool """ @@ -1736,7 +1737,7 @@ julia> is_nilpotent(character_table("S4")) false ``` """ -@gapattribute is_nilpotent(tbl::GAPGroupCharacterTable) = GAP.Globals.IsNilpotent(GAPTable(tbl))::Bool +@gapattribute is_nilpotent(tbl::GAPGroupCharacterTable) = GAP.Globals.IsNilpotent(GapObj(tbl))::Bool """ @@ -1754,7 +1755,7 @@ julia> is_perfect(character_table("S4")) false ``` """ -@gapattribute is_perfect(tbl::GAPGroupCharacterTable) = GAP.Globals.IsPerfect(GAPTable(tbl))::Bool +@gapattribute is_perfect(tbl::GAPGroupCharacterTable) = GAP.Globals.IsPerfect(GapObj(tbl))::Bool """ @@ -1772,7 +1773,7 @@ julia> is_quasisimple(character_table("S4")) false ``` """ -@gapattribute is_quasisimple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsQuasisimple(GAPTable(tbl))::Bool +@gapattribute is_quasisimple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsQuasisimple(GapObj(tbl))::Bool """ @@ -1790,7 +1791,7 @@ julia> is_simple(character_table("S4")) false ``` """ -@gapattribute is_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSimple(GAPTable(tbl))::Bool +@gapattribute is_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSimple(GapObj(tbl))::Bool """ @@ -1808,7 +1809,7 @@ julia> is_solvable(character_table("S4")) true ``` """ -@gapattribute is_solvable(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSolvable(GAPTable(tbl))::Bool +@gapattribute is_solvable(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSolvable(GapObj(tbl))::Bool """ @@ -1827,7 +1828,7 @@ julia> is_sporadic_simple(character_table("M11")) true ``` """ -@gapattribute is_sporadic_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSporadicSimple(GAPTable(tbl))::Bool +@gapattribute is_sporadic_simple(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSporadicSimple(GapObj(tbl))::Bool """ @@ -1845,7 +1846,7 @@ julia> is_supersolvable(character_table("S3")) true ``` """ -@gapattribute is_supersolvable(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSupersolvable(GAPTable(tbl))::Bool +@gapattribute is_supersolvable(tbl::GAPGroupCharacterTable) = GAP.Globals.IsSupersolvable(GapObj(tbl))::Bool ############################################################################# @@ -1859,6 +1860,12 @@ struct GAPGroupClassFunction <: GroupClassFunction values::GapObj end +GapObj(chi::GAPGroupClassFunction) = chi.values + +# The following is needed for recursive `GapObj` calls with arrays +# of class functions. +GAP.julia_to_gap(chi::GAPGroupClassFunction) = chi.values + parent(chi::GAPGroupClassFunction) = chi.table function Base.show(io::IO, chi::GAPGroupClassFunction) @@ -1866,7 +1873,7 @@ function Base.show(io::IO, chi::GAPGroupClassFunction) end function values(chi::GAPGroupClassFunction) - gapvalues = GAPWrap.ValuesOfClassFunction(chi.values) + gapvalues = GAPWrap.ValuesOfClassFunction(GapObj(chi)) return [QQAbElem(x) for x in gapvalues] end @@ -1881,7 +1888,7 @@ end function class_function(tbl::GAPGroupCharacterTable, values::Vector{<:Union{Integer, ZZRingElem, Rational, QQFieldElem, QQAbElem}}) gapvalues = GapObj([GAP.Obj(x) for x in values]) - return GAPGroupClassFunction(tbl, GAPWrap.ClassFunction(GAPTable(tbl), gapvalues)) + return GAPGroupClassFunction(tbl, GAPWrap.ClassFunction(GapObj(tbl), gapvalues)) end function class_function(G::GAPGroup, values::GapObj) @@ -2041,7 +2048,7 @@ function natural_character(G::MatrixGroup{T, MT}) where T <: FinFieldElem where tbl = character_table(G, p) ccl = conjugacy_classes(tbl) vals = [GAP.Globals.BrauerCharacterValue(representative(x).X) for x in ccl] - vals = GAPWrap.ClassFunction(GAPTable(tbl), GapObj(vals)) + vals = GAPWrap.ClassFunction(GapObj(tbl), GapObj(vals)) return class_function(G, vals) end @@ -2088,7 +2095,7 @@ function natural_character(rho::GAPGroupHomomorphism) modtbl = mod(tbl, p) ccl = conjugacy_classes(modtbl) # p-regular classes vals = [GAP.Globals.BrauerCharacterValue(rho(representative(x)).X) for x in ccl] - vals = GAPWrap.ClassFunction(GAPTable(modtbl), GapObj(vals)) + vals = GAPWrap.ClassFunction(GapObj(modtbl), GapObj(vals)) end else throw(ArgumentError("codomain must be a PermGroup or MatrixGroup")) @@ -2130,7 +2137,7 @@ julia> length(linear_characters(tbl)) ``` """ function linear_characters(tbl::GAPGroupCharacterTable) - return [class_function(tbl, chi) for chi in GAPWrap.LinearCharacters(GAPTable(tbl))] + return [class_function(tbl, chi) for chi in GAPWrap.LinearCharacters(GapObj(tbl))] end @@ -2170,10 +2177,10 @@ function induce(chi::GAPGroupClassFunction, tbl::GAPGroupCharacterTable) subtbl = parent(chi) if !(isdefined(tbl, :group) && isdefined(subtbl, :group)) # If there is no stored group then let GAP try to find the result. - fus = GAPWrap.FusionConjugacyClasses(GAPTable(subtbl), GAPTable(tbl)) + fus = GAPWrap.FusionConjugacyClasses(GapObj(subtbl), GapObj(tbl)) @req fus !== GAP.Globals.fail "class fusion is not uniquely determinaed" - ind = GAPWrap.InducedClassFunctionsByFusionMap(GAPTable(subtbl), - GAPTable(tbl), GapObj([chi.values]), GapObj(fus)) + ind = GAPWrap.InducedClassFunctionsByFusionMap(GapObj(subtbl), + GapObj(tbl), GapObj([chi]; recursive = true), GapObj(fus)) return GAPGroupClassFunction(tbl, ind[1]) else # Dispatch on the types of the stored groups. @@ -2182,14 +2189,14 @@ function induce(chi::GAPGroupClassFunction, tbl::GAPGroupCharacterTable) end function induce(chi::GAPGroupClassFunction, tbl::GAPGroupCharacterTable, fusion::Vector{Int}) - ind = GAPWrap.InducedClassFunctionsByFusionMap(GAPTable(parent(chi)), - GAPTable(tbl), GapObj([chi.values]), GapObj(fusion)) + ind = GAPWrap.InducedClassFunctionsByFusionMap(GapObj(parent(chi)), + GapObj(tbl), GapObj([chi]; recursive=true), GapObj(fusion)) return GAPGroupClassFunction(tbl, ind[1]) end # If `GAPGroup` groups are stored then we let GAP try to find the result. function _induce(chi::GAPGroupClassFunction, tbl::GAPGroupCharacterTable, G_chi::GAPGroup, G_tbl::GAPGroup) - ind = GAPWrap.InducedClassFunction(chi.values, GAPTable(tbl)) + ind = GAPWrap.InducedClassFunction(GapObj(chi), GapObj(tbl)) return GAPGroupClassFunction(tbl, ind) end @@ -2227,7 +2234,7 @@ true ``` """ function induced_cyclic(tbl::GAPGroupCharacterTable, classes::AbstractVector{Int} = 1:nrows(tbl)) - return [GAPGroupClassFunction(tbl, chi) for chi in GAPWrap.InducedCyclic(GAPTable(tbl), GapObj(classes))] + return [GAPGroupClassFunction(tbl, chi) for chi in GAPWrap.InducedCyclic(GapObj(tbl), GapObj(classes))] end """ @@ -2262,10 +2269,10 @@ function restrict(chi::GAPGroupClassFunction, subtbl::GAPGroupCharacterTable) if !(isdefined(tbl, :group) && isdefined(subtbl, :group)) # If there is no stored group then let GAP try to find the result. - fus = GAPWrap.FusionConjugacyClasses(GAPTable(subtbl), GAPTable(tbl)) + fus = GAPWrap.FusionConjugacyClasses(GapObj(subtbl), GapObj(tbl)) @req fus !== GAP.Globals.fail "class fusion is not uniquely determinaed" - rest = GAPWrap.ELMS_LIST(chi.values, GapObj(fus)) - rest = GAPWrap.ClassFunction(GAPTable(subtbl), rest) + rest = GAPWrap.ELMS_LIST(GapObj(chi), GapObj(fus)) + rest = GAPWrap.ClassFunction(GapObj(subtbl), rest) return GAPGroupClassFunction(subtbl, rest) else # Dispatch on the types of the stored groups. @@ -2274,17 +2281,17 @@ function restrict(chi::GAPGroupClassFunction, subtbl::GAPGroupCharacterTable) end function restrict(chi::GAPGroupClassFunction, subtbl::GAPGroupCharacterTable, fusion::Vector{Int}) - rest = GAPWrap.ELMS_LIST(chi.values, GapObj(fusion)) - rest = GAPWrap.ClassFunction(GAPTable(subtbl), rest) + rest = GAPWrap.ELMS_LIST(GapObj(chi), GapObj(fusion)) + rest = GAPWrap.ClassFunction(GapObj(subtbl), rest) return GAPGroupClassFunction(subtbl, rest) end # If `GAPGroup` groups are stored then we let GAP try to find the result. function _restrict(chi::GAPGroupClassFunction, subtbl::GAPGroupCharacterTable, G_chi::GAPGroup, G_tbl::GAPGroup) tbl = parent(chi) - fus = GAPWrap.FusionConjugacyClasses(GAPTable(subtbl), GAPTable(tbl)) - rest = GAPWrap.ELMS_LIST(chi.values, GapObj(fus)) - rest = GAPWrap.ClassFunction(GAPTable(subtbl), rest) + fus = GAPWrap.FusionConjugacyClasses(GapObj(subtbl), GapObj(tbl)) + rest = GAPWrap.ELMS_LIST(GapObj(chi), GapObj(fus)) + rest = GAPWrap.ClassFunction(GapObj(subtbl), rest) return GAPGroupClassFunction(subtbl, rest) end @@ -2303,9 +2310,9 @@ end restrict(chi::GAPGroupClassFunction, H::Union{GAPGroup, FinGenAbGroup}) = restrict(chi, character_table(H)) -Base.length(chi::GAPGroupClassFunction) = length(chi.values) +Base.length(chi::GAPGroupClassFunction) = length(GapObj(chi)) -Base.iterate(chi::GAPGroupClassFunction, state = 1) = state > length(chi.values) ? nothing : (chi[state], state+1) +Base.iterate(chi::GAPGroupClassFunction, state = 1) = state > length(GapObj(chi)) ? nothing : (chi[state], state+1) @doc raw""" degree(::Type{T} = QQFieldElem, chi::GAPGroupClassFunction) @@ -2325,7 +2332,7 @@ Nemo.degree(::Type{T}, chi::GAPGroupClassFunction) where T <: IntegerUnion = T(N # access character values by position function Base.getindex(chi::GAPGroupClassFunction, i::Int) - vals = GAPWrap.ValuesOfClassFunction(chi.values) + vals = GAPWrap.ValuesOfClassFunction(GapObj(chi)) return QQAbElem(vals[i]) end @@ -2339,7 +2346,7 @@ end # arithmetic with class functions function Base.:(==)(chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) @req parent(chi) === parent(psi) "character tables must be identical" - return chi.values == psi.values + return GapObj(chi) == GapObj(psi) end # Currently we cannot implement a `hash` method based on the values, @@ -2350,19 +2357,19 @@ end function Base.:+(chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) @req parent(chi) === parent(psi) "character tables must be identical" - return GAPGroupClassFunction(parent(chi), chi.values + psi.values) + return GAPGroupClassFunction(parent(chi), GapObj(chi) + GapObj(psi)) end -Base.:-(chi::GAPGroupClassFunction) = GAPGroupClassFunction(parent(chi), - chi.values) +Base.:-(chi::GAPGroupClassFunction) = GAPGroupClassFunction(parent(chi), -GapObj(chi)) function Base.:-(chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) @req parent(chi) === parent(psi) "character tables must be identical" - return GAPGroupClassFunction(parent(chi), chi.values - psi.values) + return GAPGroupClassFunction(parent(chi), GapObj(chi) - GapObj(psi)) end function Base.:*(chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) @req parent(chi) === parent(psi) "character tables must be identical" - return GAPGroupClassFunction(parent(chi), chi.values * psi.values) + return GAPGroupClassFunction(parent(chi), GapObj(chi) * GapObj(psi)) end function Base.zero(chi::GAPGroupClassFunction) @@ -2389,7 +2396,7 @@ scalar_product(chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) = scalar_ function scalar_product(::Type{T}, chi::GAPGroupClassFunction, psi::GAPGroupClassFunction) where T <: Union{Integer, ZZRingElem, QQFieldElem, QQAbElem} @req parent(chi) === parent(psi) "character tables must be identical" - return T(GAPWrap.ScalarProduct(GAPTable(parent(chi)), chi.values, psi.values))::T + return T(GAPWrap.ScalarProduct(GapObj(parent(chi)), GapObj(chi), GapObj(psi)))::T end @@ -2432,13 +2439,13 @@ coordinates(chi::GAPGroupClassFunction) = coordinates(QQFieldElem, chi) function coordinates(::Type{T}, chi::GAPGroupClassFunction) where T <: Union{Integer, ZZRingElem, QQFieldElem, QQAbElem} t = parent(chi) - GAPt = GAPTable(t) + GAPt = GapObj(t) if characteristic(t) == 0 # use scalar products for an ordinary character - c = GAPWrap.MatScalarProducts(GAPt, GAPWrap.Irr(GAPt), GapObj([chi.values])) + c = GAPWrap.MatScalarProducts(GAPt, GAPWrap.Irr(GAPt), GapObj([chi]; recursive = true)) else # decompose a Brauer character - c = GAPWrap.Decomposition(GAPWrap.Irr(GAPt), GapObj([chi.values]), GapObj("nonnegative")) + c = GAPWrap.Decomposition(GAPWrap.Irr(GAPt), GapObj([chi]; recursive = true), GapObj("nonnegative")) end return Vector{T}(c[1])::Vector{T} end @@ -2467,18 +2474,18 @@ julia> println(multiplicities_eigenvalues(chi, 5)) ``` """ function multiplicities_eigenvalues(::Type{T}, chi::GAPGroupClassFunction, i::Int) where T <: IntegerUnion - return Vector{T}(GAPWrap.EigenvaluesChar(chi.values, i)) + return Vector{T}(GAPWrap.EigenvaluesChar(GapObj(chi), i)) end multiplicities_eigenvalues(chi::GAPGroupClassFunction, i::Int) = multiplicities_eigenvalues(Int, chi, i) function Base.:*(n::IntegerUnion, chi::GAPGroupClassFunction) - return GAPGroupClassFunction(parent(chi), n * chi.values) + return GAPGroupClassFunction(parent(chi), n * GapObj(chi)) end function Base.:^(chi::GAPGroupClassFunction, n::IntegerUnion) - return GAPGroupClassFunction(parent(chi), chi.values ^ n) + return GAPGroupClassFunction(parent(chi), GapObj(chi) ^ n) end function Base.:^(chi::GAPGroupClassFunction, tbl::GAPGroupCharacterTable) @@ -2512,7 +2519,7 @@ julia> println([findfirst(y -> y == conj(x), tbl) for x in tbl]) ``` """ function conj(chi::GAPGroupClassFunction) - return GAPGroupClassFunction(parent(chi), GAPWrap.GaloisCyc(chi.values, -1)) + return GAPGroupClassFunction(parent(chi), GAPWrap.GaloisCyc(GapObj(chi), -1)) end @doc raw""" @@ -2522,7 +2529,7 @@ Return the class function whose values are the images of the values of `chi` under `sigma`. """ function (sigma::QQAbAutomorphism)(chi::GAPGroupClassFunction) - return GAPGroupClassFunction(parent(chi), GAPWrap.GaloisCyc(chi.values, sigma.exp)) + return GAPGroupClassFunction(parent(chi), GAPWrap.GaloisCyc(GapObj(chi), sigma.exp)) end Base.:^(chi::GAPGroupClassFunction, sigma::QQAbAutomorphism) = sigma(chi) @@ -2569,7 +2576,7 @@ false ``` """ function is_irreducible(chi::GAPGroupClassFunction) - return GAPWrap.IsIrreducibleCharacter(chi.values) + return GAPWrap.IsIrreducibleCharacter(GapObj(chi)) end @doc raw""" @@ -2649,7 +2656,7 @@ julia> C, f = kernel(chi); order(C) function kernel(chi::GAPGroupClassFunction) tbl = parent(chi) iso = isomorphism_to_GAP_group(tbl) - GAP_K = GAPWrap.KernelOfCharacter(GAPTable(tbl), chi.values) + GAP_K = GAPWrap.KernelOfCharacter(GapObj(tbl), GapObj(chi)) return preimages(iso, GAP_K) end @@ -2666,7 +2673,7 @@ julia> println(class_positions_of_center(character_table("2.A5")[2])) ``` """ function class_positions_of_center(chi::GAPGroupClassFunction) - return Vector{Int}(GAPWrap.ClassPositionsOfCentre(chi.values)) + return Vector{Int}(GAPWrap.ClassPositionsOfCentre(GapObj(chi))) end @doc raw""" @@ -2691,7 +2698,7 @@ julia> C, f = center(chi); order(C) function center(chi::GAPGroupClassFunction) tbl = parent(chi) iso = isomorphism_to_GAP_group(tbl) - C = GAPWrap.CentreOfCharacter(GAPTable(tbl), chi.values) + C = GAPWrap.CentreOfCharacter(GapObj(tbl), GapObj(chi)) return preimages(iso, C) end @@ -2711,7 +2718,7 @@ true ``` """ function det(chi::GAPGroupClassFunction) - values = GAPWrap.DeterminantOfCharacter(chi.values) + values = GAPWrap.DeterminantOfCharacter(GapObj(chi)) return GAPGroupClassFunction(parent(chi), values) end @@ -2733,7 +2740,7 @@ ZZRingElem[2, 1, 2, 2, 1] order(chi::GAPGroupClassFunction) = order(ZZRingElem, chi)::ZZRingElem function order(::Type{T}, chi::GAPGroupClassFunction) where T <: IntegerUnion - return T(GAPWrap.Order(det(chi).values))::T + return T(GAPWrap.Order(GapObj(det(chi))))::T end @doc raw""" @@ -2759,14 +2766,14 @@ function indicator(chi::GAPGroupClassFunction, n::Int = 2) tbl = parent(chi) if characteristic(tbl) == 0 # The indicator can be computed for any character. - ind = GAPWrap.Indicator(GAPTable(tbl), GapObj([chi.values]), n) + ind = GAPWrap.Indicator(GapObj(tbl), GapObj([chi]; recursive = true), n) return ind[1]::Int else # The indicator is defined only for `n = 2` and irreducible characters. @req n == 2 "defined for Brauer characters only for n = 2" chipos = findfirst(isequal(chi), tbl) @req chipos !== nothing "defined only for irreducible Brauer characters" - ind = GAPWrap.Indicator(GAPTable(tbl), n) + ind = GAPWrap.Indicator(GapObj(tbl), n) return ind[chipos]::Int end end @@ -2809,7 +2816,7 @@ function character_field(chi::GAPGroupClassFunction) return (F, identity_map(F)) end - values = chi.values # a list of GAP cyclotomics + values = GapObj(chi) # a list of GAP cyclotomics gapfield = GAPWrap.Field(values) N = GAPWrap.Conductor(gapfield) FF, _ = abelian_closure(QQ) @@ -2880,7 +2887,7 @@ function order_field_of_definition(::Type{T}, chi::GAPGroupClassFunction) where T <: IntegerUnion p = characteristic(chi) @req p != 0 "the character must be a Brauer character" - return T(GAPWrap.SizeOfFieldOfDefinition(chi.values, p)) + return T(GAPWrap.SizeOfFieldOfDefinition(GapObj(chi), p)) end @@ -2902,7 +2909,7 @@ ZZRingElem[1, 5, 5, 1, 1] conductor(chi::GAPGroupClassFunction) = conductor(ZZRingElem, chi) function conductor(::Type{T}, chi::GAPGroupClassFunction) where T <: IntegerUnion - return T(GAPWrap.Conductor(chi.values)) + return T(GAPWrap.Conductor(GapObj(chi))) end @@ -2930,7 +2937,7 @@ function schur_index(chi::GAPGroupClassFunction, recurse::Bool = true) indicator(chi) == -1 && return 2 # The character field contains an `m`-th root of unity. - values = chi.values + values = GapObj(chi) if conj(chi) == chi bound = ZZRingElem(2) else @@ -3027,8 +3034,8 @@ function symmetrizations(characters::Vector{GAPGroupClassFunction}, n::Int) length(characters) == 0 && return eltype(typeof(characters))[] tbl = parent(characters[1]) return [class_function(tbl, chi) - for chi in GAPWrap.Symmetrizations(GAPTable(tbl), - GAP.GapObj([chi.values for chi in characters]), n)] + for chi in GAPWrap.Symmetrizations(GapObj(tbl), + GapObj(characters; recursive = true), n)] end @doc raw""" @@ -3042,8 +3049,8 @@ function symmetric_parts(characters::Vector{GAPGroupClassFunction}, n::Int) length(characters) == 0 && return eltype(typeof(characters))[] tbl = parent(characters[1]) return [class_function(tbl, chi) - for chi in GAPWrap.SymmetricParts(GAPTable(tbl), - GAP.GapObj([chi.values for chi in characters]), n)] + for chi in GAPWrap.SymmetricParts(GapObj(tbl), + GapObj(characters; recursive = true), n)] end @doc raw""" @@ -3057,8 +3064,8 @@ function anti_symmetric_parts(characters::Vector{GAPGroupClassFunction}, n::Int) length(characters) == 0 && return eltype(typeof(characters))[] tbl = parent(characters[1]) return [class_function(tbl, chi) - for chi in GAPWrap.AntiSymmetricParts(GAPTable(tbl), - GAP.GapObj([chi.values for chi in characters]), n)] + for chi in GAPWrap.AntiSymmetricParts(GapObj(tbl), + GapObj(characters; recursive = true), n)] end @doc raw""" @@ -3075,7 +3082,7 @@ function exterior_power(chi::GAPGroupClassFunction, n::Int) #T when GAP's `ExteriorPower` method becomes available then use it tbl = parent(chi) return class_function(tbl, - GAPWrap.AntiSymmetricParts(GAPTable(tbl), GAP.Obj([chi.values]), n)[1]) + GAPWrap.AntiSymmetricParts(GapObj(tbl), GAP.Obj([chi]; recursive = true), n)[1]) end @doc raw""" @@ -3092,7 +3099,7 @@ function symmetric_power(chi::GAPGroupClassFunction, n::Int) #T when GAP's `SymmetricPower` method becomes available then use it tbl = parent(chi) return class_function(tbl, - GAPWrap.SymmetricParts(GAPTable(tbl), GAP.Obj([chi.values]), n)[1]) + GAPWrap.SymmetricParts(GapObj(tbl), GAP.Obj([chi]; recursive = true), n)[1]) end @doc raw""" @@ -3109,8 +3116,8 @@ function orthogonal_components(characters::Vector{GAPGroupClassFunction}, n::Int length(characters) == 0 && return eltype(typeof(characters))[] tbl = parent(characters[1]) return [class_function(tbl, chi) - for chi in GAPWrap.OrthogonalComponents(GAPTable(tbl), - GAP.GapObj([chi.values for chi in characters]), n)] + for chi in GAPWrap.OrthogonalComponents(GapObj(tbl), + GapObj(characters; recursive = true), n)] end @doc raw""" @@ -3127,8 +3134,8 @@ function symplectic_components(characters::Vector{GAPGroupClassFunction}, n::Int length(characters) == 0 && return eltype(typeof(characters))[] tbl = parent(characters[1]) return [class_function(tbl, chi) - for chi in GAPWrap.SymplecticComponents(GAPTable(tbl), - GAP.GapObj([chi.values for chi in characters]), n)] + for chi in GAPWrap.SymplecticComponents(GapObj(tbl), + GapObj(characters; recursive = true), n)] end function character_table_complex_reflection_group(m::Int, p::Int, n::Int) diff --git a/src/Groups/group_constructors.jl b/src/Groups/group_constructors.jl index 6eddfe41b1ef..8053e5f0555b 100644 --- a/src/Groups/group_constructors.jl +++ b/src/Groups/group_constructors.jl @@ -5,13 +5,20 @@ ################################################################################ _gap_filter(::Type{PermGroup}) = GAP.Globals.IsPermGroup -_gap_filter(::Type{PcGroup}) = GAP.Globals.IsPcGroupOrPcpGroup +_gap_filter(::Type{SubPcGroup}) = GAP.Globals.IsPcGroupOrPcpGroup _gap_filter(::Type{FPGroup}) = GAP.Globals.IsSubgroupFpGroup # TODO: matrix group handling usually is more complex: there usually # is another extra argument then to specify the base field # `_gap_filter(::Type{MatrixGroup})` is on the file `matrices/MatGrp.jl` +# We use `GAP.Globals.IsPcGroupOrPcpGroup` as `_gap_filter` result for +# both `SubPcGroup` and `PcGroup`. +# Note that `_gap_filter` is used for creating Oscar groups from GAP groups. +# In the case of `PcGroup`, we wrap the result differently in the +# call `PcGroup(G)`. +_gap_filter(::Type{PcGroup}) = GAP.Globals.IsPcGroupOrPcpGroup + """ symmetric_group(n::Int) @@ -110,11 +117,12 @@ function cyclic_group(::Type{T}, n::Union{IntegerUnion,PosInf}) where T <: GAPGr return T(GAP.Globals.CyclicGroup(_gap_filter(T), GAP.Obj(n))::GapObj) end -function cyclic_group(::Type{PcGroup}, n::Union{IntegerUnion,PosInf}) +# special handling for pc groups: distinguish on the GAP side +function cyclic_group(::Type{T}, n::Union{IntegerUnion,PosInf}) where T <: Union{PcGroup, SubPcGroup} if is_infinite(n) - return PcGroup(GAP.Globals.AbelianPcpGroup(1, GAP.GapObj([]))) + return T(GAP.Globals.AbelianPcpGroup(1, GapObj([]))) elseif n > 0 - return PcGroup(GAP.Globals.CyclicGroup(GAP.Globals.IsPcGroup, GAP.Obj(n))::GapObj) + return T(GAP.Globals.CyclicGroup(GAP.Globals.IsPcGroup, GAP.Obj(n))::GapObj) end throw(ArgumentError("n must be a positive even integer or infinity")) end @@ -161,11 +169,15 @@ end =# @doc raw""" - abelian_group(::Type{T}, v::Vector{Int}) where T <: Group -> PcGroup + abelian_group(::Type{T} = PcGroup, v::Vector{S}) where T <: Group where S <: IntegerUnion Return the direct product of cyclic groups of the orders `v[1]`, `v[2]`, $\ldots$, `v[n]`, as an instance of `T`. -Here, `T` must be one of `PermGroup`, `FPGroup`, or `PcGroup`. +Here, `T` must be one of `PermGroup`, `FPGroup`, `PcGroup`, or `SubPcGroup`. + +The `gens` value of the returned group corresponds to `v`, that is, +the number of generators is equal to `length(v)` +and the order of the `i`-th generator is `v[i]`. !!! warning The type need to be specified in the input of the function `abelian_group`, @@ -179,11 +191,22 @@ function abelian_group(::Type{T}, v::Vector{S}) where T <: GAPGroup where S <: I end # Delegating to the GAP constructor via `_gap_filter` does not work here. -function abelian_group(::Type{PcGroup}, v::Vector{T}) where T <: IntegerUnion +function abelian_group(::Type{TG}, v::Vector{T}) where TG <: Union{PcGroup, SubPcGroup} where T <: IntegerUnion if 0 in v - return PcGroup(GAP.Globals.AbelianPcpGroup(length(v), GAP.GapObj(v, recursive=true))) +# if 0 in v || (TG == PcGroup && any(x -> ! is_prime(x), v)) +#TODO: Currently GAP's IsPcpGroup groups run into problems +# already in the available Oscar tests, +# see https://github.com/gap-packages/polycyclic/issues/88, +# so we keep the code from the master branch here. + # We cannot construct an `IsPcGroup` group if some generator shall have + # order infinity or 1 or a composed number. + return TG(GAP.Globals.AbelianPcpGroup(length(v), GapObj(v, recursive=true))) + elseif TG == PcGroup && any(x -> ! is_prime(x), v) + # GAP's IsPcGroup groups cannot have generators that correspond to the + # orders given by `v` and to the defining presentation. + error("cannot create a PcGroup group with relative orders $v, perhaps try SubPcGroup") else - return PcGroup(GAP.Globals.AbelianGroup(GAP.Globals.IsPcGroup, GAP.GapObj(v, recursive=true))) + return TG(GAP.Globals.AbelianGroup(GAP.Globals.IsPcGroup, GapObj(v, recursive=true))) end end @@ -485,23 +508,23 @@ function free_group(n::Int, s::VarName = :f; eltype::Symbol = :letter) @req n >= 0 "n must be a non-negative integer" t = s isa Char ? string(s) : s if eltype == :syllable - G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(t); FreeGroupFamilyType = GapObj("syllable"))::GapObj) + G = FPGroup(GAP.Globals.FreeGroup(n, GapObj(t); FreeGroupFamilyType = GapObj("syllable"))::GapObj) else - G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(t))::GapObj) + G = FPGroup(GAP.Globals.FreeGroup(n, GapObj(t))::GapObj) end GAP.Globals.SetRankOfFreeGroup(GapObj(G), n) return G end function free_group(L::Vector{<:VarName}) - J = GAP.GapObj(L, recursive = true) + J = GapObj(L, recursive = true) G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj) GAP.Globals.SetRankOfFreeGroup(GapObj(G), length(J)) return G end function free_group(L::Vector{<:Char}) - J = GAP.GapObj(Symbol.(L), recursive = true) + J = GapObj(Symbol.(L), recursive = true) G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj) GAP.Globals.SetRankOfFreeGroup(GapObj(G), length(J)) return G @@ -536,7 +559,7 @@ end dihedral_group(::Type{T} = PcGroup, n::Union{IntegerUnion,PosInf}) Return the dihedral group of order `n`, as an instance of `T`, -where `T` is in {`PcGroup`,`PermGroup`,`FPGroup`}. +where `T` is in {`PcGroup`, `SubPcGroup`, `PermGroup`, `FPGroup`}. !!! warning @@ -569,11 +592,11 @@ function dihedral_group(::Type{T}, n::Union{IntegerUnion,PosInf}) where T <: GAP end # Delegating to the GAP constructor via `_gap_filter` does not work here. -function dihedral_group(::Type{PcGroup}, n::Union{IntegerUnion,PosInf}) +function dihedral_group(::Type{T}, n::Union{IntegerUnion,PosInf}) where T <: Union{PcGroup, SubPcGroup} if is_infinite(n) - return PcGroup(GAP.Globals.DihedralPcpGroup(0)) + return T(GAP.Globals.DihedralPcpGroup(0)) elseif iseven(n) && n > 0 - return PcGroup(GAP.Globals.DihedralGroup(GAP.Globals.IsPcGroup, GAP.Obj(n))::GapObj) + return T(GAP.Globals.DihedralGroup(GAP.Globals.IsPcGroup, GAP.Obj(n))::GapObj) end throw(ArgumentError("n must be a positive even integer or infinity")) end @@ -601,7 +624,8 @@ false Return the (generalized) quaternion group of order `n`, as an instance of `T`, -where `n` is a power of 2 and `T` is in {`PcGroup`,`PermGroup`,`FPGroup`}. +where `n` is a power of 2 and `T` is in +{`PcGroup`, `SubPcGroup`, `PermGroup`,`FPGroup`}. # Examples ```jldoctest @@ -633,9 +657,9 @@ function quaternion_group(::Type{T}, n::IntegerUnion) where T <: GAPGroup end # Delegating to the GAP constructor via `_gap_filter` does not work here. -function quaternion_group(::Type{PcGroup}, n::IntegerUnion) +function quaternion_group(::Type{T}, n::IntegerUnion) where T <: Union{PcGroup, SubPcGroup} @assert iszero(mod(n, 4)) - return PcGroup(GAP.Globals.QuaternionGroup(GAP.Globals.IsPcGroup, n)::GapObj) + return T(GAP.Globals.QuaternionGroup(GAP.Globals.IsPcGroup, n)::GapObj) end @doc raw""" @@ -651,7 +675,6 @@ false julia> is_quaternion_group(small_group(8, 4)) true - ``` """ @gapattribute is_quaternion_group(G::GAPGroup) = GAP.Globals.IsQuaternionGroup(GapObj(G))::Bool diff --git a/src/Groups/homomorphisms.jl b/src/Groups/homomorphisms.jl index 573a2929bb52..d340f10e50aa 100644 --- a/src/Groups/homomorphisms.jl +++ b/src/Groups/homomorphisms.jl @@ -10,29 +10,29 @@ end function ==(f::GAPGroupHomomorphism{S,T}, g::GAPGroupHomomorphism{S,T}) where S where T - return f.map == g.map + return GapObj(f) == GapObj(g) end function Base.hash(f::GAPGroupHomomorphism{S,T}, h::UInt) where S where T b = 0xdc777737af4c0c7b % UInt - return xor(hash(f.map, hash((S, T), h)), b) + return xor(hash(GapObj(f), hash((S, T), h)), b) end Base.:*(f::GAPGroupHomomorphism{S, T}, g::GAPGroupHomomorphism{T, U}) where S where T where U = compose(f, g) function Base.inv(f::GAPGroupHomomorphism{S,T}) where S where T - @assert GAPWrap.IsBijective(f.map) "f is not bijective" - return GAPGroupHomomorphism(codomain(f), domain(f), GAP.Globals.InverseGeneralMapping(f.map)) + @assert GAPWrap.IsBijective(GapObj(f)) "f is not bijective" + return GAPGroupHomomorphism(codomain(f), domain(f), GAP.Globals.InverseGeneralMapping(GapObj(f))) end -order(f::GAPGroupHomomorphism) = GAPWrap.Order(f.map) +order(f::GAPGroupHomomorphism) = GAPWrap.Order(GapObj(f)) function Base.:^(f::GAPGroupHomomorphism{S,T}, n::Int64) where S where T if n==1 return f else @assert domain(f) == codomain(f) "Domain and codomain do not coincide" - return GAPGroupHomomorphism(domain(f),codomain(f),(f.map)^n) + return GAPGroupHomomorphism(domain(f),codomain(f),(GapObj(f))^n) end end @@ -40,7 +40,7 @@ function compose(f::GAPGroupHomomorphism{S, T}, g::GAPGroupHomomorphism{T, U}) w dom = domain(f) cod = codomain(g) @assert codomain(f) == domain(g) - mp = GAP.Globals.CompositionMapping(g.map, f.map) + mp = GAP.Globals.CompositionMapping(GapObj(g), GapObj(f)) return GAPGroupHomomorphism(dom, cod, mp) end @@ -52,7 +52,7 @@ end Return the identity homomorphism on the group `G`. """ function id_hom(G::GAPGroup) - return GAPGroupHomomorphism(G, G, GAP.Globals.IdentityMapping(G.X)) + return GAPGroupHomomorphism(G, G, GAP.Globals.IdentityMapping(GapObj(G))) end """ @@ -78,9 +78,9 @@ function hom(G::GAPGroup, H::GAPGroup, img::Function) function gap_fun(x::GapObj) el = group_element(G, x) img_el = img(el) - return img_el.X + return GapObj(img_el) end - mp = GAPWrap.GroupHomomorphismByFunction(G.X, H.X, GAP.Obj(gap_fun)) + mp = GAPWrap.GroupHomomorphismByFunction(GapObj(G), GapObj(H), GAP.Obj(gap_fun)) return GAPGroupHomomorphism(G, H, mp) end @@ -90,19 +90,19 @@ function hom(G::GAPGroup, H::GAPGroup, img::Function, preimg::Function; is_known function gap_fun(x::GapObj) el = group_element(G, x) img_el = img(el) - return img_el.X + return GapObj(img_el) end function gap_pre_fun(x::GapObj) el = group_element(H, x) preimg_el = preimg(el) - return preimg_el.X + return GapObj(preimg_el) end if is_known_to_be_bijective - mp = GAPWrap.GroupHomomorphismByFunction(G.X, H.X, GAP.Obj(gap_fun), GAP.Obj(gap_pre_fun)) + mp = GAPWrap.GroupHomomorphismByFunction(GapObj(G), GapObj(H), GAP.Obj(gap_fun), GAP.Obj(gap_pre_fun)) else - mp = GAPWrap.GroupHomomorphismByFunction(G.X, H.X, GAP.Obj(gap_fun), false, GAP.Obj(gap_pre_fun)) + mp = GAPWrap.GroupHomomorphismByFunction(GapObj(G), GapObj(H), GAP.Obj(gap_fun), false, GAP.Obj(gap_pre_fun)) end return GAPGroupHomomorphism(G, H, mp) @@ -118,12 +118,12 @@ If `check` is set to `false` then it is not checked whether the mapping defines a group homomorphism. """ function hom(G::GAPGroup, H::GAPGroup, gensG::Vector, imgs::Vector; check::Bool = true) - vgens = GapObj([x.X for x in gensG]) - vimgs = GapObj([x.X for x in imgs]) + vgens = GapObj(gensG; recursive = true) + vimgs = GapObj(imgs; recursive = true) if check - mp = GAP.Globals.GroupHomomorphismByImages(G.X, H.X, vgens, vimgs) + mp = GAP.Globals.GroupHomomorphismByImages(GapObj(G), GapObj(H), vgens, vimgs) else - mp = GAP.Globals.GroupHomomorphismByImagesNC(G.X, H.X, vgens, vimgs) + mp = GAP.Globals.GroupHomomorphismByImagesNC(GapObj(G), GapObj(H), vgens, vimgs) end @req mp !== GAP.Globals.fail "Invalid input" return GAPGroupHomomorphism(G, H, mp) @@ -177,7 +177,7 @@ Base.:^(x::GAPGroupElem,f::GAPGroupHomomorphism) = image(f,x) Return `f`(`x`). """ function image(f::GAPGroupHomomorphism, x::GAPGroupElem) - return group_element(codomain(f), GAPWrap.ImagesRepresentative(f.map, x.X)) + return group_element(codomain(f), GAPWrap.ImagesRepresentative(GapObj(f), GapObj(x))) end """ @@ -199,7 +199,7 @@ end Return whether `f` is surjective. """ function is_surjective(f::GAPGroupHomomorphism) - return GAPWrap.IsSurjective(f.map) + return GAPWrap.IsSurjective(GapObj(f)) end """ @@ -208,7 +208,7 @@ end Return whether `f` is injective. """ function is_injective(f::GAPGroupHomomorphism) - return GAPWrap.IsInjective(f.map) + return GAPWrap.IsInjective(GapObj(f)) end """ @@ -217,7 +217,7 @@ end Return whether `f` is invertible. """ function is_invertible(f::GAPGroupHomomorphism) - return GAPWrap.IsBijective(f.map) + return GAPWrap.IsBijective(GapObj(f)) end """ @@ -226,7 +226,7 @@ end Return whether `f` is bijective. """ function is_bijective(f::GAPGroupHomomorphism) - return GAPWrap.IsBijective(f.map) + return GAPWrap.IsBijective(GapObj(f)) end @@ -240,8 +240,8 @@ or if `H` is not contained in `domain(f)`. """ function is_invariant(f::GAPGroupHomomorphism, H::GAPGroup) @assert domain(f) == codomain(f) "Not an endomorphism!" - @assert GAPWrap.IsSubset(domain(f).X, H.X) "Not a subgroup of the domain" - return GAPWrap.Image(f.map, H.X) == H.X + @assert GAPWrap.IsSubset(GapObj(domain(f)), GapObj(H)) "Not a subgroup of the domain" + return GAPWrap.Image(GapObj(f), GapObj(H)) == GapObj(H) end """ @@ -258,7 +258,7 @@ function restrict_homomorphism(f::GAPGroupHomomorphism, H::GAPGroup) # in the case that `H` is not a subgroup of `f.domain`, # and in fact just the given map may be returned.) @assert is_subset(H, domain(f)) "Not a subgroup!" - return GAPGroupHomomorphism(H, f.codomain, GAP.Globals.RestrictedMapping(f.map,H.X)::GapObj) + return GAPGroupHomomorphism(H, f.codomain, GAP.Globals.RestrictedMapping(GapObj(f), GapObj(H))::GapObj) end ################################################################################ @@ -273,7 +273,7 @@ end Return the kernel of `f`, together with its embedding into `domain`(`f`). """ function kernel(f::GAPGroupHomomorphism) - K = GAP.Globals.Kernel(f.map)::GapObj + K = GAP.Globals.Kernel(GapObj(f))::GapObj return _as_subgroup(domain(f), K) end @@ -283,7 +283,7 @@ end Return the image of `f` as subgroup of `codomain`(`f`), together with the embedding homomorphism. """ function image(f::GAPGroupHomomorphism) - K = GAPWrap.Image(f.map) + K = GAPWrap.Image(GapObj(f)) return _as_subgroup(codomain(f), K) end @@ -294,7 +294,7 @@ end Return `f`(`H`), together with the embedding homomorphism into `codomain`(`f`). """ function image(f::GAPGroupHomomorphism{S, T}, H::S) where S <: GAPGroup where T <: GAPGroup - H1 = GAPWrap.Image(f.map, H.X) + H1 = GAPWrap.Image(GapObj(f), GapObj(H)) return _as_subgroup(codomain(f), H1) end @@ -323,7 +323,7 @@ If `check` is set to `false` then the test whether `x` is an element of `image(f)` is omitted. """ function has_preimage_with_preimage(f::GAPGroupHomomorphism, x::GAPGroupElem; check::Bool = true) - return _haspreimage(f.map, domain(f), image(f)[1], x, check = check) + return _haspreimage(GapObj(f), domain(f), image(f)[1], x, check = check) end # helper function for computing `has_preimage_with_preimage` for @@ -340,7 +340,7 @@ function _haspreimage(mp::GapObj, dom::GAPGroup, img::GAPGroup, x::GAPGroupElem; # Until this problem gets fixed on the GAP side, we perform a membership test # before calling `GAP.Globals.PreImagesRepresentative`. check && ! (x in img) && return false, one(dom) - r = GAP.Globals.PreImagesRepresentative(mp, x.X)::GapObj + r = GAP.Globals.PreImagesRepresentative(mp, GapObj(x))::GapObj if r == GAP.Globals.fail return false, one(dom) else @@ -350,13 +350,13 @@ end """ - preimage(f::GAPGroupHomomorphism{S, T}, H::T) where S <: GAPGroup where T <: GAPGroup + preimage(f::GAPGroupHomomorphism{S, T}, H::GAPGroup) where S <: GAPGroup where T <: GAPGroup If `H` is a subgroup of the codomain of `f`, return the subgroup `f^-1(H)`, together with its embedding homomorphism into the domain of `f`. """ -function preimage(f::GAPGroupHomomorphism{S, T}, H::T) where S <: GAPGroup where T <: GAPGroup - H1 = GAP.Globals.PreImage(f.map, H.X)::GapObj +function preimage(f::GAPGroupHomomorphism{S, T}, H::GAPGroup) where S <: GAPGroup where T <: GAPGroup + H1 = GAP.Globals.PreImage(GapObj(f), GapObj(H))::GapObj return _as_subgroup(domain(f), H1) end @@ -381,7 +381,7 @@ julia> is_isomorphic_with_map(symmetric_group(3), dihedral_group(6)) ``` """ function is_isomorphic_with_map(G::GAPGroup, H::GAPGroup) - mp = GAP.Globals.IsomorphismGroups(G.X, H.X)::GapObj + mp = GAP.Globals.IsomorphismGroups(GapObj(G), GapObj(H))::GapObj if mp === GAP.Globals.fail return false, trivial_morphism(G, H) else @@ -423,7 +423,7 @@ true ``` """ function is_isomorphic(G::GAPGroup, H::GAPGroup) - mp = GAP.Globals.IsomorphismGroups(G.X, H.X)::GapObj + mp = GAP.Globals.IsomorphismGroups(GapObj(G), GapObj(H))::GapObj return mp !== GAP.Globals.fail end @@ -452,7 +452,7 @@ Group homomorphism ``` """ function isomorphism(G::GAPGroup, H::GAPGroup) - mp = GAP.Globals.IsomorphismGroups(G.X, H.X)::GapObj + mp = GAP.Globals.IsomorphismGroups(GapObj(G), GapObj(H))::GapObj @req mp !== GAP.Globals.fail "the groups are not isomorphic" return GAPGroupHomomorphism(G, H, mp) end @@ -480,18 +480,26 @@ end ################################################################################ _get_iso_function(::Type{PermGroup}) = GAP.Globals.IsomorphismPermGroup + +# We use `GAP.Globals.IsomorphismPcGroup` as the `_get_iso_function` value +# for both `PcGroup` and `SubPcGroup`. +# Note that `_get_iso_function` is used to create a GAP mapping, +# and afterwards an Oscar mapping is created whose codomain is obtained +# from the codomain `G` in GAP by calling `T(G)` where `T` is the desired type. _get_iso_function(::Type{PcGroup}) = GAP.Globals.IsomorphismPcGroup +_get_iso_function(::Type{SubPcGroup}) = GAP.Globals.IsomorphismPcGroup """ - isomorphism(::Type{T}, G::GAPGroup) where T <: Union{PcGroup, PermGroup} - isomorphism(::Type{T}, G::GAPGroup; on_gens=false) where T = FPGroup + isomorphism(::Type{T}, G::GAPGroup) where T <: Union{SubPcGroup, PermGroup} + isomorphism(::Type{T}, G::GAPGroup; on_gens=false) where T <: Union{PcGroup, FPGroup} Return an isomorphism from `G` to a group `H` of type `T`. An exception is thrown if no such isomorphism exists. If `on_gens` is `true` then `gens(G)` is guaranteed to correspond to -`gens(H)`. +`gens(H)`; +an exception is thrown if this is not possible. Isomorphisms are cached in `G`, subsequent calls of `isomorphism` with the same `T` (and the same value of `on_gens`) yield identical results. @@ -515,16 +523,16 @@ julia> codomain(iso) === ans true ``` """ -function isomorphism(::Type{T}, G::GAPGroup) where T <: Union{PcGroup, PermGroup} +function isomorphism(::Type{T}, G::GAPGroup) where T <: Union{SubPcGroup, PermGroup} # Known isomorphisms are cached in the attribute `:isomorphisms`. isos = get_attribute!(Dict{Tuple{Type, Bool}, Any}, G, :isomorphisms)::Dict{Tuple{Type, Bool}, Any} return get!(isos, (T, false)) do fun = _get_iso_function(T) - f = fun(G.X)::GapObj + f = fun(GapObj(G))::GapObj @req f !== GAP.Globals.fail "Could not convert group into a group of type $T" - H = T(GAP.Globals.ImagesSource(f)::GapObj) + H = T(GAPWrap.ImagesSource(f)) # TODO: remove the next line once GAP 4.13.0 is available in Oscar - GAP.Globals.UseIsomorphismRelation(G.X, H.X) + GAP.Globals.UseIsomorphismRelation(GapObj(G), GapObj(H)) return GAPGroupHomomorphism(G, H, f) end::GAPGroupHomomorphism{typeof(G), T} end @@ -534,18 +542,18 @@ function isomorphism(::Type{FPGroup}, G::GAPGroup; on_gens::Bool=false) isos = get_attribute!(Dict{Tuple{Type, Bool}, Any}, G, :isomorphisms)::Dict{Tuple{Type, Bool}, Any} return get!(isos, (FPGroup, on_gens)) do if on_gens - Ggens = GAPWrap.GeneratorsOfGroup(G.X) + Ggens = GAPWrap.GeneratorsOfGroup(GapObj(G)) if length(Ggens) == 0 # TODO: remove this special treatment as soon as the change from # https://github.com/gap-system/gap/pull/5700 is available in Oscar # (not yet in GAP 4.13.0) - f = GAP.Globals.GroupHomomorphismByImages(G.X, GAP.Globals.FreeGroup(0), GAP.Obj([]), GAP.Obj([])) + f = GAP.Globals.GroupHomomorphismByImages(GapObj(G), GAP.Globals.FreeGroup(0), GAP.Obj([]), GAP.Obj([])) GAP.Globals.SetIsBijective(f, true) else # The computations are easy if `Ggens` is a pcgs, # otherwise GAP will call `CoKernel`. - if GAP.Globals.HasFamilyPcgs(G.X) - pcgs = GAP.Globals.InducedPcgsWrtFamilyPcgs(G.X) + if GAP.Globals.HasFamilyPcgs(GapObj(G)) + pcgs = GAP.Globals.InducedPcgsWrtFamilyPcgs(GapObj(G)) if pcgs == Ggens # `pcgs` fits *and* is an object in `GAP.Globals.IsPcgs`, # for which a special `GAPWrap.IsomorphismFpGroupByGenerators` @@ -555,19 +563,89 @@ function isomorphism(::Type{FPGroup}, G::GAPGroup; on_gens::Bool=false) Ggens = pcgs end end - f = GAPWrap.IsomorphismFpGroupByGenerators(G.X, Ggens) + f = GAPWrap.IsomorphismFpGroupByGenerators(GapObj(G), Ggens) end else - f = GAPWrap.IsomorphismFpGroup(G.X) + f = GAPWrap.IsomorphismFpGroup(GapObj(G)) end @req f !== GAP.Globals.fail "Could not convert group into a group of type FPGroup" - H = FPGroup(GAP.Globals.ImagesSource(f)::GapObj) + H = FPGroup(GAPWrap.ImagesSource(f)) # TODO: remove the next line once GAP 4.13.0 is available in Oscar - GAP.Globals.UseIsomorphismRelation(G.X, H.X) + GAP.Globals.UseIsomorphismRelation(GapObj(G), GapObj(H)) return GAPGroupHomomorphism(G, H, f) end::GAPGroupHomomorphism{typeof(G), FPGroup} end +# very simpleminded, should be supported by GAP ... +function _is_pc_sequence(list::Vector{T}) where T <: GAPGroupElem + l = length(list) + l == 0 && return true + F = parent(list[1]) + G = trivial_subgroup(F)[1] + for i in l:-1:1 + H = G + G = sub(F, list[i:l])[1] + (is_normal_subgroup(H, G) && is_prime(index(G, H))) || return false + end + + return true +end + +# If `G` is not a full pc group then switch to a full pc group in Oscar. +# If `G` is infinite then there is `GAP.Globals.IsomorphismPcGroup`, +# but it has currently only methods for finite groups or for groups +# that come from the packages Cryst, AClib, or Polenta. +function isomorphism(T::Type{PcGroup}, G::GAPGroup; on_gens::Bool=false) + # Known isomorphisms are cached in the attribute `:isomorphisms`. + isos = get_attribute!(Dict{Tuple{Type, Bool}, Any}, G, :isomorphisms)::Dict{Tuple{Type, Bool}, Any} + return get!(isos, (T, on_gens)) do + if on_gens + # Throw an exception if `gens(G)` is not a polycyclic sequence for `G`. + @req _is_pc_sequence(gens(G)) "the generators do not form a pc sequence" + # Create a group in `IsPcGroup` if `G` is finite, + # and a group in `IsPcpGroup` otherwise. + if is_finite(G) + fam = GAP.Globals.ElementsFamily(GAP.Globals.FamilyObj(GapObj(G)))::GapObj + Ggens = GapObj(gens(G); recursive = true)::GapObj + Cpcgs = GAP.Globals.PcgsByPcSequence(fam, Ggens)::GapObj + CC = GAP.Globals.PcGroupWithPcgs(Cpcgs)::GapObj + CCpcgs = GAP.Globals.FamilyPcgs(CC)::GapObj + f = GAP.Globals.GroupHomomorphismByImages(GapObj(G), CC, Cpcgs, CCpcgs)::GapObj + return GAPGroupHomomorphism(G, T(CC), f) + else + f = GAP.Globals.IsomorphismPcpGroup(GapObj(G))::GapObj +#T how to create a pcp that consists of the images of `gens(G)`, +#T or get `fail`? +error("do not know how to create a pcp group on given generators in GAP") + end + else + fun = _get_iso_function(T) +#T change this: use IsomorphismPcpGroup if G is infinite! + f = fun(GapObj(G))::GapObj + @req f !== GAP.Globals.fail "Could not convert group into a group of type $T" + # The codomain of `f` can be a *subgroup* of a full pc group. + # In this situation, we have to switch to a full pc group. + C = GAP.Globals.Range(f)::GapObj + if !_is_full_pc_group(C) + @assert GAPWrap.IsPcGroup(C) || GAP.Globals.IsPcpGroup(C)::Bool + if GAPWrap.IsPcGroup(C)::Bool + Cpcgs = GAP.Globals.Pcgs(C)::GapObj + CC = GAP.Globals.PcGroupWithPcgs(Cpcgs)::GapObj + CCpcgs = GAP.Globals.FamilyPcgs(CC)::GapObj + else + Cpcgs = GAP.Globals.Pcp(C)::GapObj + CC = GAP.Globals.PcpGroupByPcp(Cpcgs)::GapObj + CCpcgs = GAP.Globals.Pcp(CC)::GapObj + end + switch = GAP.Globals.GroupHomomorphismByImages(C, CC, Cpcgs, CCpcgs)::GapObj + f = GAP.Globals.CompositionMapping(switch, f)::GapObj + end + end + H = T(GAPWrap.ImagesSource(f)) + return GAPGroupHomomorphism(G, H, f) + end::GAPGroupHomomorphism{typeof(G), T} +end + """ isomorphism(::Type{FinGenAbGroup}, G::GAPGroup) @@ -583,15 +661,15 @@ function isomorphism(::Type{FinGenAbGroup}, G::GAPGroup) @req is_finite(G) "the group is not finite" #T this restriction is not nice - indep = GAP.Globals.IndependentGeneratorsOfAbelianGroup(G.X)::GapObj + indep = GAP.Globals.IndependentGeneratorsOfAbelianGroup(GapObj(G))::GapObj orders = ZZRingElem[GAPWrap.Order(x) for x in indep] n = length(indep) A = abelian_group(FinGenAbGroup, orders) - f(g) = A(Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(G.X, g.X))) + f(g) = A(Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(GapObj(G), GapObj(g)))) finv = function(g::elem_type(FinGenAbGroup)) - res = GAPWrap.One(G.X) + res = GAPWrap.One(GapObj(G)) for i in 1:n res = res * indep[i]^GAP.Obj(g.coeff[i]) end @@ -622,23 +700,37 @@ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup A2, A2_to_A = snf(A) end A_to_A2 = inv(A2_to_A) - # the isomorphic gap group - G = abelian_group(T, exponents) - # `GAPWrap.GeneratorsOfGroup(G.X)` consists of independent elements + # 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 + if 0 in exponents + GapG = GAP.Globals.AbelianPcpGroup(length(exponents), GapObj(exponents, recursive=true)) + else + GapG = GAP.Globals.AbelianGroup(GAP.Globals.IsPcGroup, GapObj(exponents, recursive=true)) + end + G = PcGroup(GAP.Globals.SubgroupNC(GapG, GAP.Globals.FamilyPcgs(GapG))) + else + G = abelian_group(T, exponents) + GapG = GapObj(G) + end + + # `GAPWrap.GeneratorsOfGroup(GapG)` consists of independent elements # of the orders in `exponents`. - # `GAP.Globals.IndependentGenerators(G.X)` chooses generators + # `GAP.Globals.IndependentGeneratorsOfAbelianGroup(GapG)` chooses generators # that may differ from these generators, # and that belong to the exponent vectors returned by - # `GAPWrap.IndependentGeneratorExponents(G.X, g)`. - # `GAPWrap.GeneratorsOfGroup(G.X)` corresponds to `gens(A2)`, + # `GAPWrap.IndependentGeneratorExponents(GapG, g)`. + # `GAPWrap.GeneratorsOfGroup(GapG)` corresponds to `gens(A2)`, # we let `hom` compute elements in `A2` that correspond to - # `GAP.Globals.IndependentGenerators(G.X)`. - Ggens = Vector{GapObj}(GAPWrap.GeneratorsOfGroup(G.X)::GapObj) + # `GAP.Globals.IndependentGenerators(GapG)`. + Ggens = Vector{GapObj}(GAPWrap.GeneratorsOfGroup(GapG)::GapObj) if length(Ggens) < length(exponents) # It may happen that GAP omits the generators of order 1. Insert them. @assert length(Ggens) + length(filter(x -> x == 1, exponents)) == length(exponents) - o = one(G).X + o = GapObj(one(G)) newGgens = Vector{GapObj}() pos = 1 for i in 1:length(exponents) @@ -651,17 +743,16 @@ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup end Ggens = newGgens end - gensindep = GAP.Globals.IndependentGeneratorsOfAbelianGroup(G.X)::GapObj + gensindep = GAP.Globals.IndependentGeneratorsOfAbelianGroup(GapG)::GapObj Aindep = abelian_group(ZZRingElem[GAPWrap.Order(g) for g in gensindep]) - - imgs = [Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(G.X, a)) for a in Ggens] + 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) n = length(exponents) f = function(a::elem_type(FinGenAbGroup)) exp = A_to_A2(a) - img = GAP.Globals.One(G.X) + img = GAP.Globals.One(GapG) for i in 1:n img = img * Ggens[i]^GAP.Obj(exp[i]) end @@ -669,7 +760,7 @@ function isomorphism(::Type{T}, A::FinGenAbGroup) where T <: GAPGroup end finv = function(g) - exp = Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(G.X, g.X)) + exp = Vector{ZZRingElem}(GAPWrap.IndependentGeneratorExponents(GapG, GapObj(g))) return Aindep_to_A(Aindep(exp)) end @@ -793,12 +884,14 @@ end FinGenAbGroup(G::T) where T <: GAPGroup PcGroup(G::T) where T <: Union{GAPGroup, FinGenAbGroup} pc_group(G::T) where T <: Union{GAPGroup, FinGenAbGroup} + SubPcGroup(G::T) where T <: Union{GAPGroup, FinGenAbGroup} + sub_pc_group(G::T) where T <: Union{GAPGroup, FinGenAbGroup} PermGroup(G::T) where T <: Union{GAPGroup, FinGenAbGroup} permutation_group(G::T) where T <: Union{GAPGroup, FinGenAbGroup} Return a group of the requested type that is isomorphic to `G`. If one needs the isomorphism then -[isomorphism(::Type{T}, G::GAPGroup) where T <: Union{FPGroup, PcGroup, PermGroup}](@ref) +[isomorphism(::Type{T}, G::GAPGroup) where T <: Union{SubPcGroup, PermGroup}](@ref) can be used instead. """ function (::Type{S})(G::T) where {S <: Union{FinGenAbGroup, GAPGroup}, T <: GAPGroup} @@ -811,13 +904,11 @@ end fp_group(G::T) where {T <: Union{FinGenAbGroup, GAPGroup, MultTableGroup}} = FPGroup(G) pc_group(G::T) where {T <: Union{FinGenAbGroup, GAPGroup, MultTableGroup}} = PcGroup(G) +sub_pc_group(G::T) where {T <: Union{FinGenAbGroup, GAPGroup, MultTableGroup}} = SubPcGroup(G) permutation_group(G::T) where {T <: Union{FinGenAbGroup, GAPGroup, MultTableGroup}} = PermGroup(G) # Now for MultTableGroup -# Remove once Hecke lower bound is >= 0.14.1 -@attributes MultTableGroup - function isomorphism(::Type{T}, A::MultTableGroup) where T <: GAPGroup # Known isomorphisms are cached in the attribute `:isomorphisms`. isos = get_attribute!(Dict{Tuple{Type, Bool}, Any}, A, :isomorphisms)::Dict{Tuple{Type, Bool}, Any} @@ -893,11 +984,12 @@ Finitely presented group of infinite order ``` """ function simplified_fp_group(G::FPGroup) - f = GAP.Globals.IsomorphismSimplifiedFpGroup(G.X) + f = GAP.Globals.IsomorphismSimplifiedFpGroup(GapObj(G)) H = FPGroup(GAPWrap.Image(f)) # TODO: remove the next line once https://github.com/gap-system/gap/pull/4810 # is deployed to Oscar - GAP.Globals.UseIsomorphismRelation(G.X, H.X) +#T do this as soon as GAP.jl guarantees GAP 4.13! + GAP.Globals.UseIsomorphismRelation(GapObj(G), GapObj(H)) return H, GAPGroupHomomorphism(G,H,f) end @@ -1050,12 +1142,12 @@ GAPGroupHomomorphism{PermGroup, PermGroup} ``` """ function automorphism_group(G::GAPGroup) - AutGAP = GAP.Globals.AutomorphismGroup(G.X)::GapObj + AutGAP = GAP.Globals.AutomorphismGroup(GapObj(G))::GapObj return AutomorphismGroup(AutGAP, G) end function Base.show(io::IO, A::AutomorphismGroup{T}) where T <: GAPGroup - print(io, "Aut( "* String(GAP.Globals.StringView(A.G.X)) * " )") + print(io, "Aut( "* String(GAP.Globals.StringView(GapObj(A.G))) * " )") end """ @@ -1090,7 +1182,7 @@ Return the element f of type `GAPGroupHomomorphism{T,T}`. function hom(x::GAPGroupElem{AutomorphismGroup{T}}) where T <: GAPGroup A = parent(x) G = A.G - return GAPGroupHomomorphism(G, G, x.X) + return GAPGroupHomomorphism(G, G, GapObj(x)) end (f::GAPGroupElem{AutomorphismGroup{T}})(x::GAPGroupElem) where T <: GAPGroup = apply_automorphism(f, x, true) @@ -1100,16 +1192,16 @@ Base.:^(x::GAPGroupElem{T},f::GAPGroupElem{AutomorphismGroup{T}}) where T <: GAP function (A::AutomorphismGroup{T})(f::GAPGroupHomomorphism{T,T}) where T <: GAPGroup @assert domain(f)==A.G && codomain(f)==A.G "f not in A" @assert is_bijective(f) "f not in A" - return group_element(A, f.map) + return group_element(A, GapObj(f)) end function apply_automorphism(f::GAPGroupElem{AutomorphismGroup{T}}, x::GAPGroupElem, check::Bool=true) where T <: GAPGroup A = parent(f) G = parent(x) if check - @assert A.G == G || x.X in A.G.X "Not in the domain of f!" #TODO Do we really need the IN check? + @assert A.G == G || GapObj(x) in GapObj(A.G) "Not in the domain of f!" #TODO Do we really need the IN check? end - return typeof(x)(G, GAPWrap.ImagesRepresentative(f.X, x.X)) + return typeof(x)(G, GAPWrap.ImagesRepresentative(GapObj(f), GapObj(x))) end Base.:*(f::GAPGroupElem{AutomorphismGroup{T}}, g::GAPGroupHomomorphism) where T = hom(f)*g @@ -1121,7 +1213,7 @@ Base.:*(f::GAPGroupHomomorphism, g::GAPGroupElem{AutomorphismGroup{T}}) where T Return the inner automorphism in `automorphism_group(parent(g))` defined by `x` -> `x^g`. """ function inner_automorphism(g::GAPGroupElem) - return GAPGroupHomomorphism(parent(g), parent(g), GAP.Globals.ConjugatorAutomorphism(parent(g).X, g.X)) + return GAPGroupHomomorphism(parent(g), parent(g), GAP.Globals.ConjugatorAutomorphism(GapObj(parent(g)), GapObj(g))) end """ @@ -1132,11 +1224,11 @@ Return whether `f` is an inner automorphism. """ function is_inner_automorphism(f::GAPGroupHomomorphism) @assert domain(f) == codomain(f) "Not an automorphism!" - return GAPWrap.IsInnerAutomorphism(f.map) + return GAPWrap.IsInnerAutomorphism(GapObj(f)) end function is_inner_automorphism(f::GAPGroupElem{AutomorphismGroup{T}}) where T <: GAPGroup - return GAPWrap.IsInnerAutomorphism(f.X) + return GAPWrap.IsInnerAutomorphism(GapObj(f)) end """ @@ -1145,7 +1237,7 @@ end Return the subgroup of `A` of the inner automorphisms. """ function inner_automorphism_group(A::AutomorphismGroup{T}) where T <: GAPGroup - AutGAP = GAP.Globals.InnerAutomorphismsAutomorphismGroup(A.X) + AutGAP = GAP.Globals.InnerAutomorphismsAutomorphismGroup(GapObj(A)) return _as_subgroup(A, AutGAP) end @@ -1155,8 +1247,8 @@ end Return whether `f`(`H`) == `H`. """ function is_invariant(f::GAPGroupElem{AutomorphismGroup{T}}, H::T) where T<:GAPGroup - @assert GAPWrap.IsSubset(parent(f).G.X, H.X) "Not a subgroup of the domain" - return GAPWrap.Image(f.X, H.X) == H.X + @assert GAPWrap.IsSubset(GapObj(parent(f).G), GapObj(H)) "Not a subgroup of the domain" + return GAPWrap.Image(GapObj(f), GapObj(H)) == GapObj(H) end """ @@ -1170,7 +1262,7 @@ homomorphism defined over `G` such that the kernel of `f` is invariant under """ function induced_automorphism(f::GAPGroupHomomorphism, mH::GAPGroupHomomorphism) @assert is_invariant(mH, kernel(f)[1]) "The kernel is not invariant under g!" - map = GAP.Globals.InducedAutomorphism(f.map, mH.map) + map = GAP.Globals.InducedAutomorphism(GapObj(f), GapObj(mH)) A = automorphism_group(image(f)[1]) return A(GAPGroupHomomorphism(codomain(f), codomain(f), map)) end diff --git a/src/Groups/libraries/atlasgroups.jl b/src/Groups/libraries/atlasgroups.jl index d1b1c135be73..891e4a1c3dcf 100644 --- a/src/Groups/libraries/atlasgroups.jl +++ b/src/Groups/libraries/atlasgroups.jl @@ -29,8 +29,7 @@ ERROR: ArgumentError: the group atlas does not provide a representation for M function atlas_group(name::String) G = GAP.Globals.AtlasGroup(GapObj(name)) @req (G !== GAP.Globals.fail) "the group atlas does not provide a representation for $name" - T = _get_type(G) - return T(G) + return _oscar_group(G) end function atlas_group(::Type{T}, name::String) where T <: Union{PermGroup, MatrixGroup} @@ -40,8 +39,7 @@ function atlas_group(::Type{T}, name::String) where T <: Union{PermGroup, Matrix G = GAP.Globals.AtlasGroup(GapObj(name), GAP.Globals.IsMatrixGroup, true)::GapObj end @req (G !== GAP.Globals.fail) "the group atlas does not provide a representation of type $T for $name" - TT = _get_type(G) - return TT(G) + return _oscar_group(G) end @@ -66,7 +64,7 @@ Permutation group of degree 5 and order 60 function atlas_group(info::Dict) gapname = info[:name] l = GAP.Globals.AGR.MergedTableOfContents(GapObj("all"), GapObj(gapname))::GapObj - pos = findfirst(r -> String(r.repname) == info[:repname], Vector{GAP.GapObj}(l)) + pos = findfirst(r -> String(r.repname) == info[:repname], Vector{GapObj}(l)) @req (pos !== nothing) "no Atlas group for $info" G = GAP.Globals.AtlasGroup(l[pos]) @req (G !== GAP.Globals.fail) "the group atlas does not provide a representation for $info" @@ -81,8 +79,7 @@ function atlas_group(info::Dict) matgrp.X = G return matgrp else - TT = _get_type(G) - return TT(G) + return _oscar_group(G) end end @@ -218,7 +215,7 @@ function all_atlas_group_infos(name::String, L...) iso = iso_oscar_gap(data) push!(gapargs, gapfunc, codomain(iso)) elseif func === character - push!(gapargs, gapfunc, data.values) + push!(gapargs, gapfunc, GapObj(data)) else # we can translate `data` to GAP push!(gapargs, gapfunc, GAP.Obj(data)) diff --git a/src/Groups/libraries/smallgroups.jl b/src/Groups/libraries/smallgroups.jl index 684d726bbb9f..182cb9e325b1 100644 --- a/src/Groups/libraries/smallgroups.jl +++ b/src/Groups/libraries/smallgroups.jl @@ -89,8 +89,7 @@ end function small_group(n::IntegerUnion, m::IntegerUnion) G = _small_group(n, m) - T = _get_type(G) - return T(G) + return _oscar_group(G) end function _small_group(n::IntegerUnion, m::IntegerUnion) @@ -210,7 +209,7 @@ function all_small_groups(L...) # TODO: perhaps add an option so that ids are returned instead of groups, # by calling GAP.Globals.IdsOfAllSmallGroups - return [_get_type(x)(x) for x in K] + return [_oscar_group(x) for x in K] end #T problem: diff --git a/src/Groups/matrices/MatGrp.jl b/src/Groups/matrices/MatGrp.jl index 63dd8301e45c..94e201da54da 100644 --- a/src/Groups/matrices/MatGrp.jl +++ b/src/Groups/matrices/MatGrp.jl @@ -152,7 +152,7 @@ group_element(G::MatrixGroup, x::GapObj) = MatrixGroupElem(G,x) # Compute and store the component `G.X` if this is possible. function assign_from_description(G::MatrixGroup) F = codomain(_ring_iso(G)) - GAP.Globals.IsBaseRingSupportedForClassicalMatrixGroup(F, GAP.GapObj(G.descr)) || error("no generators are known for the matrix group of type $(G.descr) over $(base_ring(G))") + GAP.Globals.IsBaseRingSupportedForClassicalMatrixGroup(F, GapObj(G.descr)) || error("no generators are known for the matrix group of type $(G.descr) over $(base_ring(G))") if G.descr==:GL G.X=GAP.Globals.GL(G.deg, F) elseif G.descr==:SL G.X=GAP.Globals.SL(G.deg, F) elseif G.descr==:Sp G.X=GAP.Globals.Sp(G.deg, F) @@ -249,7 +249,7 @@ function Base.getproperty(G::MatrixGroup{T}, sym::Symbol) where T if isdefined(G,:descr) assign_from_description(G) elseif isdefined(G,:gens) - V = GAP.GapObj([g.X for g in gens(G)]) + V = GapObj([g.X for g in gens(G)]) G.X = isempty(V) ? GAP.Globals.Group(V, one(G).X) : GAP.Globals.Group(V) else error("Cannot determine underlying GAP object") @@ -435,6 +435,7 @@ function Base.:^(x::MatrixGroupElem, y::MatrixGroupElem) end comm(x::MatrixGroupElem, y::MatrixGroupElem) = inv(x)*conj(x,y) +#T why needed? GAPGroupElem has x^-1*x^y """ det(x::MatrixGroupElem) diff --git a/src/Groups/matrices/iso_nf_fq.jl b/src/Groups/matrices/iso_nf_fq.jl index 5ff103b39024..b7c5f2e9a08c 100644 --- a/src/Groups/matrices/iso_nf_fq.jl +++ b/src/Groups/matrices/iso_nf_fq.jl @@ -91,7 +91,7 @@ function _isomorphic_group_over_finite_field(G::MatrixGroup{T}; min_char::Int = preimg = function(y) return GAP.Globals.MappedWord(GAPWrap.UnderlyingElement(GAPWrap.Image(GptoF, map_entries(_ring_iso(Gp), matrix(y)))), GAPWrap.FreeGeneratorsOfFpGroup(F), - GAP.GapObj(gen)) + GapObj(gen)) end return Gp, MapFromFunc(G, Gp, img, preimg) diff --git a/src/Groups/matrices/matrix_manipulation.jl b/src/Groups/matrices/matrix_manipulation.jl index 1fb703c4794c..8118e2c1237f 100644 --- a/src/Groups/matrices/matrix_manipulation.jl +++ b/src/Groups/matrices/matrix_manipulation.jl @@ -33,52 +33,6 @@ function matrix(A::Vector{AbstractAlgebra.Generic.FreeModuleElem{T}}) where T <: return X end -@doc raw""" - upper_triangular_matrix(L) - -Return the upper triangular matrix whose entries on and above the diagonal are the elements of `L`. - -An exception is thrown whenever the length of `L` is not equal to $n(n+1)/2$, -for some integer $n$. -""" -function upper_triangular_matrix(L) - T = eltype(L) - @assert T <: RingElem "L must be a collection of ring elements" - d = Int(floor((sqrt(1+8*length(L))-1)/2)) - @req length(L)==div(d*(d+1),2) "Input vector of invalid length" - R = parent(L[1]) - x = zero_matrix(R,d,d) - pos=1 - for i in 1:d, j in i:d - x[i,j] = L[pos] - pos+=1 - end - return x -end - -@doc raw""" - lower_triangular_matrix(L) - -Return the upper triangular matrix whose entries on and below the diagonal are the elements of `L`. - -An exception is thrown whenever the length of `L` is not equal to $n(n+1)/2$, -for some integer $n$. -""" -function lower_triangular_matrix(L) - T = eltype(L) - @assert T <: RingElem "L must be a collection of ring elements" - d = Int(floor((sqrt(1+8*length(L))-1)/2)) - @req length(L)==div(d*(d+1),2) "Input vector of invalid length" - R = parent(L[1]) - x = zero_matrix(R,d,d) - pos=1 - for i in 1:d, j in 1:i - x[i,j] = L[pos] - pos+=1 - end - return x -end - """ conjugate_transpose(x::MatElem{T}) where T <: FinFieldElem @@ -161,26 +115,6 @@ permutation_matrix(F::Ring, p::PermGroupElem) = permutation_matrix(F, Vector(p)) ######################################################################## # TODO: Move to AbstractAlgebra -""" - is_alternating(B::MatElem) - -Return whether the form corresponding to the matrix `B` is alternating, -i.e. `B = -transpose(B)` and `B` has zeros on the diagonal. -Return `false` if `B` is not a square matrix. -""" -function is_alternating(B::MatElem) - n = nrows(B) - n==ncols(B) || return false - - for i in 1:n - B[i,i]==0 || return false - for j in i+1:n - B[i,j]==-B[j,i] || return false - end - end - - return true -end """ is_hermitian(B::MatElem{T}) where T <: FinFieldElem diff --git a/src/Groups/perm.jl b/src/Groups/perm.jl index dffaf07a180f..25a0af4609e4 100644 --- a/src/Groups/perm.jl +++ b/src/Groups/perm.jl @@ -123,7 +123,7 @@ Sym(6) ``` """ function perm(L::AbstractVector{<:IntegerUnion}) - return PermGroupElem(symmetric_group(length(L)), GAPWrap.PermList(GAP.GapObj(L;recursive=true))) + return PermGroupElem(symmetric_group(length(L)), GAPWrap.PermList(GapObj(L;recursive=true))) end @@ -161,7 +161,7 @@ true ``` """ function perm(g::PermGroup, L::AbstractVector{<:IntegerUnion}) - x = GAPWrap.PermList(GAP.GapObj(L;recursive=true)) + x = GAPWrap.PermList(GapObj(L;recursive=true)) @req x !== GAP.Globals.fail "the list does not describe a permutation" @req (length(L) <= degree(g) && x in GapObj(g)) "the element does not embed in the group" return PermGroupElem(g, x) @@ -170,7 +170,7 @@ end perm(g::PermGroup, L::AbstractVector{<:ZZRingElem}) = perm(g, [Int(y) for y in L]) function (g::PermGroup)(L::AbstractVector{<:IntegerUnion}) - x = GAPWrap.PermList(GAP.GapObj(L;recursive=true)) + x = GAPWrap.PermList(GapObj(L;recursive=true)) @req (length(L) <= degree(g) && x in GapObj(g)) "the element does not embed in the group" return PermGroupElem(g, x) end diff --git a/src/Groups/sub.jl b/src/Groups/sub.jl index 78b7f8fd708d..56c11ec1a178 100644 --- a/src/Groups/sub.jl +++ b/src/Groups/sub.jl @@ -5,7 +5,7 @@ ################################################################################ function _as_subgroup_bare(G::T, H::GapObj) where T <: GAPGroup - return _oscar_group(H, G) + return _oscar_subgroup(H, G) end function _as_subgroup(G::GAPGroup, H::GapObj) @@ -51,7 +51,7 @@ function sub(gens::GAPGroupElem...) end """ - is_subset(H::T, G::T) where T <: GAPGroup + is_subset(H::GAPGroup, G::GAPGroup) Return `true` if `H` is a subset of `G`, otherwise return `false`. @@ -66,20 +66,21 @@ julia> is_subset(g, h) false ``` """ -function is_subset(H::T, G::T) where T <: GAPGroup +function is_subset(H::GAPGroup, G::GAPGroup) + _check_compatible(H, G, error = false) || return false return all(h -> h in G, gens(H)) end """ - is_subgroup(H::T, G::T) where T <: GAPGroup + is_subgroup(H::GAPGroup, G::GAPGroup) Return (`true`,`f`) if `H` is a subgroup of `G`, where `f` is the embedding homomorphism of `H` into `G`, otherwise return (`false`,`nothing`). If you do not need the embedding then better call -[`is_subset(H::T, G::T) where T <: GAPGroup`](@ref). +[`is_subset(H::GAPGroup, G::GAPGroup)`](@ref). """ -function is_subgroup(H::T, G::T) where T <: GAPGroup +function is_subgroup(H::GAPGroup, G::GAPGroup) if !is_subset(H, G) return (false, nothing) else @@ -92,12 +93,12 @@ function is_subgroup(H::T, G::T) where T <: GAPGroup end """ - embedding(H::T, G::T) where T <: GAPGroup + embedding(H::GAPGroup, G::GAPGroup) Return the embedding morphism of `H` into `G`. An exception is thrown if `H` is not a subgroup of `G`. """ -function embedding(H::T, G::T) where T <: GAPGroup +function embedding(H::GAPGroup, G::GAPGroup) a, f = is_subgroup(H, G) @req a "H is not a subgroup of G" return f @@ -125,7 +126,8 @@ julia> trivial_subgroup(symmetric_group(5)) ############################################################################### """ - index(::Type{I} = ZZRingElem, G::T, H::T) where I <: IntegerUnion where T <: Union{GAPGroup, FinGenAbGroup} + index(::Type{I} = ZZRingElem, G::GAPGroup, H::GAPGroup) where I <: IntegerUnion + index(::Type{I} = ZZRingElem, G::FinGenAbGroup, H::FinGenAbGroup) where I <: IntegerUnion Return the index of `H` in `G`, as an instance of type `I`. @@ -137,9 +139,12 @@ julia> index(G,H) 2 ``` """ -index(G::T, H::T) where T <: Union{GAPGroup, FinGenAbGroup} = index(ZZRingElem, G, H) +index(G::GAPGroup, H::GAPGroup) = index(ZZRingElem, G, H) -function index(::Type{I}, G::T, H::T) where I <: IntegerUnion where T <: GAPGroup +index(G::FinGenAbGroup, H::FinGenAbGroup) = index(ZZRingElem, G, H) + +function index(::Type{I}, G::GAPGroup, H::GAPGroup) where I <: IntegerUnion + _check_compatible(G, H) i = GAP.Globals.Index(GapObj(G), GapObj(H))::GapInt @req (i !== GAP.Globals.infinity) "index() not supported for subgroup of infinite index, use is_finite()" return I(i) @@ -160,6 +165,14 @@ function _as_subgroups(G::T, subs::GapObj) where T <: GAPGroup return res end +function _as_subgroups(G::PcGroup, subs::GapObj) + res = Vector{SubPcGroup}(undef, length(subs)) + for i = 1:length(res) + res[i] = _as_subgroup_bare(G, subs[i]::GapObj) + end + return res +end + """ normal_subgroups(G::Group) @@ -175,13 +188,13 @@ julia> normal_subgroups(symmetric_group(5)) Permutation group of degree 5 and order 1 julia> normal_subgroups(quaternion_group(8)) -6-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 4 - Pc group of order 4 - Pc group of order 4 - Pc group of order 2 - Pc group of order 1 +6-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 4 + Sub-pc group of order 4 + Sub-pc group of order 4 + Sub-pc group of order 2 + Sub-pc group of order 1 ``` """ @gapattribute normal_subgroups(G::GAPGroup) = @@ -201,10 +214,10 @@ julia> maximal_normal_subgroups(symmetric_group(4)) Alt(4) julia> maximal_normal_subgroups(quaternion_group(8)) -3-element Vector{PcGroup}: - Pc group of order 4 - Pc group of order 4 - Pc group of order 4 +3-element Vector{SubPcGroup}: + Sub-pc group of order 4 + Sub-pc group of order 4 + Sub-pc group of order 4 ``` """ @gapattribute maximal_normal_subgroups(G::GAPGroup) = @@ -224,8 +237,8 @@ julia> minimal_normal_subgroups(symmetric_group(4)) Permutation group of degree 4 and order 4 julia> minimal_normal_subgroups(quaternion_group(8)) -1-element Vector{PcGroup}: - Pc group of order 2 +1-element Vector{SubPcGroup}: + Sub-pc group of order 2 ``` """ @gapattribute minimal_normal_subgroups(G::GAPGroup) = @@ -246,10 +259,10 @@ julia> characteristic_subgroups(symmetric_group(3)) Permutation group of degree 3 and order 1 julia> characteristic_subgroups(quaternion_group(8)) -3-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 2 - Pc group of order 1 +3-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 2 + Sub-pc group of order 1 ``` """ @gapattribute characteristic_subgroups(G::GAPGroup) = @@ -268,7 +281,7 @@ julia> center(symmetric_group(3)) (Permutation group of degree 3 and order 1, Hom: permutation group -> Sym(3)) julia> center(quaternion_group(8)) -(Pc group of order 2, Hom: pc group -> pc group) +(Sub-pc group of order 2, Hom: sub-pc group -> pc group) ``` """ @gapattribute center(G::GAPGroup) = _as_subgroup(G, GAP.Globals.Centre(GapObj(G))) @@ -280,7 +293,8 @@ Return the centralizer of `H` in `G`, i.e., the subgroup of all $g$ in `G` such that $g h$ equals $h g$ for every $h$ in `H`, together with its embedding morphism into `G`. """ -function centralizer(G::T, H::T) where T <: GAPGroup +function centralizer(G::GAPGroup, H::GAPGroup) + _check_compatible(G, H) return _as_subgroup(G, GAP.Globals.Centralizer(GapObj(G), GapObj(H))) end @@ -320,11 +334,11 @@ julia> chief_series(alternating_group(4)) Permutation group of degree 4 and order 1 julia> chief_series(quaternion_group(8)) -4-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 4 - Pc group of order 2 - Pc group of order 1 +4-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 4 + Sub-pc group of order 2 + Sub-pc group of order 1 ``` """ @gapattribute chief_series(G::GAPGroup) = _as_subgroups(G, GAP.Globals.ChiefSeries(GapObj(G))) @@ -349,11 +363,11 @@ julia> composition_series(alternating_group(4)) Permutation group of degree 4 and order 1 julia> composition_series(quaternion_group(8)) -4-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 4 - Pc group of order 2 - Pc group of order 1 +4-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 4 + Sub-pc group of order 2 + Sub-pc group of order 1 ``` """ @gapattribute composition_series(G::GAPGroup) = _as_subgroups(G, GAP.Globals.CompositionSeries(GapObj(G))) @@ -370,12 +384,12 @@ An exception is thrown if $G$ is not a $p$-group. # Examples ```jldoctest julia> jennings_series(dihedral_group(16)) -5-element Vector{PcGroup}: - Pc group of order 16 - Pc group of order 4 - Pc group of order 2 - Pc group of order 2 - Pc group of order 1 +5-element Vector{SubPcGroup}: + Sub-pc group of order 16 + Sub-pc group of order 4 + Sub-pc group of order 2 + Sub-pc group of order 2 + Sub-pc group of order 1 julia> jennings_series(dihedral_group(10)) ERROR: ArgumentError: group must be a p-group @@ -431,15 +445,15 @@ See also [`upper_central_series`](@ref) and [`nilpotency_class`](@ref). # Examples ```jldoctest julia> lower_central_series(dihedral_group(8)) -3-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 2 - Pc group of order 1 +3-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 2 + Sub-pc group of order 1 julia> lower_central_series(dihedral_group(12)) -2-element Vector{PcGroup}: - Pc group of order 12 - Pc group of order 3 +2-element Vector{SubPcGroup}: + Sub-pc group of order 12 + Sub-pc group of order 3 julia> lower_central_series(symmetric_group(4)) 2-element Vector{PermGroup}: @@ -467,15 +481,15 @@ See also [`lower_central_series`](@ref) and [`nilpotency_class`](@ref). # Examples ```jldoctest julia> upper_central_series(dihedral_group(8)) -3-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 2 - Pc group of order 1 +3-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 2 + Sub-pc group of order 1 julia> upper_central_series(dihedral_group(12)) -2-element Vector{PcGroup}: - Pc group of order 2 - Pc group of order 1 +2-element Vector{SubPcGroup}: + Sub-pc group of order 2 + Sub-pc group of order 1 julia> upper_central_series(symmetric_group(4)) 1-element Vector{PermGroup}: @@ -518,7 +532,7 @@ end ################################################################################ """ - is_maximal_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup + is_maximal_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) Return whether `H` is a maximal subgroup of `G`, i. e., whether `H` is a proper subgroup of `G` and there is no proper subgroup of `G` @@ -543,7 +557,7 @@ julia> is_maximal_subgroup(sylow_subgroup(G, 3)[1], sylow_subgroup(G, 2)[1]) ERROR: ArgumentError: H is not a subgroup of G ``` """ -function is_maximal_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup +function is_maximal_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) # In earlier times, `is_maximal` returned `false` if `H` was not a subgroup # if `G`, but at that time `G` was the first argument. # In order to avoid wrong results due to the reordering of arguments, @@ -563,7 +577,7 @@ function is_maximal_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup end """ - is_normalized_by(H::T, G::T) where T <: GAPGroup + is_normalized_by(H::GAPGroup, G::GAPGroup) Return whether the group `H` is normalized by `G`, i.e., whether `H` is invariant under conjugation with elements of `G`. @@ -586,10 +600,14 @@ julia> is_normalized_by(derived_subgroup(G)[1], sylow_subgroup(G, 2)[1]) true ``` """ -is_normalized_by(H::T, G::T) where T <: GAPGroup = GAPWrap.IsNormal(GapObj(G), GapObj(H)) +function is_normalized_by(H::GAPGroup, G::GAPGroup) + _check_compatible(H, G) + return GAPWrap.IsNormal(GapObj(G), GapObj(H)) +end + """ - is_normal_subgroup(H::T, G::T) where T <: GAPGroup + is_normal_subgroup(H::GAPGroup, G::GAPGroup) Return whether the group `H` is a normal subgroup of `G`, i.e., whether `H` is a subgroup of `G` that is invariant under conjugation with elements of `G`. @@ -610,12 +628,12 @@ julia> is_normal_subgroup(derived_subgroup(G)[1], sylow_subgroup(G, 2)[1]) false ``` """ -function is_normal_subgroup(H::T, G::T) where T <: GAPGroup +function is_normal_subgroup(H::GAPGroup, G::GAPGroup) return is_subset(H, G) && is_normalized_by(H, G) end """ - is_characteristic_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup + is_characteristic_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) Return whether the subgroup `H` of `G` is characteristic in `G`, i.e., `H` is invariant under all automorphisms of `G`. @@ -639,7 +657,7 @@ julia> is_characteristic_subgroup(sylow_subgroup(G, 3)[1], sylow_subgroup(G, 2)[ ERROR: ArgumentError: H is not a subgroup of G ``` """ -function is_characteristic_subgroup(H::T, G::T; check::Bool = true) where T <: GAPGroup +function is_characteristic_subgroup(H::GAPGroup, G::GAPGroup; check::Bool = true) if check @req is_subset(H, G) "H is not a subgroup of G" end @@ -736,7 +754,7 @@ end Return the quotient group `G/N`, together with the projection `G` -> `G/N`, where `N` is the normal closure of `elements` in `G`. -See [`quo(G::T, N::T) where T <: GAPGroup`](@ref) +See [`quo(G::GAPGroup, N::GAPGroup)`](@ref) for information about the type of `G/N`. """ function quo(G::T, elements::Vector{S}) where T <: GAPGroup where S <: GAPGroupElem @@ -763,7 +781,7 @@ function quo(::Type{Q}, G::T, elements::Vector{S}) where {Q <: GAPGroup, T <: GA end """ - quo([::Type{Q}, ]G::T, N::T) where {Q <: GAPGroup, T <: GAPGroup} + quo([::Type{Q}, ]G::GAPGroup, N::GAPGroup) where Q <: GAPGroup Return the quotient group `G/N`, together with the projection `G` -> `G/N`. @@ -772,7 +790,7 @@ and an exception is thrown if not. If `Q` is not given then the type of `G/N` is not determined by the type of `G`. - `G/N` may have the same type as `G` (which is reasonable if `N` is trivial), -- `G/N` may have type `PcGroup` (which is reasonable if `G/N` is finite and solvable), or +- `G/N` may have type `PcGroup` or `SubPcGroup` (which is reasonable if `G/N` is finite and solvable), or - `G/N` may have type `PermGroup` (which is reasonable if `G/N` is finite and non-solvable). - `G/N` may have type `FPGroup` (which is reasonable if `G/N` is infinite). @@ -792,20 +810,26 @@ julia> typeof(quo(PermGroup, G, N)[1]) PermGroup ``` """ -function quo(G::T, N::T) where T <: GAPGroup +function quo(G::GAPGroup, N::GAPGroup) + _check_compatible(G, N) mp = GAP.Globals.NaturalHomomorphismByNormalSubgroup(GapObj(G), GapObj(N))::GapObj # The call may have found out new information about `GapObj(G)`, # for example that `GapObj(G)` is finite. #FIXME: The GAP function should deal with this situation. GAP.Globals.UseSubsetRelation(GapObj(G), GapObj(N)) - cod = GAP.Globals.ImagesSource(mp)::GapObj +#T HACK: In order to avoid `SubPcGroup`s as return values of `quo` +#T where possible, take the *full* pc group if the range is a pc group +#T and if the GAP mapping is surjective. + cod = GAPWrap.Range(mp) + if !(GAPWrap.IsPcGroup(cod) && GAPWrap.IsSurjective(mp)) + cod = GAPWrap.ImagesSource(mp) + end S = elem_type(G) - S1 = _get_type(cod) - codom = S1(cod) + codom = _oscar_group(cod) return codom, GAPGroupHomomorphism(G, codom, mp) end -function quo(::Type{Q}, G::T, N::T) where {Q <: GAPGroup, T <: GAPGroup} +function quo(::Type{Q}, G::GAPGroup, N::GAPGroup) where Q <: GAPGroup F, epi = quo(G, N) if !(F isa Q) map = isomorphism(Q, F) @@ -854,8 +878,7 @@ PermGroup function maximal_abelian_quotient(G::GAPGroup) map = GAP.Globals.MaximalAbelianQuotient(GapObj(G))::GapObj F = GAPWrap.Range(map)::GapObj - S1 = _get_type(F) - F = S1(F) + F = _oscar_group(F) return F, GAPGroupHomomorphism(G, F, map) end @@ -979,6 +1002,38 @@ function schur_multiplier(::Type{T}, G::Union{GAPGroup, FinGenAbGroup}) where T end +""" + schur_cover(::Type{T} = FPGroup, G::GAPGroup) where T <: GAPGroup + +Return `S, f` where `S` is a Schur cover of `G` and `f` is an epimorphism +from `S` to `G`. + +# Examples +```jldoctest +julia> S, f = schur_cover(symmetric_group(4)); order(S) +48 + +julia> S, f = schur_cover(PermGroup, dihedral_group(12)); order(S) +24 +``` +""" +schur_cover(G::GAPGroup) = schur_cover(FPGroup, G) + +function schur_cover(::Type{T}, G::GAPGroup) where T <: GAPGroup + f = GAPWrap.EpimorphismSchurCover(GapObj(G)) + H = _oscar_group(GAPWrap.Source(f)) + if H isa T + S = H + mp = GAPGroupHomomorphism(S, G, f) + else + iso = isomorphism(T, H) + S = codomain(iso) + mp = compose(inv(iso), GAPGroupHomomorphism(H, G, f)) + end + return S, mp +end + + function __create_fun(mp, codom, ::Type{S}) where S function mp_julia(x::S) el = GAPWrap.Image(mp, GapObj(x)) @@ -1066,10 +1121,10 @@ julia> derived_series(symmetric_group(5)) Alt(5) julia> derived_series(dihedral_group(8)) -3-element Vector{PcGroup}: - Pc group of order 8 - Pc group of order 2 - Pc group of order 1 +3-element Vector{SubPcGroup}: + Sub-pc group of order 8 + Sub-pc group of order 2 + Sub-pc group of order 1 ``` """ @gapattribute derived_series(G::GAPGroup) = _as_subgroups(G, GAP.Globals.DerivedSeriesOfGroup(GapObj(G))) @@ -1103,17 +1158,17 @@ julia> derived_length(dihedral_group(8)) @doc raw""" intersect(V::T...) where T <: Group - intersect(V::AbstractVector{T}) where T <: Group + intersect(V::AbstractVector{<:GAPGroup}) If `V` is $[ G_1, G_2, \ldots, G_n ]$, return the intersection $K$ of the groups $G_1, G_2, \ldots, G_n$, together with the embeddings of $K into $G_i$. """ -function intersect(G1::T, V::T...) where T<:GAPGroup +function intersect(G1::GAPGroup, V::GAPGroup...) return intersect([G1, V...]) end -function intersect(V::AbstractVector{T}) where T<:GAPGroup +function intersect(V::AbstractVector{<:GAPGroup}) L = GapObj(V; recursive=true) K = GAP.Globals.Intersection(L)::GapObj Embds = [_as_subgroup(G, K)[2] for G in V] diff --git a/src/Groups/types.jl b/src/Groups/types.jl index 48c5941b96f9..c5937679461d 100644 --- a/src/Groups/types.jl +++ b/src/Groups/types.jl @@ -9,7 +9,7 @@ For expert usage, you can extract the underlying GAP object via `GapObj`, i.e., if `G` is a `GAPGroup`, then `GapObj(G)` is the `GapObj` underlying `G`. Concrete subtypes of `GAPGroup` are `PermGroup`, `FPGroup`, `PcGroup`, -and `MatrixGroup`. +`SubPcGroup`, and `MatrixGroup`. """ abstract type GAPGroup <: AbstractAlgebra.Group end @@ -142,6 +142,7 @@ It is displayed as product of disjoint cycles. """ const PermGroupElem = BasicGAPGroupElem{PermGroup} + """ PcGroup @@ -151,6 +152,11 @@ Contrary to arbitrary finitely presented groups (see [Finitely presented groups](@ref)), this presentation allows for efficient computations with the group elements. +For a group `G` of type `PcGroup`, the elements in `gens(G)` satisfy the +relators of the underlying presentation. + +Functions that compute subgroups of `G` return groups of type `SubPcGroup`. + # Examples - `cyclic_group(n::Int)`: cyclic group of order `n` - `abelian_group(PcGroup, v::Vector{Int})`: @@ -161,13 +167,32 @@ this presentation allows for efficient computations with the group elements. X::GapObj function PcGroup(G::GapObj) - @assert GAPWrap.IsPcGroup(G) || GAP.Globals.IsPcpGroup(G) - z = new(G) - return z + # The constructor is not allowed to replace the given GAP group. + # (The function `pc_group` may do this.) + @assert _is_full_pc_group(G) + return new(G) + end +end + +function pc_group(G::GapObj) + _is_full_pc_group(G) && return PcGroup(G) + + # Switch to a full pcp or pc group. + if GAPWrap.IsPcpGroup(G) + return PcGroup(GAP.Globals.PcpGroupByPcp(GAP.Globals.Pcp(G)::GapObj)::GapObj) + elseif GAPWrap.IsPcGroup(G) + return PcGroup(GAP.Globals.PcGroupWithPcgs(GAP.Globals.Pcgs(G)::GapObj)::GapObj) end + throw(ArgumentError("G must be in IsPcGroup or IsPcpGroup")) +end + +# Return `true` if the generators of `G` fit to those of its pc presentation. +function _is_full_pc_group(G::GapObj) + GAPWrap.IsPcpGroup(G) || GAPWrap.IsPcGroup(G) || return false + return GAP.Globals.GroupGeneratorsDefinePresentation(G)::Bool end +#T the code for PcpGroups is too expensive! -pc_group(G::GapObj) = PcGroup(G) """ PcGroupElem @@ -181,7 +206,7 @@ generators. # Examples ```jldoctest -julia> G = abelian_group(PcGroup, [2, 4]); +julia> G = abelian_group(PcGroup, [2, 3]); julia> G[1], G[2] (f1, f2) @@ -197,6 +222,64 @@ as shown in Section [Elements of groups](@ref elements_of_groups). """ const PcGroupElem = BasicGAPGroupElem{PcGroup} + +""" + SubPcGroup + +Subgroup of a polycyclic group, +a group that is defined by generators that are elements of a group `G` +of type [`PcGroup`](@ref). +The arithmetic operations with elements are thus performed using the +polycyclic presentation of `G`. + +Operations for computing subgroups of a group of type `PcGroup` or +`SubPcGroup`, such as `derived_subgroup` and `sylow_subgroup`, +return groups of type `SubPcGroup`. +""" +@attributes mutable struct SubPcGroup <: GAPGroup + X::GapObj + full_group::PcGroup +#T better create an embedding! + + function SubPcGroup(G::GapObj) + @assert GAPWrap.IsPcGroup(G) || GAPWrap.IsPcpGroup(G) + if GAPWrap.IsPcGroup(G) + full = GAP.Globals.GroupOfPcgs(GAP.Globals.FamilyPcgs(G)::GapObj)::GapObj +#T use GAPWrap! + else + full = GAP.Globals.PcpGroupByCollectorNC(GAP.Globals.Collector(G)::GapObj)::GapObj + end + z = new(G, PcGroup(full)) + return z + end +end + +sub_pc_group(G::GapObj) = SubPcGroup(G) + + +""" + SubPcGroupElem + +Element of a subgroup of a polycyclic group. + +The elements are displayed in the same way as the elements of full +polycyclic groups, see [`PcGroupElem`](@ref). + +# Examples + +```jldoctest +julia> G = abelian_group(SubPcGroup, [4, 2]); + +julia> G[1], G[2] +(f1, f3) + +julia> G[2]*G[1] +f1*f3 +``` +""" +const SubPcGroupElem = BasicGAPGroupElem{SubPcGroup} + + """ FPGroup @@ -275,11 +358,22 @@ end # Construct an Oscar group wrapping the GAP group `obj` # *and* compatible with a given Oscar group `G`. +sub_type(T::Type) = T +sub_type(::Type{PcGroup}) = SubPcGroup +sub_type(G::GAPGroup) = sub_type(typeof(G)) + +# `_oscar_subgroup(obj, G)` is used to create the subgroup of `G` +# that is described by the GAP group `obj`; # default: ignore `G` -_oscar_group(obj::GapObj, G::T) where T <: GAPGroup = T(obj) +function _oscar_subgroup(obj::GapObj, G::GAPGroup) + S = sub_type(G)(obj) + @assert GAP.Globals.FamilyObj(GapObj(S)) === GAP.Globals.FamilyObj(GapObj(G)) + return S +end +#T better rename to _oscar_subgroup? # `PermGroup`: set the degree of `G` -function _oscar_group(obj::GapObj, G::PermGroup) +function _oscar_subgroup(obj::GapObj, G::PermGroup) n = GAPWrap.LargestMovedPoint(obj) N = degree(G) n <= N || error("requested degree ($N) is smaller than the largest moved point ($n)") @@ -287,7 +381,7 @@ function _oscar_group(obj::GapObj, G::PermGroup) end # `MatrixGroup`: set dimension and ring of `G` -function _oscar_group(obj::GapObj, G::MatrixGroup) +function _oscar_subgroup(obj::GapObj, G::MatrixGroup) d = GAP.Globals.DimensionOfMatrixGroup(obj) d == G.deg || error("requested dimension of matrices ($(G.deg)) does not match the given matrix dimension ($d)") @@ -302,12 +396,6 @@ function _oscar_group(obj::GapObj, G::MatrixGroup) return M end -################################################################################ -# -# "Coerce" an Oscar group `G` to one that is compatible with -# the given Oscar group `S`. -compatible_group(G::T, S::T) where T <: GAPGroup = _oscar_group(GapObj(G), S) - ################################################################################ @@ -347,6 +435,8 @@ struct GAPGroupHomomorphism{S<: GAPGroup, T<: GAPGroup} <: Map{S,T,GAPMap,GAPGro end end +GapObj(f::GAPGroupHomomorphism) = f.map + """ AutomorphismGroup{T} <: GAPGroup @@ -462,34 +552,64 @@ parent_type(::Type{BasicGAPGroupElem{T}}) where T <: GAPGroup = T # X is a GAP filter such as IsPermGroup, and Y is a corresponding # Julia type such as `PermGroup`. # - -const _gap_group_types = Tuple{GAP.GapObj, Type}[] +const _gap_group_types = Tuple{GapObj, Type}[] -function _get_type(G::GapObj) +# `_oscar_group(G)` wraps the GAP group `G` into a suitable Oscar group `OG`, +# such that `GapObj(OG)` is equal to `G`. +# The function is not allowed to create an independent new GAP group object; +# this would be fatal at least for pc groups and fp groups. +function _oscar_group(G::GapObj) for pair in _gap_group_types if pair[1](G) if pair[2] == MatrixGroup #T HACK: We need more information in the case of matrix groups. #T (Usually we should not need to guess the Oscar side of a GAP group.) - return function(dom::GAP.GapObj) - deg = GAP.Globals.DimensionOfMatrixGroup(dom) - iso = iso_gap_oscar(GAP.Globals.FieldOfMatrixGroup(dom)) - ring = codomain(iso) - matgrp = matrix_group(ring, deg) - matgrp.ring_iso = inv(iso) - matgrp.X = dom - return matgrp - end + deg = GAP.Globals.DimensionOfMatrixGroup(G) + iso = iso_gap_oscar(GAP.Globals.FieldOfMatrixGroup(G)) + ring = codomain(iso) + matgrp = matrix_group(ring, deg) + matgrp.ring_iso = inv(iso) + matgrp.X = G + return matgrp elseif pair[2] == AutomorphismGroup - return function(A::GAP.GapObj) - actdom_gap = GAP.Globals.AutomorphismDomain(A) - actdom_oscar = _get_type(actdom_gap)(actdom_gap) - return AutomorphismGroup(A, actdom_oscar) - end + actdom_gap = GAP.Globals.AutomorphismDomain(G) + actdom_oscar = _oscar_group(actdom_gap) + return AutomorphismGroup(G, actdom_oscar) + elseif pair[2] === PcGroup && !_is_full_pc_group(G) + # We have to switch to the appropriate subgroup type + # if and only if `G` is not the full group. + return SubPcGroup(G) else - return pair[2] + return pair[2](G) end end end error("Not a known type of group") end + + +# Check the compatibility of two groups in the sense that an element in the +# first group can be multiplied with an element in the second. +#T To which group does the product belong? +#T In which functions is this used? +# The group *types* can be different. + +# The underlying GAP groups must belong to the same family. +function _check_compatible(G1::GAPGroup, G2::GAPGroup; error::Bool = true) + GAPWrap.FamilyObj(G1.X) === GAPWrap.FamilyObj(G2.X) && return true + error && throw(ArgumentError("G1 and G2 are not compatible")) + return false +end + +# Any two permutation groups are compatible. +_check_compatible(G1::PermGroup, G2::PermGroup; error::Bool = true) = true + +# The groups must have the same dimension and the same base ring. +function _check_compatible(G1::MatrixGroup, G2::MatrixGroup; error::Bool = true) + base_ring(G1) == base_ring(G2) && degree(G1) == degree(G2) && return true + error && throw(ArgumentError("G1 and G2 must have same base_ring and degree")) + return false +end + +#TODO FinGenAbGroup: How do they behave? +# And does this question arise, since embeddings are used throughout? diff --git a/src/InvariantTheory/fundamental_invariants.jl b/src/InvariantTheory/fundamental_invariants.jl index d160494ee3b7..3aca3525f805 100644 --- a/src/InvariantTheory/fundamental_invariants.jl +++ b/src/InvariantTheory/fundamental_invariants.jl @@ -31,7 +31,6 @@ function fundamental_invariants_via_king(RG::FinGroupInvarRing, beta::Int=0) S = elem_type(R)[] G = IdealGens(R, elem_type(R)[], ordR) - singular_assure(G) GO = elem_type(R)[] g = order(Int, group(RG)) @@ -92,7 +91,7 @@ function fundamental_invariants_via_king(RG::FinGroupInvarRing, beta::Int=0) # Compute the monomials which would be input for the Reynolds operator # TODO: Properly wrap kbase (or reimplement it; an iterator would be lovely) - mons = gens(ideal(R, Singular.kbase(G.S, d))) + mons = gens(ideal(R, Singular.kbase(singular_generators(G), d))) if isempty(mons) || (length(mons) == 1 && is_zero(mons[1])) break end diff --git a/src/InvariantTheory/invariant_rings.jl b/src/InvariantTheory/invariant_rings.jl index 478438222f4c..ce48f0ba39e7 100644 --- a/src/InvariantTheory/invariant_rings.jl +++ b/src/InvariantTheory/invariant_rings.jl @@ -224,7 +224,7 @@ function reynolds_operator( end @doc raw""" - reynolds_operator(IR::FinGroupInvarRing{FldT, GrpT, T}, f::T) where {FldT, GrpT, T <: MPolyRingElem} + reynolds_operator(IR::FinGroupInvarRing{FldT, GrpT, T}, f::T) where {FldT, GrpT, T <: MPolyRingElem} In the non-modular case, return the image of `f` under the Reynolds operator projecting onto `IR`. @@ -353,8 +353,8 @@ function reynolds_operator( end @doc raw""" - reynolds_operator(IR::FinGroupInvarRing{FldT, GrpT, T}, f::T, chi::GAPGroupClassFunction) - where {FldT, GrpT, T <: MPolyRingElem} + reynolds_operator(IR::FinGroupInvarRing{FldT, GrpT, T}, f::T, chi::GAPGroupClassFunction) + where {FldT, GrpT, T <: MPolyRingElem} In the case of characteristic zero, return the image of `f` under the twisted Reynolds operator projecting onto the isotypic component of the polynomial ring @@ -427,7 +427,7 @@ end ################################################################################ @doc raw""" - basis(IR::FinGroupInvarRing, d::Int, algorithm::Symbol = :default) + basis(IR::FinGroupInvarRing, d::Int, algorithm::Symbol = :default) Given an invariant ring `IR` and an integer `d`, return a basis for the invariants in degree `d`. @@ -602,7 +602,7 @@ function _molien_series_nonmodular_via_gap( t = GAP.Globals.CharacterTable(GapObj(G)) if G isa MatrixGroup if is_zero(characteristic(coefficient_ring(I))) - psi = natural_character(G).values + psi = GapObj(natural_character(G)) else psi = [ GAP.Globals.BrauerCharacterValue(GAPWrap.Representative(c)) for @@ -617,10 +617,10 @@ function _molien_series_nonmodular_via_gap( ] end if chi === nothing - info = GAP.Globals.MolienSeriesInfo(GAP.Globals.MolienSeries(t, GAP.GapObj(psi))) + info = GAP.Globals.MolienSeriesInfo(GAP.Globals.MolienSeries(t, GapObj(psi))) else info = GAP.Globals.MolienSeriesInfo( - GAP.Globals.MolienSeries(t, GAP.GapObj(psi), chi.values) + GAP.Globals.MolienSeries(t, GapObj(psi), GapObj(chi)) ) end num = S( diff --git a/src/InvariantTheory/iterators.jl b/src/InvariantTheory/iterators.jl index eef9e028a735..b5d2c1fe48c2 100644 --- a/src/InvariantTheory/iterators.jl +++ b/src/InvariantTheory/iterators.jl @@ -124,7 +124,7 @@ function dimension_via_molien_series( end @doc raw""" - iterate_basis(IR::FinGroupInvarRing, d::Int, algorithm::Symbol = :default) + iterate_basis(IR::FinGroupInvarRing, d::Int, algorithm::Symbol = :default) Given an invariant ring `IR` and an integer `d`, return an iterator over a basis for the invariants in degree `d`. diff --git a/src/Misc/Misc.jl b/src/Misc/Misc.jl new file mode 100644 index 000000000000..96f1590cb004 --- /dev/null +++ b/src/Misc/Misc.jl @@ -0,0 +1,3 @@ +include("MoveToHecke.jl") +include("MoveToNemo.jl") +include("MoveToAbstractAlgebra.jl") diff --git a/src/Misc/MoveToAbstractAlgebra.jl b/src/Misc/MoveToAbstractAlgebra.jl new file mode 100644 index 000000000000..56e1cd1caa6f --- /dev/null +++ b/src/Misc/MoveToAbstractAlgebra.jl @@ -0,0 +1,57 @@ +############################################################################### +# A place to accumulate code that should eventually be moved to AbstractAlgebra.jl +############################################################################### + +function symbols(Kt::Generic.RationalFunctionField) + return Kt.S +end + +function number_of_generators( + F::AbstractAlgebra.Generic.FracField{T} +) where {T<:MPolyRingElem} + return number_of_generators(base_ring(F)) +end + +function gen(F::AbstractAlgebra.Generic.FracField{T}) where {T<:PolyRingElem} + return F(gen(base_ring(F))) +end + +function gen(F::AbstractAlgebra.Generic.FracField{T}, i::Int) where {T<:MPolyRingElem} + return F(gen(base_ring(F), i)) +end + +function gens( + F::AbstractAlgebra.Generic.FracField{T} +) where {T<:Union{PolyRingElem,MPolyRingElem}} + return map(F, gens(base_ring(F))) +end + +""" + is_alternating(B::MatElem) + +Return whether the form corresponding to the matrix `B` is alternating, +i.e. `B = -transpose(B)` and `B` has zeros on the diagonal. +Return `false` if `B` is not a square matrix. +""" +function is_alternating(B::MatElem) + n = nrows(B) + n == ncols(B) || return false + + for i in 1:n + is_zero_entry(B, i, i) || return false + for j in (i + 1):n + B[i, j] == -B[j, i] || return false + end + end + + return true +end + +function Base.copy(f::MPolyRingElem) + Ox = parent(f) + g = MPolyBuildCtx(Ox) + for (c, e) in Base.Iterators.zip(MPolyCoeffs(f), MPolyExponentVectors(f)) + push_term!(g, c, e) + end + return finish(g) +end diff --git a/src/Misc/MoveToHecke.jl b/src/Misc/MoveToHecke.jl new file mode 100644 index 000000000000..f08c32276de3 --- /dev/null +++ b/src/Misc/MoveToHecke.jl @@ -0,0 +1,17 @@ +############################################################################### +# A place to accumulate code that should eventually be moved to Hecke.jl +############################################################################### + +function getindex(r::Hecke.SRow, u::AbstractUnitRange) + s = sparse_row(base_ring(r)) + shift = 1-first(u) + for (p,v) = r + if p in u + push!(s.pos, p+shift) + push!(s.values, v) + end + end + return s +end + +Oscar.canonical_unit(x::AbsSimpleNumFieldOrderQuoRingElem) = one(parent(x)) diff --git a/src/Misc/MoveToNemo.jl b/src/Misc/MoveToNemo.jl new file mode 100644 index 000000000000..bcfa3bdf9f4b --- /dev/null +++ b/src/Misc/MoveToNemo.jl @@ -0,0 +1,11 @@ +############################################################################### +# A place to accumulate code that should eventually be moved to Nemo.jl +############################################################################### + +function Hecke.numerator(f::QQPolyRingElem, parent::ZZPolyRing = Hecke.Globals.Zx) + g = parent() + ccall((:fmpq_poly_get_numerator, Nemo.libflint), Cvoid, (Ref{ZZPolyRingElem}, Ref{QQPolyRingElem}), g, f) + return g +end + +Hecke.minpoly(a::QQBarFieldElem) = minpoly(Hecke.Globals.Qx, a) diff --git a/src/Modules/FreeModElem-orderings.jl b/src/Modules/FreeModElem-orderings.jl index cf5a227cdc1b..502a1c5a7a96 100644 --- a/src/Modules/FreeModElem-orderings.jl +++ b/src/Modules/FreeModElem-orderings.jl @@ -58,10 +58,15 @@ Return an array of `Tuple{Int, Int}` that puts the terms of `f` in the order `ord`. The index tuple `(i, j)` corresponds to `term(f[i], j)`. """ function Orderings.permutation_of_terms(f::FreeModElem{<:MPolyRingElem}, ord::ModuleOrdering) + # redispatch to take advantage of the type of ord.o + return Orderings.__permutation_of_terms(f, ord.o) +end + +function Orderings.__permutation_of_terms(f::FreeModElem{<:MPolyRingElem}, ord::Orderings.AbsOrdering) ff = coordinates(f) - p = collect((i, j) for i in ff.pos for j in 1:length(f[i])) + p = collect((i, j) for i in ff.pos for j in 1:length(ff[i])) sort!(p, lt = (k, l) -> (Orderings._cmp_vector_monomials(k[1], ff[k[1]], k[2], - l[1], ff[l[1]], l[2], ord.o) > 0)) + l[1], ff[l[1]], l[2], ord) > 0)) return p end @@ -176,7 +181,7 @@ function Base.iterate(a::GeneralPermutedIterator{:terms, <:FreeModElem{<:MPolyRi state += 1 state <= length(a.perm) || return nothing (i, j) = a.perm[state] - return term(a.elem[i], j)*parent(a.elem)[i], state + return FreeModElem(i, term(a.elem[i], j), parent(a.elem)), state end function Base.eltype(a::GeneralPermutedIterator{:terms, T}) where T <: FreeModElem{<:MPolyRingElem} @@ -196,7 +201,7 @@ function Base.iterate(a::GeneralPermutedIterator{:monomials, <:FreeModElem{<:MPo state += 1 state <= length(a.perm) || return nothing (i, j) = a.perm[state] - return monomial(a.elem[i], j)*parent(a.elem)[i], state + return FreeModElem(i, monomial(a.elem[i], j), parent(a.elem)), state end function Base.eltype(a::GeneralPermutedIterator{:monomials, T}) where T <: FreeModElem{<:MPolyRingElem} @@ -241,7 +246,7 @@ Return the leading monomial of `f` with respect to the order `ordering`. """ function leading_monomial(f::FreeModElem; ordering::ModuleOrdering = default_ordering(parent(f))) (i, j) = index_of_leading_term(f, ordering) - return monomial(f[i], j)*parent(f)[i] + return FreeModElem(i, monomial(f[i], j), parent(f)) end @doc raw""" @@ -251,7 +256,7 @@ Return the leading term of `f` with respect to the order `ordering`. """ function leading_term(f::FreeModElem; ordering::ModuleOrdering = default_ordering(parent(f))) (i, j) = index_of_leading_term(f, ordering) - return term(f[i], j)*parent(f)[i] + return FreeModElem(i, term(f[i], j), parent(f)) end @doc raw""" diff --git a/src/Modules/UngradedModules/FreeModElem.jl b/src/Modules/UngradedModules/FreeModElem.jl index e4dae775114a..e70b3508cfe2 100644 --- a/src/Modules/UngradedModules/FreeModElem.jl +++ b/src/Modules/UngradedModules/FreeModElem.jl @@ -22,6 +22,13 @@ function FreeModElem(c::Vector{T}, parent::FreeMod{T}) where T return FreeModElem{T}(sparse_coords,parent) end +function FreeModElem(i::Int, x::T, parent::FreeMod{T}) where T + @assert 1 <= i <= rank(parent) + sparse_coords = sparse_row(base_ring(parent), [i], [x]) + return FreeModElem{T}(sparse_coords, parent) +end + + #@doc raw""" # (F::FreeMod{T})(c::SRow{T}) where T # @@ -147,10 +154,11 @@ end Return the standard basis of `F`. """ function basis(F::AbstractFreeMod) - bas = elem_type(F)[] + bas = Vector{elem_type(F)}(undef, dim(F)) + e = one(base_ring(F)) for i=1:dim(F) - s = Hecke.sparse_row(base_ring(F), [(i, base_ring(F)(1))]) - push!(bas, F(s)) + s = sparse_row(base_ring(F), [i], [e]) + bas[i] = F(s) end return bas end @@ -172,7 +180,8 @@ Return the `i`th basis vector of `F`, that is, return the `i`th standard unit ve """ function basis(F::AbstractFreeMod, i::Int) @assert 0 < i <= ngens(F) - s = Hecke.sparse_row(base_ring(F), [(i, base_ring(F)(1))]) + e = one(base_ring(F)) + s = sparse_row(base_ring(F), [i], [e]) return F(s) end gen(F::AbstractFreeMod, i::Int) = basis(F,i) @@ -273,7 +282,7 @@ end Return the zero element of `F`. """ -zero(F::AbstractFreeMod) = F(sparse_row(base_ring(F), Tuple{Int, elem_type(base_ring(F))}[])) +zero(F::AbstractFreeMod) = F(sparse_row(base_ring(F))) @doc raw""" parent(a::AbstractFreeModElem) diff --git a/src/Modules/UngradedModules/FreeResolutions.jl b/src/Modules/UngradedModules/FreeResolutions.jl index 672d8c4696e4..b7312236e7b0 100644 --- a/src/Modules/UngradedModules/FreeResolutions.jl +++ b/src/Modules/UngradedModules/FreeResolutions.jl @@ -1,4 +1,4 @@ - function map(FR::FreeResolution, i::Int) +function map(FR::FreeResolution, i::Int) return map(FR.C, i) end @@ -313,7 +313,7 @@ function free_resolution(M::SubquoModule{<:MPolyRingElem}; cc_complete = false #= Start with presentation =# - pm = presentation(M) + pm = algorithm == :mres ? _presentation_minimal(M, minimal_kernel=false) : presentation(M) maps = [pm.maps[j] for j in 2:3] br = base_ring(M) diff --git a/src/Modules/UngradedModules/HomologicalAlgebra.jl b/src/Modules/UngradedModules/HomologicalAlgebra.jl index 81aebe3e72df..5eedf184df53 100644 --- a/src/Modules/UngradedModules/HomologicalAlgebra.jl +++ b/src/Modules/UngradedModules/HomologicalAlgebra.jl @@ -233,7 +233,7 @@ function tor(M::ModuleFP, N::ModuleFP, i::Int) return simplify_light(homology(lifted_resolution,i))[1] end -simplify_light(F::FreeMod) = F +simplify_light(F::FreeMod) = (F, identity_map(F), identity_map(F)) #TODO, mF # (hom lift) => hom and tensor functor diff --git a/src/Modules/UngradedModules/Methods.jl b/src/Modules/UngradedModules/Methods.jl index a0fe741d2c81..a283cf48ad00 100644 --- a/src/Modules/UngradedModules/Methods.jl +++ b/src/Modules/UngradedModules/Methods.jl @@ -228,22 +228,6 @@ ring_map(f::FreeModuleHom) = f.ring_map ring_map(f::SubQuoHom{<:AbstractFreeMod, <:ModuleFP, Nothing}) = nothing ring_map(f::SubQuoHom) = f.ring_map -############################# -#TODO move to Hecke -# re-evaluate and use or not - -function getindex(r::Hecke.SRow, u::AbstractUnitRange) - s = sparse_row(base_ring(r)) - shift = 1-first(u) - for (p,v) = r - if p in u - push!(s.pos, p+shift) - push!(s.values, v) - end - end - return s -end - function default_ordering(F::FreeMod) if iszero(F) return default_ordering(base_ring(F))*ModuleOrdering(F, Orderings.ModOrdering(Vector{Int}(), :lex)) diff --git a/src/Modules/UngradedModules/Presentation.jl b/src/Modules/UngradedModules/Presentation.jl index 9820cf8f1142..24ba8ce886df 100644 --- a/src/Modules/UngradedModules/Presentation.jl +++ b/src/Modules/UngradedModules/Presentation.jl @@ -1,12 +1,16 @@ #################### @doc raw""" - presentation(M::SubquoModule) + presentation(M::SubquoModule; minimal=false) -Return a free presentation of `M`. +Return a free presentation of `M`. If `minimal` is set to `true`, the returned +presentation is minimal. """ -function presentation(SQ::SubquoModule) - if is_graded(SQ) +function presentation(SQ::SubquoModule; + minimal=false) + if minimal + return _presentation_minimal(SQ) + elseif is_graded(SQ) return _presentation_graded(SQ) else return _presentation_simple(SQ) @@ -559,6 +563,56 @@ function prune_with_map(M::ModuleFP{T}) where {T<:MPolyRingElem{<:FieldElem}} # return M_new, phi end +function _presentation_minimal(SQ::ModuleFP{T}; + minimal_kernel::Bool=true) where {T<:MPolyRingElem{<:FieldElem}} + R = base_ring(SQ) + + # Prepare to set some names + br_name = AbstractAlgebra.get_name(R) + if br_name === nothing + br_name = "br" + end + + SQ_new, phi = prune_with_map(SQ) + F0 = ambient_free_module(SQ_new) + + # M_new is a quotient of a free module + proj_map = hom(F0, SQ_new, gens(SQ_new), check=false) + F0_to_SQ = compose(proj_map, phi) + F0_to_SQ.generators_map_to_generators = true + AbstractAlgebra.set_name!(F0, "$br_name^$(ngens(F0))") + + # if we want a minimal kernel too, we go to prune_with_map + K, inc = sub(F0, relations(SQ_new)) + if minimal_kernel + K, inc2 = prune_with_map(K) + inc = compose(inc2, inc) + end + F1 = if is_graded(SQ) + graded_free_module(R, degrees_of_generators(K)) + else + free_module(R, ngens(K)) + end + F1_to_F0 = compose(hom(F1, K, gens(K), check=false), inc) + AbstractAlgebra.set_name!(F1, "$br_name^$(ngens(F1))") + + # When there is no kernel, clean things up + if is_zero(F1) + F1 = FreeMod(R, 0) + F1_to_F0 = hom(F1, F0, elem_type(F0)[]; check=false) + end + + # prepare the end of the presentation + Z = FreeMod(R, 0) + AbstractAlgebra.set_name!(Z, "0") + SQ_to_Z = hom(SQ, Z, elem_type(Z)[zero(Z) for i in 1:ngens(SQ)]; check=false) + + # compile the presentation complex + CC = Hecke.ComplexOfMorphisms(ModuleFP, ModuleFPHom[F1_to_F0, F0_to_SQ, SQ_to_Z], check=false, seed = -2) + set_attribute!(CC, :show => Hecke.pres_show) + return CC +end + function prune_with_map(F::FreeMod) return F, hom(F, F, gens(F)) end diff --git a/src/Modules/UngradedModules/SubquoModuleElem.jl b/src/Modules/UngradedModules/SubquoModuleElem.jl index c37829c57b34..b36e758226d4 100644 --- a/src/Modules/UngradedModules/SubquoModuleElem.jl +++ b/src/Modules/UngradedModules/SubquoModuleElem.jl @@ -933,7 +933,9 @@ end Return the generators of `M`. """ function gens(M::SubquoModule{T}) where T - return SubquoModuleElem{T}[gen(M,i) for i=1:ngens(M)] + R = base_ring(M) + e = R(1) + return [SubquoModuleElem{T}(sparse_row(R, [i], [e]), M) for i in 1:ngens(M)] end @doc raw""" @@ -943,9 +945,7 @@ Return the `i`th generator of `M`. """ function gen(M::SubquoModule{T}, i::Int) where T R = base_ring(M) - v::SRow{T} = sparse_row(R) - v.pos = [i] - v.values = [R(1)] + v = sparse_row(R, [i], [R(1)]) return SubquoModuleElem{T}(v, M) end diff --git a/src/Modules/homological-algebra.jl b/src/Modules/homological-algebra.jl index 0f72cde55219..1e41dc547b48 100644 --- a/src/Modules/homological-algebra.jl +++ b/src/Modules/homological-algebra.jl @@ -11,7 +11,7 @@ ############################################################################## @doc raw""" - fitting_ideal(M::ModuleFP{T}, i::Int) where T <: MPolyRingElem + fitting_ideal(M::ModuleFP{T}, i::Int) where T <: MPolyRingElem Return the `i`-th Fitting ideal of `M`. @@ -86,7 +86,7 @@ end ############################################################################## @doc raw""" - is_flat(M::ModuleFP{T}) where T <: MPolyRingElem + is_flat(M::ModuleFP{T}) where T <: MPolyRingElem Return `true` if `M` is flat, `false` otherwise. @@ -141,7 +141,7 @@ function is_flat(M::SubquoModule{T}) where T <: MPolyRingElem end @doc raw""" - non_flat_locus(M::ModuleFP{T}) where T <: MPolyRingElem + non_flat_locus(M::ModuleFP{T}) where T <: MPolyRingElem Return an ideal of `base_ring(M)` which defines the non-flat-locus of `M` in the sense that the localization of `M` at a prime ideal of `base_ring(M)` @@ -203,7 +203,7 @@ end ############################################################################## @doc raw""" - is_regular_sequence(V::Vector{T}, M::ModuleFP{T}) where T <: MPolyRingElem + is_regular_sequence(V::Vector{T}, M::ModuleFP{T}) where T <: MPolyRingElem Return `true` if the elements of `V` form, in the given order, a regular sequence on `M`. Return `false`, otherwise. @@ -273,7 +273,7 @@ end ############################################################################## @doc raw""" - koszul_homology(V::Vector{T}, M::ModuleFP{T}, p::Int) where T <: MPolyRingElem + koszul_homology(V::Vector{T}, M::ModuleFP{T}, p::Int) where T <: MPolyRingElem If $f_1, \dots, f_r$ are the entries of `V` in the given order, return the `p`-th homology module of the complex $K(f_1, \dots, f_r)\otimes_R M$, where $K(f_1, \dots, f_r)$ is the @@ -428,7 +428,7 @@ end ############################################################################## @doc raw""" - depth(I::MPolyIdeal{T}, M::ModuleFP{T}) where T <: MPolyRingElem + depth(I::MPolyIdeal{T}, M::ModuleFP{T}) where T <: MPolyRingElem Return the depth of `I` on `M`. @@ -572,7 +572,7 @@ end ############################################################################## @doc raw""" - koszul_matrix(V::Vector{T}, p::Int) where T <: MPolyRingElem + koszul_matrix(V::Vector{T}, p::Int) where T <: MPolyRingElem If $f_1, \dots, f_r$ are the entries of `V` in the given order, return the matrix representing the `p`-th map of the Koszul complex $K(f_1, \dots, f_r)$. @@ -626,7 +626,7 @@ function _koszul_matrix_from_singular(V::Vector{T}, i::Int) where T <: MPolyRing end @doc raw""" - koszul_complex(V::Vector{T}) where T <: MPolyRingElem + koszul_complex(V::Vector{T}) where T <: MPolyRingElem If $f_1, \dots, f_r$ are the entries of `V` in the given order, return the Koszul complex $K(f_1, \dots, f_r)$. diff --git a/src/NumberTheory/GaloisGrp/GaloisGrp.jl b/src/NumberTheory/GaloisGrp/GaloisGrp.jl index 40c1dc8108d0..2404a49d1e25 100644 --- a/src/NumberTheory/GaloisGrp/GaloisGrp.jl +++ b/src/NumberTheory/GaloisGrp/GaloisGrp.jl @@ -2730,14 +2730,22 @@ function are_disjoint(G::GaloisCtx{T}, S::GaloisCtx{T}) where T <: Union{Hecke.q @vprint :GaloisGroup 2 "\n poly discs have small gcd ($g)..." o1 = any_order(number_field(G.f, cached = false, check = false)[1]) p = radical(g) - O1 = pmaximal_overorder(o1, p) + if is_probable_prime(p) + O1 = pmaximal_overorder(o1, p) + else + O1, = Hecke._TameOverorderBL(o1, [p]) + end p = gcd(discriminant(O1), p) if isone(p) @vprint :GaloisGroup 2 "p-maximal order of 1st is p-free\n" return true end o2 = any_order(number_field(S.f, cached = false, check = false)[1]) - O2 = pmaximal_overorder(o2, p) + if is_probable_prime(p) + O2 = pmaximal_overorder(o2, p) + else + O2, = Hecke._TameOverorderBL(o2, [p]) + end @vprint :GaloisGroup 2 "p-maximal order of 2nd is " p = gcd(discriminant(O2), p) if isone(p) diff --git a/src/Oscar.jl b/src/Oscar.jl index c4d402264c21..d926c3059fb9 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -274,6 +274,8 @@ include("TropicalGeometry/TropicalGeometry.jl") include("InvariantTheory/InvariantTheory.jl") +include("Misc/Misc.jl") + # Serialization should always come at the end of Oscar source code # but before experimental, any experimental serialization should # be written inside the corresponding experimental code sub directory @@ -281,13 +283,6 @@ include("Serialization/main.jl") include("../experimental/Experimental.jl") -if is_dev -# include("../examples/ModStdNF.jl") -# include("../examples/ModStdQ.jl") -# include("../examples/ModStdQt.jl") - include("../examples/PrimDec.jl") -end - include("deprecations.jl") @doc raw""" diff --git a/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl b/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl index 04fc34151c04..1eb7cf43e588 100644 --- a/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl +++ b/src/PolyhedralGeometry/PolyhedralComplex/standard_constructions.jl @@ -45,7 +45,7 @@ function common_refinement( end @doc raw""" - k_skeleton(PC::PolyhedralComplex,k::Int) + k_skeleton(PC::PolyhedralComplex,k::Int) Return the k-skeleton of a polyhedral complex. diff --git a/src/PolyhedralGeometry/PolyhedralFan/standard_constructions.jl b/src/PolyhedralGeometry/PolyhedralFan/standard_constructions.jl index 37d4abca7e26..5681dd871699 100644 --- a/src/PolyhedralGeometry/PolyhedralFan/standard_constructions.jl +++ b/src/PolyhedralGeometry/PolyhedralFan/standard_constructions.jl @@ -112,7 +112,7 @@ function star_subdivision(Sigma::_FanLikeType, new_ray::AbstractVector{<:Integer end mc_old = maximal_cones(IncidenceMatrix, Sigma) - facet_normals = pm_object(Sigma).FACET_NORMALS + facet_normals = matrix(QQ, pm_object(Sigma).FACET_NORMALS) refinable_cones = _get_maximal_cones_containing_vector(Sigma, new_ray) @req length(refinable_cones) > 0 "$new_ray not contained in support of fan." new_cones = _get_refinable_facets(Sigma, new_ray, refinable_cones, facet_normals, mc_old) @@ -137,12 +137,12 @@ function _get_refinable_facets( Sigma::_FanLikeType, new_ray::AbstractVector{<:IntegerUnion}, refinable_cones::Vector{Int}, - facet_normals::AbstractMatrix, + facet_normals::MatElem, mc_old::IncidenceMatrix, ) new_cones = Vector{Int}[] v_facet_signs = _facet_signs(facet_normals, new_ray) - R = pm_object(Sigma).RAYS + R = rays(Sigma) hd = pm_object(Sigma).HASSE_DIAGRAM hd_graph = Graph{Directed}(hd.ADJACENCY) hd_maximal_cones = inneighbors(hd_graph, hd.TOP_NODE + 1) @@ -168,11 +168,11 @@ end function _get_refinable_facets_of_cone( mc_hd_index::Int, - facet_normals::AbstractMatrix, + facet_normals::MatElem, hd, hd_graph::Graph{Directed}, v_facet_signs::AbstractVector, - R::AbstractMatrix, + R::AbstractVector{<:RayVector}, mcfi::Vector{Int}, ) refinable_facets = Vector{Int}[] @@ -182,7 +182,7 @@ function _get_refinable_facets_of_cone( for fc_index in inneighbors(hd_graph, mc_hd_index) fc_indices = Polymake.to_one_based_indexing(Polymake._get_entry(hd.FACES, fc_index - 1)) length(fc_indices) > 0 || return refinable_facets # The only facet was 0 - inner_ray = sum([R[i, :] for i in fc_indices]) + inner_ray = sum([R[i] for i in fc_indices]) fc_facet_signs = _facet_signs(facet_normals, inner_ray) if (!_check_containment_via_facet_signs(v_facet_signs[mcfi], fc_facet_signs[mcfi])) push!(refinable_facets, Vector{Int}(fc_indices)) @@ -191,11 +191,8 @@ function _get_refinable_facets_of_cone( return refinable_facets end -# FIXME: Small workaround, since sign does not work for polymake types. -_int_sign(e) = e > 0 ? 1 : (e < 0 ? -1 : 0) -_facet_signs(F::AbstractMatrix, v::AbstractVector{<:IntegerUnion}) = - [_int_sign(e) for e in (F * Polymake.Vector{Polymake.Integer}(v))] -_facet_signs(F::AbstractMatrix, v::AbstractVector) = [_int_sign(e) for e in (F * v)] +_facet_signs(F::MatElem, v::AbstractVector) = sign.(Int, F * v)[:, 1] + function _check_containment_via_facet_signs(smaller::Vector{Int}, bigger::Vector{Int}) for a in zip(smaller, bigger) p = prod(a) diff --git a/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl b/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl index 8891640b8e10..dbb7bda55346 100644 --- a/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl +++ b/src/PolyhedralGeometry/Polyhedron/standard_constructions.jl @@ -2142,7 +2142,7 @@ function rand_normal_polytope(d::Int, n::Int; seed=nothing, precision=nothing) end @doc raw""" - rss_associahedron(n::Int) + rss_associahedron(n::Int) Produce a polytope of constrained expansions in ambient dimension `n` according to [RSS03](@cite). diff --git a/src/PolyhedralGeometry/helpers.jl b/src/PolyhedralGeometry/helpers.jl index 1a223840d881..f062dda88f15 100644 --- a/src/PolyhedralGeometry/helpers.jl +++ b/src/PolyhedralGeometry/helpers.jl @@ -1,7 +1,7 @@ import Polymake: IncidenceMatrix @doc raw""" - IncidenceMatrix + 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. @@ -34,7 +34,7 @@ number_of_rows(A::Polymake.Matrix) = Polymake.nrows(A) number_of_columns(A::Polymake.Matrix) = Polymake.ncols(A) @doc raw""" - row(i::IncidenceMatrix, n::Int) + row(i::IncidenceMatrix, n::Int) Return the indices where the `n`-th row of `i` is `true`, as a `Set{Int}`. @@ -56,7 +56,7 @@ Set{Int64} with 3 elements: row(i::IncidenceMatrix, n::Int) = convert(Set{Int}, Polymake.row(i, n)) @doc raw""" - column(i::IncidenceMatrix, n::Int) + column(i::IncidenceMatrix, n::Int) Return the indices where the `n`-th column of `i` is `true`, as a `Set{Int}`. diff --git a/src/Rings/AlgClosureFp.jl b/src/Rings/AlgClosureFp.jl index 32c57802f697..699d9d9d2e3e 100644 --- a/src/Rings/AlgClosureFp.jl +++ b/src/Rings/AlgClosureFp.jl @@ -14,7 +14,7 @@ using ..Oscar import Base: +, -, *, //, ==, deepcopy_internal, hash, isone, iszero, one, parent, show, zero -import ..Oscar.AbstractAlgebra: pretty, Lowercase +import ..Oscar: pretty, Lowercase import ..Oscar: algebraic_closure, base_field, base_ring, base_ring_type, characteristic, data, degree, divexact, elem_type, embedding, has_preimage_with_preimage, IntegerUnion, is_unit, map_entries, @@ -31,7 +31,7 @@ end function show(io::IO, A::AlgClosure) io = pretty(io) - print(io, "Algebraic Closure of ", Lowercase(), A.k) + print(io, "Algebraic closure of ", Lowercase(), A.k) end base_field(A::AlgClosure) = A.k diff --git a/src/Rings/MPolyMap/AffineAlgebras.jl b/src/Rings/MPolyMap/AffineAlgebras.jl index 6a28f81c11f6..d78b06601274 100644 --- a/src/Rings/MPolyMap/AffineAlgebras.jl +++ b/src/Rings/MPolyMap/AffineAlgebras.jl @@ -99,7 +99,7 @@ Return `true` if `F` is bijective, `false` otherwise. function is_bijective(F::AffAlgHom) return is_injective(F) && is_surjective(F) end - + ################################################################################ # # Finiteness diff --git a/src/Rings/MPolyMap/MPolyQuo.jl b/src/Rings/MPolyMap/MPolyQuo.jl index 2af27d29e5b0..a221a05a2298 100644 --- a/src/Rings/MPolyMap/MPolyQuo.jl +++ b/src/Rings/MPolyMap/MPolyQuo.jl @@ -48,7 +48,7 @@ to `S`, if such a homomorphism exists, and throw an error, otherwise. !!! note The function returns a well-defined homomorphism `A` $\to$ `S` iff the - given data defines a homomorphism from the base ring of `A` to `S` whose + given data defines a homomorphism `base_ring(A)` $\to$ `S` whose kernel contains the modulus of `A`. This condition is checked by the function in case `check = true` (default). diff --git a/src/Rings/MPolyQuo.jl b/src/Rings/MPolyQuo.jl index c3b019cd1d05..62134d1a479a 100644 --- a/src/Rings/MPolyQuo.jl +++ b/src/Rings/MPolyQuo.jl @@ -724,9 +724,9 @@ end @doc raw""" simplify(a::MPolyQuoIdeal) -If `a` is an ideal of the quotient of a multivariate polynomial ring `R` by an ideal `I` of `R`, say, -replace the internal polynomial representative of each generator of `a` by its normal form -mod `I` with respect to the `default_ordering` on `R`. +If `a` is an ideal of the affine algebra `A = R/I`, say, replace the internal polynomial representative +of each generator of `a` by its normal form mod `I` with respect to `ordering(A)`. + # Examples ```jldoctest @@ -866,13 +866,8 @@ end @doc raw""" simplify(f::MPolyQuoRingElem) -If `f` is an element of the quotient of a multivariate polynomial ring `R` by an ideal `I` of `R`, say, -replace the internal polynomial representative of `f` by its normal form mod `I` with respect to -the `default_ordering` on `R`. - -!!! note -Since this method only has a computational backend for quotients of polynomial rings -over a field, it is not implemented generically. +If `f` is an element of the affine algebra `A = R/I`, say, replace the internal polynomial representative of `f` +by its normal form mod `I` with respect to `ordering(A)`. # Examples ```jldoctest @@ -1753,7 +1748,7 @@ end Given a homogeneous ideal `I` of a graded affine algebra over a field, return an array containing a minimal set of generators of `I`. If `I` -is the zero ideal an empty list is returned. +is the zero ideal, an empty list is returned. # Examples ```jldoctest @@ -1885,3 +1880,41 @@ end # extension of common functionality symbols(A::MPolyQuoRing) = symbols(base_ring(A)) + +######################################################################## +# Tensor products of rings +######################################################################## + +function tensor_product(A::MPolyRing, B::MPolyRing) + kk = coefficient_ring(A) + @assert kk === coefficient_ring(B) "coefficient rings do not coincide" + res, a, b = polynomial_ring(kk, symbols(A), symbols(B); cached=false) + return res, hom(A, res, a), hom(B, res, b) +end + +function tensor_product(A::MPolyRing, B::MPolyQuoRing) + R = base_ring(B) + AR, inc_A, inc_R = tensor_product(A, R) + I = ideal(AR, inc_R.(gens(modulus(B)))) + res, pr = quo(R, I) + return res, compose(inc_A, pr), hom(B, res, pr.(inc_R.(gens(R)))) +end + +function tensor_product(A::MPolyQuoRing, B::MPolyQuoRing) + RA = base_ring(A) + RB = base_ring(B) + P, inc_A, inc_B = tensor_product(RA, RB) + I = ideal(P, inc_A.(gens(modulus(A)))) + ideal(P, inc_B.(gens(modulus(B)))) + res, pr = quo(R, I) + return res, hom(A, res, pr.(inc_A.(gens(RA))); check=false), hom(B, res, pr.(inc_B.(gens(RB))); check=false) +end + +function tensor_product(A::MPolyQuoRing, B::MPolyRing) + RA = base_ring(A) + P, inc_A, inc_B = tensor_product(RA, B) + I = ideal(P, inc_A.(gens(modulus(A)))) + res, pr = quo(R, I) + return res, hom(A, res, pr.(inc_A.(gens(RA))); check=false), compose(inc_B, pr) +end + + diff --git a/src/Rings/binomial_ideals.jl b/src/Rings/binomial_ideals.jl index 85a1e8c73a55..40ca7110f10d 100644 --- a/src/Rings/binomial_ideals.jl +++ b/src/Rings/binomial_ideals.jl @@ -293,7 +293,7 @@ function cellular_decomposition_macaulay(I::MPolyIdeal) end @assert !isone(I) @assert is_binomial(I) - return _cellular_decomposition(I) + return _cellular_decomposition_macaulay(I) end function _cellular_decomposition_macaulay(I::MPolyIdeal) diff --git a/src/Rings/groebner.jl b/src/Rings/groebner.jl index 0a29dc6b0b17..7a05e3ec8003 100644 --- a/src/Rings/groebner.jl +++ b/src/Rings/groebner.jl @@ -103,13 +103,6 @@ degrevlex([x, y]) ``` """ function _compute_standard_basis(B::IdealGens, ordering::MonomialOrdering, complete_reduction::Bool = false) - # incorrect one - #singular_assure(B, ordering) - #R = B.Sx - #I = Singular.Ideal(R, gens(B.S)...) - #i = Singular.std(I, complete_reduction = complete_reduction) - #BA = IdealGens(B.Ox, i, complete_reduction) - # correct one (segfaults) gensSord = singular_generators(B, ordering) i = Singular.std(gensSord, complete_reduction = complete_reduction) BA = IdealGens(B.Ox, i, complete_reduction) @@ -522,8 +515,7 @@ julia> S = syzygy_generators([x^3+y+2,x*y^2-13*x^2,y-14]) """ function syzygy_generators(a::Vector{<:MPolyRingElem}) I = ideal(a) - singular_assure(I) - s = Singular.syz(I.gens.S) + s = Singular.syz(singular_generators(I)) F = free_module(parent(a[1]), length(a)) @assert rank(s) == length(a) return [F(s[i]) for i=1:Singular.ngens(s)] @@ -814,8 +806,8 @@ function reduce_with_quotients_and_unit(F::Vector{T}, G::IdealGens{T}; ordering: end @doc raw""" - reduce_with_quotients_and_unit(I::IdealGens, J::IdealGens; - ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + 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` @@ -868,7 +860,7 @@ end @doc raw""" - reduce_with_quotients(I::IdealGens, J::IdealGens; ordering::MonomialOrdering = default_ordering(base_ring(J)), complete_reduction::Bool = false) + 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` @@ -1302,8 +1294,7 @@ 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_assure(G) - Singular.dimension(G.S) == 0 || error("Dimension of corresponding ideal must be zero.") + Singular.dimension(singular_generators(G)) == 0 || error("Dimension of corresponding ideal must be zero.") SR_destination, = Singular.polynomial_ring(base_ring(G.Sx),["$i" for i in gens(G.Sx)]; ordering = Singular.ordering_as_symbol(singular(ordering))) ptr = Singular.libSingular.fglmzero(G.S.ptr, G.Sx.ptr, SR_destination.ptr) @@ -1532,6 +1523,7 @@ function groebner_basis_hilbert_driven(I::MPolyIdeal{P}; 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). diff --git a/src/Rings/mpoly-affine-algebras.jl b/src/Rings/mpoly-affine-algebras.jl index 417e35e8a30f..5f99c148eae1 100644 --- a/src/Rings/mpoly-affine-algebras.jl +++ b/src/Rings/mpoly-affine-algebras.jl @@ -31,6 +31,10 @@ If, say, `A = R/I`, where `R` is a multivariate polynomial ring over a field `K`, and `I` is an ideal of `R`, return `true` if `A` is finite-dimensional as a `K`-vector space, `false` otherwise. +!!! note + `A` is finite-dimensional as a `K`-vector space iff it has Krull dimension zero. This condition is checked by the function. + + # Examples ```jldoctest julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); @@ -63,12 +67,10 @@ end vector_space_dimension(A::MPolyQuoRing) If, say, `A = R/I`, where `R` is a multivariate polynomial ring over a field -`K`, and `I` is a zero-dimensional ideal of `R`, return the dimension of `A` -as a `K`-vector space. +`K`, and `I` is an ideal of `R`, return the dimension of `A` as a `K`-vector space. -If `A` is not a finite-dimensional vector space, an exception is raised. -Use [`is_finite_dimensional_vector_space`](@ref) to test whether the dimension -is finite. +!!! note + If `A` is not finite-dimensional as a `K`-vector space, an error is thrown. # Examples ```jldoctest @@ -108,13 +110,12 @@ end monomial_basis(A::MPolyQuoRing) If, say, `A = R/I`, where `R` is a multivariate polynomial ring over a field -`K`, and `I` is a zero-dimensional ideal of `R`, return a vector of monomials of `R` +`K`, and `I` is an ideal of `R`, return a vector of monomials of `R` such that the residue classes of these monomials form a basis of `A` as a `K`-vector space. -If `A` is not a finite-dimensional vector space, an exception is raised. -Use [`is_finite_dimensional_vector_space`](@ref) to test whether the dimension -is finite. +!!! note + If `A` is not finite-dimensional as a `K`-vector space, an error is thrown. # Examples ```jldoctest @@ -401,7 +402,7 @@ function hilbert_function(A::MPolyQuoRing, d::Int) end @doc raw""" - hilbert_polynomial(A::MPolyQuoRing) + hilbert_polynomial(A::MPolyQuoRing) Given a $\mathbb Z$-graded affine algebra $A = R/I$ over a field $K$, where the grading is inherited from the standard $\mathbb Z$-grading on the polynomial ring $R$, @@ -919,18 +920,17 @@ function is_normal(A::MPolyRing; check::Bool=true) end @doc raw""" - is_normal(A::MPolyAnyRing; check::Bool=true) -> Bool - -# Input: -- an affine algebra ``A`` over a perfect field, -- if `check` is `true`, then confirm that ``A`` is reduced; this is expensive. + is_normal(A::MPolyQuoRing; check::Bool=true) -> Bool -# Output: -Returns `true` if the ring ``A`` is normal, `false` otherwise. +Given an affine algebra `A` over a perfect field, return `true` if `A` is normal, and `false` otherwise. !!! note This function performs the first step of the normalization algorithm of Greuel, Laplagne, and Seelisch [GLS10](@cite) and may, thus, be more efficient than computing the full normalization of `A`. +!!! warning + If `check` is `true`, the function checks whether `A` is indeed reduced. This may take some time. + + # Examples ```jldoctest julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); @@ -955,7 +955,7 @@ function is_normal(A::MPolyQuoRing; check::Bool=true) end @doc raw""" - is_cohen_macaulay(A::MPolyQuoRing) + is_cohen_macaulay(A::MPolyQuoRing) Given a $\mathbb Z$-graded affine algebra `A = R/I` over a field, say, `K`, where the grading is inherited from the standard $\mathbb Z$-grading on the polynomial ring `R`, @@ -1118,7 +1118,7 @@ end is_algebraically_independent(V::Vector{T}) where T <: Union{MPolyRingElem, MPolyQuoRingElem} Given a vector `V` of elements of a multivariate polynomial ring over a field `K`, say, or of a quotient of such a ring, -return if the elements of `V` are algebraically independent over `K`. +return `true` if the elements of `V` are algebraically independent over `K`, and `false` otherwise. # Examples ```jldoctest diff --git a/src/Rings/mpoly-graded.jl b/src/Rings/mpoly-graded.jl index efa04e056677..31357ec61979 100644 --- a/src/Rings/mpoly-graded.jl +++ b/src/Rings/mpoly-graded.jl @@ -1269,7 +1269,7 @@ end homogeneous_component(R::MPolyDecRing, g::FinGenAbGroupElem) Given a polynomial ring `R` over a field which is graded by a free -group of type `FinGenAbGroup`, and given an element `g` of that group, +group, and given an element `g` of that group, return the homogeneous component of `R` of degree `g` as a standard vector space. Additionally, return the map which sends an element of that vector space to the corresponding monomial in `R`. @@ -1287,7 +1287,7 @@ an integer `d`, convert `d` into an element `g` of the grading group of `R` proceed as above. !!! note - If the component is not finite dimensional, an error message will be thrown. + If the component is not finite dimensional, an error will be thrown. # Examples ```jldoctest @@ -1348,9 +1348,9 @@ end @doc raw""" - vector_space(K::Field, polys::Vector{T}; target = nothing) where {T <: MPolyRingElem} + vector_space(K::Field, polys::Vector{T}; target = nothing) where {T <: MPolyRingElem} -Return a `K`-vector space `V` and an epimorphism from `V` onto the `K`-vector +Return a `K`-vector space `V` and an isomorphism from `V` to the `K`-vector space spanned by the polynomials in `polys`. Note that all polynomials must have the same parent `R`, and `K` must be equal @@ -1363,13 +1363,12 @@ julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]); julia> polys = [x + y, x - y, 2x + 3y]; julia> V, VtoPoly = vector_space(QQ, polys) -(Vector space of dimension 3 over QQ, Map: V -> R) +(Vector space of dimension 2 over QQ, Map: V -> R) julia> VtoPoly.(basis(V)) -3-element Vector{QQMPolyRingElem}: +2-element Vector{QQMPolyRingElem}: x y - 0 ``` """ function vector_space(K::Field, polys::Vector{T}; target = nothing) where {T <: MPolyRingElem} @@ -1385,7 +1384,7 @@ function vector_space(K::Field, polys::Vector{T}; target = nothing) where {T <: expvec = Dict{Vector{Int}, Int}() expvec_idx = Vector{Vector{Int}}() M = sparse_matrix(K) - + # take polynomials and turn them into sparse row vectors for f in polys pos = Vector{Int}() @@ -1402,21 +1401,21 @@ function vector_space(K::Field, polys::Vector{T}; target = nothing) where {T <: end # row reduce - rref!(M) + d = rref!(M) # turn the reduced sparse rows back into polynomials - b = Vector{elem_type(R)}() - for i in 1:nrows(M) + b = Vector{elem_type(R)}(undef, d) + for i in 1:d s = MPolyBuildCtx(R) for (k,v) in M[i] push_term!(s, v, expvec_idx[k]) end - push!(b, finish(s)) + b[i] = finish(s) end # create a standard vector space of the right dimension F = free_module(K, length(b); cached = false) - + # helper computing images function img(x) sum([x[i] * b[i] for i in 1:length(b) if !is_zero_entry(x.v, 1, i)]; init = zero(R)) diff --git a/src/Rings/mpoly-ideals.jl b/src/Rings/mpoly-ideals.jl index 63d6a4fefac9..db9d58a43ac8 100644 --- a/src/Rings/mpoly-ideals.jl +++ b/src/Rings/mpoly-ideals.jl @@ -2053,13 +2053,14 @@ small_generating_set(I::MPolyIdeal{<:MPolyDecRingElem}; algorithm::Symbol=:simpl # Grassmann Pluecker Ideal # ################################################################################ +#returns Pluecker ideal in ring with standard grading function grassmann_pluecker_ideal(subspace_dimension::Int, ambient_dimension::Int) I = convert(MPolyIdeal{QQMPolyRingElem}, Polymake.ideal.pluecker_ideal(subspace_dimension, ambient_dimension)) - base = base_ring(I) + base, _ = grade(base_ring(I)) o = degrevlex(base) - return ideal(IdealGens(base, gens(I), o; + return ideal(IdealGens(base,base.(gens(I)), o; keep_ordering=true, isReduced=true, isGB=true)) @@ -2068,9 +2069,9 @@ end @doc raw""" grassmann_pluecker_ideal([ring::MPolyRing,] subspace_dimension::Int, ambient_dimension::Int) -Given a ring, an ambient dimension and a subspace dimension return the ideal in the given ring -generated by the Plücker relations. If the ring is not specified return the ideal -in a multivariate polynomial ring over the rationals. +Given a (possibly graded) ring, an ambient dimension, and a subspace dimension, return the ideal in the ring +generated by the Plücker relations. If the input ring is not graded, return the ideal in the ring with the standard grading. +If the ring is not specified return the ideal in a multivariate polynomial ring over the rationals with the standard grading. The Grassmann-Plücker ideal is the homogeneous ideal generated by the relations defined by the Plücker Embedding of the Grassmannian. That is given Gr$(k, n)$ the Moduli @@ -2102,6 +2103,9 @@ function grassmann_pluecker_ideal(ring::MPolyRing, @assert is_integral(c) return coeff_ring(numerator(c)) end + if !is_graded(ring) + ring, _ = grade(ring) + end h = hom(base_ring(I), ring, coeffmap, gens(ring)) converted_generators = elem_type(ring)[h(g) for g in groebner_basis(I; ordering = degrevlex(base_ring(I)))] ideal(IdealGens(ring, converted_generators, o; diff --git a/src/Rings/mpoly.jl b/src/Rings/mpoly.jl index 9aee702a1023..ec542c614f8d 100644 --- a/src/Rings/mpoly.jl +++ b/src/Rings/mpoly.jl @@ -20,21 +20,6 @@ function _variables_for_singular(n::Int) end _variables_for_singular(S::Vector{Symbol}) = _variables_for_singular(length(S)) -function number_of_generators(F::AbstractAlgebra.Generic.FracField{T}) where {T <: MPolyRingElem} - return number_of_generators(base_ring(F)) -end - -function gen(F::AbstractAlgebra.Generic.FracField{T}) where {T <: PolyRingElem} - return F(gen(base_ring(F))) -end - -function gen(F::AbstractAlgebra.Generic.FracField{T}, i::Int) where {T <: MPolyRingElem} - return F(gen(base_ring(F), i)) -end - -function gens(F::AbstractAlgebra.Generic.FracField{T}) where {T <: Union{PolyRingElem, MPolyRingElem}} - return map(F, gens(base_ring(F))) -end ###################################################################### # pretty printing for iJulia notebooks.. @@ -78,10 +63,70 @@ using .Orderings #type for orderings, use this... #in general: all algos here needs revision: do they benefit from gb or not? +@doc raw""" + default_ordering(R::MPolyRing) + +Return the monomial ordering that is used for computations with ideals in `R` +if no other ordering is specified -- either directly by the user or by +requirements of a specific algorithm. +""" @attr MonomialOrdering{T} function default_ordering(R::T) where {T<:MPolyRing} return degrevlex(R) end +# Only for internal use +function set_default_ordering!(R::MPolyRing, o::MonomialOrdering) + @assert R === base_ring(o) + set_attribute!(R, :default_ordering, o) + return nothing +end + +@doc raw""" + with_ordering(f, R::MPolyRing, o::MonomialOrdering) + +Use the monomial ordering `o` for computations in `R` during the execution of +`f`. +This may be used with `do` block syntax, see the example. + +This functionality is meant for advanced users. In general it should not be +necessary to explicitly set a monomial ordering. +Further, there is no guarantee that `o` is actually used. For example, if an +algorithm requires an elimination ordering, `o` might be ignored. + +# Example +```jldoctest withordering +julia> R, (x, y, z) = QQ["x", "y", "z"]; + +julia> f = x + y^2; + +julia> I = ideal(R, [y^2 - z, x - z^2]); + +julia> normal_form(f, I) # this uses degrevlex +x + z + +julia> with_ordering(R, lex(R)) do + # this uses lex + normal_form(f, I) + end +z^2 + z +``` +Notice that in this small example we could have achieved the same by using the +keyword argument `ordering`: +```jldoctest withordering +julia> normal_form(f, I, ordering = lex(R)) +z^2 + z +``` +""" +function with_ordering(f, R::MPolyRing, o::MonomialOrdering) + old = default_ordering(R) + set_default_ordering!(R, o) + x = try + f() + finally + set_default_ordering!(R, old) + end + return x +end mutable struct BiPolyArray{S} Ox::NCRing #Oscar Poly Ring or Algebra @@ -316,7 +361,7 @@ function singular_generators(B::IdealGens, monorder::MonomialOrdering=default_or end @doc raw""" -set_ordering(I::IdealGens, monord::MonomialOrdering) + set_ordering(I::IdealGens, monord::MonomialOrdering) Return an ideal generating system with an associated monomial ordering. @@ -727,15 +772,6 @@ function oscar_assure(B::IdealGens) oscar_assure(B.gens) end -function Base.copy(f::MPolyRingElem) - Ox = parent(f) - g = MPolyBuildCtx(Ox) - for (c,e) = Base.Iterators.zip(MPolyCoeffs(f), MPolyExponentVectors(f)) - push_term!(g, c, e) - end - return finish(g) -end - function map_entries(R, M::Singular.smatrix) s = nrows(M), ncols(M) S = parent(R(zero(base_ring(M)))) @@ -1240,7 +1276,3 @@ function hessian_matrix(f::MPolyRingElem) end hessian(f::MPolyRingElem) = det(hessian_matrix(f)) - -function set_default_ordering!(S::MPolyRing, ord::MonomialOrdering) - set_attribute!(S, :default_ordering, ord) -end diff --git a/src/Rings/orderings.jl b/src/Rings/orderings.jl index 746b67b60569..6b3559ff1757 100644 --- a/src/Rings/orderings.jl +++ b/src/Rings/orderings.jl @@ -133,7 +133,7 @@ end """ The product of `a` and `b` (`vcat` of the the matrices) """ -mutable struct ProdOrdering <: AbsGenOrdering +struct ProdOrdering <: AbsGenOrdering a::AbsGenOrdering b::AbsGenOrdering end @@ -428,7 +428,7 @@ function _cmp_monomials(f::MPolyRingElem, k::Int, g::MPolyRingElem, l::Int, o::S elseif tdk < tdl return -1 end - for i in reverse(o.vars) + for i in Iterators.reverse(o.vars) ek = exponent(f, k, i) el = exponent(g, l, i) if ek > el @@ -1719,7 +1719,7 @@ function induce(vars::Vector{Int}, o::ProdOrdering) end @doc raw""" - induce(vars::AbstractVector{<:MPolyRingElem}, ord::ModuleOrdering) + induce(vars::AbstractVector{<:MPolyRingElem}, ord::MonomialOrdering) Return the monomial ordering on the variables `vars` induced by transferring the ordering `ord`. @@ -1748,15 +1748,9 @@ end # Module orderings (not module Orderings) # For ordering the generators (I think) -mutable struct ModOrdering{T} <: AbsModOrdering +struct ModOrdering{T <: AbstractVector{Int}} <: AbsModOrdering gens::T ord::Symbol - function ModOrdering(u::T, s::Symbol) where {T <: AbstractVector{Int}} - r = new{T}() - r.gens = u - r.ord = s - return r - end end mutable struct ModuleOrdering{S} @@ -1774,9 +1768,10 @@ base_ring(a::ModuleOrdering) = a.M base_ring_type(::Type{ModuleOrdering{S}}) where {S} = S -mutable struct ModProdOrdering <: AbsModOrdering - a::AbsOrdering - b::AbsOrdering +struct ModProdOrdering{A <: AbsOrdering, B <: AbsOrdering} <: AbsModOrdering + a::A + b::B + ModProdOrdering(a::A, b::B) where {A <: AbsOrdering, B <: AbsOrdering} = new{A,B}(a,b) end Base.:*(a::AbsGenOrdering, b::AbsModOrdering) = ModProdOrdering(a, b) @@ -1979,8 +1974,13 @@ end Return the permutation that puts the terms of `f` in the order `ord`. """ function permutation_of_terms(f::MPolyRingElem, ord::MonomialOrdering) + # redispatch to take advantage of the type of ord.o + return __permutation_of_terms(f, ord.o) +end + +function __permutation_of_terms(f::MPolyRingElem, ord::AbsOrdering) p = collect(1:length(f)) - sort!(p, lt = (k, l) -> (Orderings._cmp_monomials(f, k, f, l, ord.o) < 0), rev = true) + sort!(p, lt = (k, l) -> (Orderings._cmp_monomials(f, k, f, l, ord) < 0), rev = true) return p end diff --git a/src/Serialization/Fields.jl b/src/Serialization/Fields.jl index b1ac6ff47c38..e99459a8836f 100644 --- a/src/Serialization/Fields.jl +++ b/src/Serialization/Fields.jl @@ -39,7 +39,7 @@ function get_parents(parent_ring::T) where T <: Union{FracField, if !serialize_with_id(parent_ring) return RingMatSpaceUnion[] end - + base = base_ring(parent_ring) parents = get_parents(base) push!(parents, parent_ring) @@ -121,7 +121,7 @@ end @register_serialization_type AbsSimpleNumField uses_id function save_object(s::SerializerState, K::SimpleNumField) - save_data_dict(s) do + save_data_dict(s) do save_typed_object(s, defining_polynomial(K), :def_pol) save_object(s, var(K), :var) end @@ -175,7 +175,7 @@ function load_object(s::DeserializerState, ::Type{<: NumFieldElemTypeUnion}, polynomial = load_node(s) do _ load_object(s, PolyRingElem, parents[1:end - 1]) end - + K = parents[end] loaded_terms = evaluate(polynomial, gen(K)) return K(loaded_terms) @@ -211,7 +211,7 @@ end # elements function save_object(s::SerializerState, k::FqFieldElem) K = parent(k) - + if absolute_degree(K) == 1 save_object(s, lift(ZZ, k)) else @@ -293,7 +293,7 @@ end @register_serialization_type FracField uses_id function save_object(s::SerializerState, K::FracField) - save_data_dict(s) do + save_data_dict(s) do save_typed_object(s, base_ring(K), :base_ring) end end @@ -379,7 +379,7 @@ function load_object(s::DeserializerState, loaded_num = load_node(s, 1) do _ load_object(s, coeff_type, parents[1:end - 1]) end - + loaded_den = load_node(s, 2) do _ load_object(s, coeff_type, parents[1:end - 1]) end @@ -405,7 +405,7 @@ end function save_object(s::SerializerState, r::ArbFieldElem) c_str = ccall((:arb_dump_str, Nemo.libflint), Ptr{UInt8}, (Ref{ArbFieldElem},), r) save_object(s, unsafe_string(c_str)) - + # free memory ccall((:flint_free, Nemo.libflint), Nothing, (Ptr{UInt8},), c_str) end @@ -532,6 +532,55 @@ function load_object(s::DeserializerState, ::Type{<:EmbeddedNumFieldElem}, paren return parent_field(loaded_alg_elem) end +################################################################################ +# QQBar + +@register_serialization_type QQBarField +@register_serialization_type QQBarFieldElem + +function save_object(s::SerializerState, q::QQBarFieldElem) + is_unique = false + min_poly_q = minpoly(q) + roots_min_q = roots(QQBarField(), min_poly_q) + precision = 30 + approximation = undef + + while(!is_unique) + CC = AcbField(precision; cached = false) + approximation = CC(q) + n_overlaps = length(filter(x -> overlaps(approximation, CC(x)), roots_min_q)) + if n_overlaps == 1 + is_unique = true + else + precision *= 2 + end + end + + save_data_dict(s) do + save_object(s, min_poly_q, :minpoly) + save_object(s, approximation, :acb) + save_object(s, precision, :precision) + end +end + +function load_object(s::DeserializerState, ::Type{QQBarFieldElem}) + Qx, x = polynomial_ring(QQ, :x, cached = false) + min_poly = load_object(s, PolyRingElem{QQ}, Qx, :minpoly) + precision = load_object(s, Int, :precision) + CC = AcbField(precision; cached = false) + approximation = load_object(s, AcbFieldElem, CC, :acb) + roots_min_poly = roots(QQBarField(), min_poly) + + try + only(filter(x -> overlaps(approximation, CC(x)), roots_min_poly)) + catch e + if e isa ArgumentError + error("The approximation is not precise enough to determine a unique root") + end + rethrow(e) + end +end + ################################################################################ # Padic Field @register_serialization_type PadicField diff --git a/src/Serialization/GAP.jl b/src/Serialization/GAP.jl index 09b40499f009..26908eb23103 100644 --- a/src/Serialization/GAP.jl +++ b/src/Serialization/GAP.jl @@ -31,7 +31,7 @@ end # # the Oscar (de)serialization methods that delegate to GAP's method selection # -@register_serialization_type GAP.GapObj uses_id +@register_serialization_type GapObj uses_id function save_object(s::SerializerState, X::GapObj) GAP.Globals.SerializeInOscar(X, s) @@ -98,7 +98,7 @@ install_GAP_serialization(:IsFreeGroup, save_object(s, wfilt, :wfilt) # rank and names of generators Xnames = GAP.getbangproperty(elfam, :names)::GapObj - if GAP.Globals.Length(Xnames)::GAP.Obj == GAP.Globals.infinity + if !GAPWrap.IsFinite(Xnames) # store the initial names and the prefix prefix = GAP.getbangindex(Xnames, 1)::GapObj save_object(s, string(prefix), :nameprefix) @@ -143,12 +143,12 @@ install_GAP_deserialization( GapObj(nameprefix) end init = load_node(s, :names) do names - GapObj([GapObj(x) for x in names], true) + GapObj(names; recursive = true) end G = GAP.Globals.FreeGroup(wfilt, GAP.Globals.infinity, prefix, init)::GapObj else init = load_node(s, :names) do names - GapObj([GapObj(x) for x in names], true) + GapObj(names; recursive = true) end G = GAP.Globals.FreeGroup(wfilt, init)::GapObj end @@ -240,7 +240,7 @@ install_GAP_serialization(:IsPcGroup, function(X::GapObj, s::SerializerState) elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(X)) fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj - if GAP.Globals.IsIdenticalObj(fullpcgs, GAPWrap.Pcgs(X)) + if fullpcgs === GAPWrap.Pcgs(X) # full pc group: Save the defining data. save_data_dict(s) do save_object(s, "IsPcGroup", :GapType) @@ -294,12 +294,10 @@ install_GAP_deserialization( fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) rws = GAP.Globals.SingleCollector(F, GapObj(relord))::GapObj for (i, elm) in load_object(s, Vector, (Tuple, [Int, [Vector, Int]]), :power_rels) - GAP.Globals.SetPower(rws, i, - GapObj(GAPWrap.ObjByExtRep(fam, GapObj(elm)))) + GAP.Globals.SetPower(rws, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) end for (j, i, elm) in load_object(s, Vector, (Tuple, [Int, Int, [Vector, Int]]), :comm_rels) - GAP.Globals.SetCommutator(rws, j, i, - GapObj(GAPWrap.ObjByExtRep(fam, GapObj(elm)))) + GAP.Globals.SetCommutator(rws, j, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) end G = GAP.Globals.GroupByRwsNC(rws)::GapObj else diff --git a/src/Serialization/Groups.jl b/src/Serialization/Groups.jl index 5a2af32005a4..d34966db886e 100644 --- a/src/Serialization/Groups.jl +++ b/src/Serialization/Groups.jl @@ -194,11 +194,12 @@ end ############################################################################## -# PcGroup +# PcGroup, SubPcGroup @register_serialization_type PcGroup uses_id +@register_serialization_type SubPcGroup uses_id -function save_object(s::SerializerState, G::PcGroup) +function save_object(s::SerializerState, G::Union{PcGroup, SubPcGroup}) save_data_dict(s) do save_object(s, G.X, :X) end @@ -208,6 +209,10 @@ function load_object(s::DeserializerState, ::Type{PcGroup}) return PcGroup(load_object(s, GapObj, :X)) end +function load_object(s::DeserializerState, ::Type{SubPcGroup}) + return SubPcGroup(load_object(s, GapObj, :X)) +end + ############################################################################## # PcGroupElem @@ -215,8 +220,9 @@ end # that defines the element. @register_serialization_type PcGroupElem uses_params +@register_serialization_type SubPcGroupElem uses_params -function save_object(s::SerializerState, g::PcGroupElem) +function save_object(s::SerializerState, g::Union{PcGroupElem, SubPcGroupElem}) elfam = GAPWrap.FamilyObj(g.X) fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs) save_object(s, Vector{Int}(GAP.Globals.ExponentsOfPcElement(fullpcgs, g.X))) @@ -230,6 +236,15 @@ function load_object(s::DeserializerState, ::Type{PcGroupElem}, parent_group::Pc return Oscar.group_element(parent_group, gapelm) end +function load_object(s::DeserializerState, ::Type{SubPcGroupElem}, parent_group::SubPcGroup) + lo = load_object(s, Vector, Int) + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(parent_group.X)) + fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj + gapelm = GAP.Globals.PcElementByExponentsNC(fullpcgs, GapObj(lo, true))::GapObj + return Oscar.group_element(parent_group, gapelm) +end + + ############################################################################## # FinGenAbGroup diff --git a/src/Serialization/PolyhedralGeometry.jl b/src/Serialization/PolyhedralGeometry.jl index c16c4dc399b2..883ab3a84b9d 100644 --- a/src/Serialization/PolyhedralGeometry.jl +++ b/src/Serialization/PolyhedralGeometry.jl @@ -87,7 +87,7 @@ function save_object(s::SerializerState, lp::LinearProgram{QQFieldElem}) lpcoeffs = lp.polymake_lp.LINEAR_OBJECTIVE serialized = Polymake.call_function(Symbol("Core::Serializer"), :serialize, lpcoeffs) jsonstr = Polymake.call_function(:common, :encode_json, serialized) - save_data_dict(s) do + save_data_dict(s) do save_object(s, lp.feasible_region, :feasible_region) save_object(s, lp.convention, :convention) save_json(s, jsonstr, :lpcoeffs) @@ -133,7 +133,7 @@ function save_object(s::SerializerState, milp::MixedIntegerLinearProgram{QQField end end -function load_object(s::DeserializerState, ::Type{<: MixedIntegerLinearProgram}, field::QQField) +function load_object(s::DeserializerState, ::Type{<: MixedIntegerLinearProgram}, field::QQField) fr = load_object(s, Polyhedron, field, :feasible_region) conv = load_object(s, String, :convention) milp_coeffs = load_node(s, :milp_coeffs) do coeffs @@ -150,7 +150,7 @@ function load_object(s::DeserializerState, ::Type{<: MixedIntegerLinearProgram}, json(vars) ) end - + all = Polymake._lookup_multi(pm_object(fr), "MILP") index = 0 for i in 1:length(all) @@ -170,4 +170,3 @@ end @register_serialization_type Polyhedron uses_params @register_serialization_type PolyhedralFan uses_params @register_serialization_type SubdivisionOfPoints uses_params - diff --git a/src/Serialization/containers.jl b/src/Serialization/containers.jl index 8081f034f1d8..7509105a957a 100644 --- a/src/Serialization/containers.jl +++ b/src/Serialization/containers.jl @@ -305,6 +305,37 @@ function save_type_params(s::SerializerState, obj::Dict{S, T}) where {S <: Union end end +function save_type_params(s::SerializerState, obj::Dict{T, S}) where {T, S} + save_data_dict(s) do + save_object(s, encode_type(Dict), :name) + save_data_dict(s, :params) do + if serialize_with_params(S) + if isempty(obj) + save_object(s, encode_type(S), :value_type) + else + v = first(values(obj)) + save_object(s, encode_type(S), :value_type) + save_type_params(s, v, :value_params) + end + else + save_object(s, encode_type(S), :value_type) + end + + if serialize_with_params(T) + if isempty(obj) + save_object(s, encode_type(T), :key_type) + else + k = first(keys(obj)) + save_object(s, encode_type(T), :key_type) + save_type_params(s, k, :key_params) + end + else + save_object(s, encode_type(T), :key_type) + end + end + end +end + function load_type_params(s::DeserializerState, ::Type{<:Dict}) if haskey(s, :value_type) key_type = load_node(_ -> decode_type(s), s, :key_type) @@ -316,7 +347,13 @@ function load_type_params(s::DeserializerState, ::Type{<:Dict}) load_params_node(s) end end - + + if serialize_with_params(key_type) + d[:key_params] = load_node(s, :key_params) do _ + load_params_node(s) + end + end + return d end @@ -341,32 +378,65 @@ end function save_object(s::SerializerState, obj::Dict{S, T}) where {S <: Union{Symbol, String, Int}, T} save_data_dict(s) do for (k, v) in obj - save_object(s, v, Symbol(k)) + if !Base.issingletontype(typeof(v)) + save_object(s, v, Symbol(k)) + end + end + end +end + +function save_object(s::SerializerState, obj::Dict) + save_data_array(s) do + for (k, v) in obj + save_object(s, (k, v)) end end end +function load_object(s::DeserializerState, ::Type{Dict{String, Int}}) + return Dict{String, Int}(string(k) => parse(Int, v) for (k,v) in s.obj) +end + function load_object(s::DeserializerState, ::Type{<:Dict}, params::Dict{Symbol, Any}) key_type = params[:key_type] value_type = haskey(params, :value_type) ? params[:value_type] : Any dict = Dict{key_type, value_type}() - for (k, _) in s.obj + for (i, (k, _)) in enumerate(s.obj) if k == :key_type continue end if key_type == Int key = parse(Int, string(k)) + elseif haskey(params, :key_params) # type is not Int, String or Symbol + load_node(s, i) do _ + # 1 is for first entry of tuple which is the key in this case + key = load_object(s, key_type, params[:key_params], 1) + end else key = key_type(k) end if value_type != Any if serialize_with_params(value_type) - dict[key] = load_object(s, value_type, params[:value_params], k) + if haskey(params, :key_params) # key type is not Int, String or Symbol + load_node(s, i) do _ + # 2 is for second entry of tuple which is the value in this case + dict[key] = load_object(s, value_type, params[:value_params], 2) + end + else + dict[key] = load_object(s, value_type, params[:value_params], k) + end else - dict[key] = load_object(s, value_type, k) + if haskey(params, :key_params) # key type is not Int, String or Symbol + load_node(s, i) do _ + # 2 is for second entry of tuple which is the value in this case + dict[key] = load_object(s, value_type, 2) + end + else + dict[key] = load_object(s, value_type, k) + end end elseif params[k] isa Type dict[key] = load_object(s, params[k], k) diff --git a/src/TropicalGeometry/TropicalGeometry.jl b/src/TropicalGeometry/TropicalGeometry.jl index 31601929b6a4..b98e77e45aa8 100644 --- a/src/TropicalGeometry/TropicalGeometry.jl +++ b/src/TropicalGeometry/TropicalGeometry.jl @@ -1,15 +1,3 @@ -############################################################################### -# -# Temporary workarounds -# -############################################################################### -function symbols(Kt::Generic.RationalFunctionField) - return Kt.S -end - - - - ############################################################################### # # Includes diff --git a/src/TropicalGeometry/debug.jl b/src/TropicalGeometry/debug.jl index 32d752f80ee5..953dfbcdec8c 100644 --- a/src/TropicalGeometry/debug.jl +++ b/src/TropicalGeometry/debug.jl @@ -7,8 +7,7 @@ println(base_ring(inI)) # same print as input for tropical_link x = gens(Kx) inI1 = inI + ideal(Kx,[x[1]+1]) -singular_assure(inI1) -singularIdeal = inI1.gens.S +singularIdeal = singular_generators(inI1) singularRing = base_ring(singularIdeal) singularIdeal = Singular.satstd(singularIdeal,Singular.MaximalIdeal(singularRing,1)) inI1 = ideal(Kx,singularIdeal) diff --git a/src/TropicalGeometry/links.jl b/src/TropicalGeometry/links.jl index 9f5b4f761929..468b63db2a55 100644 --- a/src/TropicalGeometry/links.jl +++ b/src/TropicalGeometry/links.jl @@ -94,8 +94,7 @@ function tropical_link(inI; p_adic_prime=1000003) ### # Optional: Compute a partially saturated GB using satstd ### - singular_assure(inI1) # defines necessary objects on the Singular side - singularIdeal = inI1.gens.S + singularIdeal = singular_generators(inI1) singularRing = base_ring(singularIdeal) singularIdeal = Singular.satstd(singularIdeal,Singular.MaximalIdeal(singularRing,1)) inI1 = ideal(Kx,singularIdeal) # cast the Singular ideal back to an Oscar ideal @@ -122,8 +121,7 @@ function tropical_link(inI; p_adic_prime=1000003) ### # Optional: Compute a partially saturated GB using satstd ### - singular_assure(inI0) # defines necessary objects on the Singular side - singularIdeal = inI0.gens.S + singularIdeal = singular_generators(inI0) singularRing = base_ring(singularIdeal) singularIdeal = Singular.satstd(singularIdeal,Singular.MaximalIdeal(singularRing,1)) inI0 = ideal(Kx,singularIdeal) # cast the Singular ideal back to an Oscar ideal diff --git a/src/exports.jl b/src/exports.jl index 8cb857a86836..1bb441d0808c 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -29,6 +29,7 @@ export AbsProjectiveCurve export AbsProjectiveScheme export AbsProjectiveSchemeMorphism export AbsProjectiveVariety +export AbsRationalMap export AbstractAlgebra export AffineAlgebraicSet export AffineHalfspace @@ -155,6 +156,8 @@ export SimplicialComplex export Singular export Sp export SubObjectIterator +export SubPcGroup +export SubPcGroupElem export SubQuoHom export SubdivisionOfPoints, subdivision_of_points export SubquoModule @@ -1243,6 +1246,7 @@ export rand_subpolytope export rank export rank_action export rational_equivalence_class +export rational_map export rational_point_conic export rational_solutions export rational_to_continued_fraction_hirzebruch_jung @@ -1319,6 +1323,7 @@ export save_mps export scalar_product export scheme export schensted +export schur_cover export schur_index export schur_multiplier export schur_polynomial @@ -1513,6 +1518,7 @@ export weight_ordering export weighted_projective_space export weyl_algebra export weyl_vector +export with_ordering export witt_index export wreath_product export write_as_full diff --git a/system/Build.jl b/system/Build.jl index 8c8b81336e35..14b36016ab03 100644 --- a/system/Build.jl +++ b/system/Build.jl @@ -27,11 +27,12 @@ Oscar.system("precompile.jl") sysimage=joinpath(tmp, "Oscar.$(Libdl.dlext)") if !("JULIA_CPU_TARGET" in keys(ENV)) || (ENV["JULIA_CPU_TARGET"] == "") - PackageCompiler.create_sysimage([:Oscar], sysimage_path=sysimage, precompile_execution_file=CO) + println("Building system image for generic target. Use JULIA_CPU_TARGET to change.") + target = PackageCompiler.default_app_cpu_target() else target = ENV["JULIA_CPU_TARGET"] - PackageCompiler.create_sysimage([:Oscar], sysimage_path=sysimage, precompile_execution_file=CO; cpu_target=target) end +PackageCompiler.create_sysimage([:Oscar], sysimage_path=sysimage, precompile_execution_file=CO; cpu_target=target) println("(re)start julia as") println("\tjulia -J $(sysimage)") diff --git a/system/precompile.jl b/system/precompile.jl index f0630ea0ad9a..957072614e73 100644 --- a/system/precompile.jl +++ b/system/precompile.jl @@ -1,9 +1,8 @@ import Pkg Pkg.add("Documenter") Pkg.add("PrettyTables") -Pkg.add("Aqua") - +Pkg.add("JSONSchema") Pkg.precompile() - +ENV["OSCAR_TEST_SUBSET"] = "short" include(joinpath(pkgdir(Oscar), "test", "runtests.jl")) Hecke.system("precompile.jl") diff --git a/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl b/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl index aef9d9c393b3..5dfcaddf18f9 100644 --- a/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl +++ b/test/AlgebraicGeometry/Schemes/BlowupMorphism.jl @@ -1,18 +1,17 @@ @testset "basics about blowups" begin R, (x,y,z) = QQ["x", "y", "z"] f = x^2 + y^3 + z^5 - X = CoveredScheme(spec(R, ideal(R, f))) - U = affine_charts(X)[1] # the first chart - - IZ = IdealSheaf(U, ideal(OO(U),[x, y, z]),covered_scheme=X) + X = spec(R, ideal(R, f)) + I = ideal(OO(X), [x, y, z]) + IZ = IdealSheaf(X, I) bl = blow_up(IZ) - @test bl isa AbsCoveredSchemeMorphism{<:AbsCoveredScheme, typeof(X), Nothing, <:BlowupMorphism} + @test bl isa AbsCoveredSchemeMorphism{<:AbsCoveredScheme, typeof(space(IZ)), Nothing, <:BlowupMorphism} @test Oscar.underlying_morphism(bl) === projection(bl) Y = domain(bl) - @test codomain(bl) === X + @test codomain(bl) === space(IZ) @test Y isa AbsCoveredScheme E = exceptional_divisor(bl) diff --git a/test/AlgebraicGeometry/Schemes/CoveredProjectiveSchemes.jl b/test/AlgebraicGeometry/Schemes/CoveredProjectiveSchemes.jl index f913c6bf2a5a..7b6b94a1ccae 100644 --- a/test/AlgebraicGeometry/Schemes/CoveredProjectiveSchemes.jl +++ b/test/AlgebraicGeometry/Schemes/CoveredProjectiveSchemes.jl @@ -11,6 +11,12 @@ p = projection(B) @test domain(p) === Bcov @test codomain(p) === covered_scheme(P) + + X = affine_space(QQ,2) + x1, x2 = coordinates(X) + I = ideal([x1^2, x2^3]) + p = blow_up(X, I) + @test !is_normal(domain(p)) end @testset "Oscar.blow_up_chart" begin diff --git a/test/AlgebraicGeometry/Schemes/IdealSheaves.jl b/test/AlgebraicGeometry/Schemes/IdealSheaves.jl index 84ec39f15240..067cd36d4662 100644 --- a/test/AlgebraicGeometry/Schemes/IdealSheaves.jl +++ b/test/AlgebraicGeometry/Schemes/IdealSheaves.jl @@ -45,7 +45,7 @@ I = IdealSheaf(X, U, OO(U).([x[1]-1, x[2]-2, x[3]-3])) J = IdealSheaf(X, U, OO(U).([x[1]-5, x[2]-1, x[3]])) - @test I+J == IdealSheaf(X, U, [one(OO(U))]) + @test I+J == Oscar.unit_ideal_sheaf(X) K = I*J end diff --git a/test/AlgebraicGeometry/Schemes/ProjectiveSchemes.jl b/test/AlgebraicGeometry/Schemes/ProjectiveSchemes.jl index 575c7b24563b..04c1c80eeb20 100644 --- a/test/AlgebraicGeometry/Schemes/ProjectiveSchemes.jl +++ b/test/AlgebraicGeometry/Schemes/ProjectiveSchemes.jl @@ -398,3 +398,27 @@ end @test arithmetic_genus(Y) isa Int64 @test genus(Y) == -2 end + +@testset "rational morphisms of projective schemes" begin + IP1 = projective_space(QQ, [:s, :t]) + IP2 = projective_space(QQ, [:x, :y, :z]) + + x, y, z = gens(homogeneous_coordinate_ring(IP2)) + s, t = gens(homogeneous_coordinate_ring(IP1)) + phi = rational_map(IP1, IP2, [s^2, s*t, t^2]) + S, a, b = Oscar.graph_ring(phi) + + C, _ = sub(IP2, ideal(homogeneous_coordinate_ring(IP2), y^2-x*z)) + img_gens = [s^2, s*t, t^2] + phi = rational_map(IP1, C, img_gens) + + IP3 = projective_space(QQ, [:u, :v, :w, :x]) + S3 = homogeneous_coordinate_ring(IP3) + u, v, w, x = gens(S3) + + IP1xIP1, _ = sub(IP3, u*x - v*w) + + pr = rational_map(IP1xIP1, IP1, [u, v]) + sec = rational_map(IP1, IP1xIP1, [s, t, s, t]) +end + diff --git a/test/AlgebraicGeometry/Schemes/Resolution_structure.jl b/test/AlgebraicGeometry/Schemes/Resolution_structure.jl index 9922acad004a..928934d93456 100644 --- a/test/AlgebraicGeometry/Schemes/Resolution_structure.jl +++ b/test/AlgebraicGeometry/Schemes/Resolution_structure.jl @@ -5,7 +5,7 @@ IS = IdealSheaf(W,I) WC = scheme(IS) inc_X = Oscar.CoveredClosedEmbedding(WC,IS) - phi = Oscar.embedded_desingularization(inc_X) + phi = embedded_desingularization(inc_X) H = IdealSheaf(W, ideal(R, x + 3*y), covered_scheme=WC) @test is_subset(total_transform(phi, H), strict_transform(phi, H)) @test length(phi.ex_div) == 4 @@ -18,6 +18,8 @@ @test is_one(ideal_sheaf(phi.ex_div[3]) + ideal_sheaf(phi.ex_div[4]) + image_ideal(phi.embeddings[end])) @test !is_empty(singular_locus(domain(phi.embeddings[2]))[1]) @test is_empty(singular_locus(domain(phi.embeddings[3]))[1]) + @test length(components(exceptional_divisor(phi))) == 4 + @test length(components(exceptional_locus(phi))) == 4 end @testset "non-embedded desingularization of curves" begin @@ -27,10 +29,10 @@ end IS = IdealSheaf(W,I) X = subscheme(IS) U = first(affine_charts(X)) - phi = Oscar.desingularization(X) + phi = desingularization_only_blowups(X) H = IdealSheaf(U, ideal(OO(U), x + 3*y), covered_scheme=X) @test is_subset(total_transform(phi, H), strict_transform(phi, H)) - center(phi) + @test dim(center(Oscar.last_map(phi))) == 0 @test length(phi.ex_div) == 2 aff_charts = affine_charts(domain(phi)) sl1,_ = singular_locus(aff_charts[1]) @@ -39,6 +41,33 @@ end @test is_one(modulus(OO(sl1))) @test is_one(modulus(OO(sl2))) @test is_one(modulus(OO(sl3))) + @test length(components(exceptional_divisor(phi))) == 1 + @test length(components(exceptional_locus(phi))) == 1 + psi = desingularization(X) + @test length(morphisms(psi)) == 1 + @test morphism(psi,1) isa NormalizationMorphism +end + +@testset "non-embedded desingularization Lipman (dim=2)" begin + R,(x,y,z) = polynomial_ring(QQ,3) + I=ideal(R,[(x^2-y^5)*(x^2+y^2+z^4)]) + W = AffineScheme(R) + IS = IdealSheaf(W,I) + X = subscheme(IS) + U = first(affine_charts(X)) + phi = desingularization(X) + @test morphism(phi,1) isa NormalizationMorphism + @test morphism(phi,2) isa BlowupMorphism + @test morphism(phi,3) isa BlowupMorphism + aff_charts = affine_charts(domain(phi)) + sl1,_ = singular_locus(aff_charts[1]) + @test is_one(modulus(OO(sl1))) + sl5,_ = singular_locus(aff_charts[5]) + @test is_one(modulus(OO(sl5))) + sl6,_ = singular_locus(aff_charts[6]) + @test is_one(modulus(OO(sl6))) + @test length(components(exceptional_divisor(phi))) == 2 + @test_broken length(components(exceptional_locus(phi))) == 2 end @testset "order of an ideal" begin @@ -47,7 +76,7 @@ end I = ideal(R,[(x+y)^3+z^4]) IS = IdealSheaf(W,I) WC = scheme(IS) - IY = Oscar.max_order_locus(IS) + IY = locus_of_maximal_order(IS) decomp = Oscar.maximal_associated_points(IY) @test length(decomp) == 1 @test decomp[1] == IdealSheaf(W,ideal(R,[z,x+y]), covered_scheme=WC) @@ -55,8 +84,8 @@ end li = Oscar._delta_list(JS) @test length(li) == 3 @test li[1] == JS - @test Oscar.radical(li[2]) == IdealSheaf(W,ideal(R,[x,z]), covered_scheme=WC) - @test Oscar.radical(li[2]) == Oscar.radical(Oscar.locus_of_order_geq_b(JS,2)) - @test Oscar.radical(li[3]) == IdealSheaf(W,ideal(R,[x,y,z]),covered_scheme=WC) + @test radical(li[2]) == IdealSheaf(W,ideal(R,[x,z]), covered_scheme=WC) + @test radical(li[2]) == radical(Oscar.locus_of_order_geq_b(JS,2)) + @test radical(li[3]) == IdealSheaf(W,ideal(R,[x,y,z]),covered_scheme=WC) end diff --git a/test/AlgebraicGeometry/Schemes/transforms.jl b/test/AlgebraicGeometry/Schemes/transforms.jl index 334ac5ad57ed..3a6afbe78a28 100644 --- a/test/AlgebraicGeometry/Schemes/transforms.jl +++ b/test/AlgebraicGeometry/Schemes/transforms.jl @@ -13,8 +13,8 @@ IE = ideal_sheaf(E) W2 = domain(bl) - Isheaf = IdealSheaf(W,I,covered_scheme=codomain(bl)) - C2sheaf = IdealSheaf(W,C2,covered_scheme=codomain(bl)) + Isheaf = IdealSheaf(W, I; covered_scheme=codomain(bl)) + C2sheaf = IdealSheaf(W, C2; covered_scheme=codomain(bl)) Istrict = strict_transform(bl,Isheaf) @test is_smooth(subscheme(Istrict)) diff --git a/test/Combinatorics/EnumerativeCombinatorics/partitions.jl b/test/Combinatorics/EnumerativeCombinatorics/partitions.jl index 19b586fef162..ba4aae556c5a 100644 --- a/test/Combinatorics/EnumerativeCombinatorics/partitions.jl +++ b/test/Combinatorics/EnumerativeCombinatorics/partitions.jl @@ -3,37 +3,40 @@ ############################################################################ # Partition constructor ############################################################################ - @test partition(Int8,2,2,1) == partition(Int8[2,2,1]) - @test typeof(partition(Int8,2,2,1)) == typeof(partition(Int8[2,2,1])) - @test partition(Int8,1) == partition(Int8[1]) - @test typeof(partition(Int8,1)) == typeof(partition(Int8[1])) - @test partition(Int8,) == partition(Int8[]) - @test typeof(partition(Int8,)) == typeof(partition(Int8[])) - - @test partition(2,2,1) == partition([2,2,1]) - @test partition(1) == partition([1]) - @test partition() == partition([]) - @test partition(Int8) == partition(Int8[]) + @testset "Partition constructor" begin + @test partition(Int8,2,2,1) == partition(Int8[2,2,1]) + @test typeof(partition(Int8,2,2,1)) == typeof(partition(Int8[2,2,1])) + @test partition(Int8,1) == partition(Int8[1]) + @test typeof(partition(Int8,1)) == typeof(partition(Int8[1])) + @test partition(Int8,) == partition(Int8[]) + @test typeof(partition(Int8,)) == typeof(partition(Int8[])) + + @test partition(2,2,1) == partition([2,2,1]) + @test partition(1) == partition([1]) + @test partition() == partition([]) + @test partition(Int8) == partition(Int8[]) + end ############################################################################ # number_of_partitions(n) ############################################################################ + @testset "number_of_partitions(n)" begin + # From https://oeis.org/A000041 + @test [ number_of_partitions(i) for i in 0:49 ] == + ZZRingElem[ 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604, 6842, 8349, 10143, 12310, 14883, 17977, 21637, 26015, 31185, 37338, 44583, 53174, 63261, 75175, 89134, 105558, 124754, 147273, 173525 ] - # From https://oeis.org/A000041 - @test [ number_of_partitions(i) for i in 0:49 ] == - ZZRingElem[ 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604, 6842, 8349, 10143, 12310, 14883, 17977, 21637, 26015, 31185, 37338, 44583, 53174, 63261, 75175, 89134, 105558, 124754, 147273, 173525 ] + # For some random large numbers, checked with Sage + # Partitions(991).cardinality() + @test number_of_partitions(991) == ZZ(16839773100833956878604913215477) - # For some random large numbers, checked with Sage - # Partitions(991).cardinality() - @test number_of_partitions(991) == ZZ(16839773100833956878604913215477) - - @test number_of_partitions(-1) == ZZ(0) + @test number_of_partitions(-1) == ZZ(0) + end ############################################################################ # partitions(n) ############################################################################ - for n = 0:20 + @testset "partitions($n)" for n in 0:10 P = collect(partitions(n)) # Check that the number of partitions is correct @@ -52,118 +55,109 @@ ############################################################################ # number_of_partitions(n,k) ############################################################################ - @test number_of_partitions(0,0) == 1 - @test number_of_partitions(1,0) == 0 - @test number_of_partitions(1,1) == 1 - @test number_of_partitions(0,1) == 0 - @test number_of_partitions(2,3) == 0 - - # From https://oeis.org/A008284 - @test [ number_of_partitions(n,k) for n in 1:14 for k in 1:n ] == - ZZRingElem[ 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 3, 3, 2, 1, 1, 1, 3, 4, 3, 2, 1, 1, 1, 4, 5, 5, 3, 2, 1, 1, 1, 4, 7, 6, 5, 3, 2, 1, 1, 1, 5, 8, 9, 7, 5, 3, 2, 1, 1, 1, 5, 10, 11, 10, 7, 5, 3, 2, 1, 1, 1, 6, 12, 15, 13, 11, 7, 5, 3, 2, 1, 1, 1, 6, 14, 18, 18, 14, 11, 7, 5, 3, 2, 1, 1, 1, 7, 16, 23, 23, 20, 15, 11, 7, 5, 3, 2, 1, 1 ] - - # For some random large numbers, checked with Sage - # Partitions(1991,length=170).cardinality() - @test number_of_partitions(1991,170) == ZZ(22381599503916828837298114953756766080813312) - @test number_of_partitions(1991,1000) == ZZ(16839773100833956878604913215477) - @test number_of_partitions(1991,670) == ZZ(3329965216307826492368402165868892548) - @test number_of_partitions(1991,1991) == ZZ(1) - @test number_of_partitions(1991,1) == ZZ(1) - - # From Knuth (2011), p. 25. - @test sum([number_of_partitions(30, i) for i in 0:10]) == 3590 - - @test number_of_partitions(-1, 0) == ZZ(0) - @test number_of_partitions(0, -1) == ZZ(0) + @testset "number_of_partitions(n,k)" begin + @test number_of_partitions(0,0) == 1 + @test number_of_partitions(1,0) == 0 + @test number_of_partitions(1,1) == 1 + @test number_of_partitions(0,1) == 0 + @test number_of_partitions(2,3) == 0 + + # From https://oeis.org/A008284 + @test [ number_of_partitions(n,k) for n in 1:14 for k in 1:n ] == + ZZRingElem[ 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 3, 3, 2, 1, 1, 1, 3, 4, 3, 2, 1, 1, 1, 4, 5, 5, 3, 2, 1, 1, 1, 4, 7, 6, 5, 3, 2, 1, 1, 1, 5, 8, 9, 7, 5, 3, 2, 1, 1, 1, 5, 10, 11, 10, 7, 5, 3, 2, 1, 1, 1, 6, 12, 15, 13, 11, 7, 5, 3, 2, 1, 1, 1, 6, 14, 18, 18, 14, 11, 7, 5, 3, 2, 1, 1, 1, 7, 16, 23, 23, 20, 15, 11, 7, 5, 3, 2, 1, 1 ] + + # For some random large numbers, checked with Sage + # Partitions(1991,length=170).cardinality() + @test number_of_partitions(1991,170) == ZZ(22381599503916828837298114953756766080813312) + @test number_of_partitions(1991,1000) == ZZ(16839773100833956878604913215477) + @test number_of_partitions(1991,670) == ZZ(3329965216307826492368402165868892548) + @test number_of_partitions(1991,1991) == ZZ(1) + @test number_of_partitions(1991,1) == ZZ(1) + + # From Knuth (2011), p. 25. + @test sum([number_of_partitions(30, i) for i in 0:10]) == 3590 + + @test number_of_partitions(-1, 0) == ZZ(0) + @test number_of_partitions(0, -1) == ZZ(0) + end ############################################################################ # partitions(n,k) ############################################################################ - for n in 0:20 - for k = 0:n+1 - P = collect(partitions(n,k)) + @testset "partitions($n,$k)" for n in 0:10, k in 0:n+1 + P = collect(partitions(n,k)) - # Create the same by filtering all partitions - Q = collect(partitions(n)) - filter!( Q->length(Q) == k, Q) + # Create the same by filtering all partitions + Q = collect(partitions(n)) + filter!( Q->length(Q) == k, Q) - # Check that P and Q coincide (up to reordering) - @test length(P) == length(Q) - @test Set(P) == Set(Q) + # Check that P and Q coincide (up to reordering) + @test length(P) == length(Q) + @test Set(P) == Set(Q) - # Compare length with number_of_partitions(n,k) - @test length(P) == number_of_partitions(n,k) - end + # Compare length with number_of_partitions(n,k) + @test length(P) == number_of_partitions(n,k) end ############################################################################ - # partitions(n,k,l1,l2) + # partitions(n,k,lb,ub) ############################################################################ - for n in 0:20 - for k = 0:n+1 - for l1 = 0:n - for l2 = l1:n - P = collect(partitions(n,k,l1,l2)) + @testset "partitions($n,$k,lb,ub)" for n in 0:10, k in 0:n+1 + @testset "partitions($n,$k,$lb,$ub)" for lb in 0:n, ub in lb:n + P = collect(partitions(n,k,lb,ub)) - # Create the same by filtering all partitions - Q = collect(partitions(n,k)) - filter!( Q->all(>=(l1),Q), Q) - filter!( Q->all(<=(l2),Q), Q) + # Create the same by filtering all partitions + Q = collect(partitions(n,k)) + filter!( Q->all(>=(lb),Q), Q) + filter!( Q->all(<=(ub),Q), Q) - # Check that P and Q coincide (up to reordering) - @test length(P) == length(Q) - @test Set(P) == Set(Q) - end - end + # Check that P and Q coincide (up to reordering) + @test length(P) == length(Q) + @test Set(P) == Set(Q) end end ############################################################################ - # partitions(n,k,l1,l2; only_distinct_parts=true) + # partitions(n,k,lb,ub; only_distinct_parts=true) ############################################################################ - for n in 0:20 - for k = 0:n+1 - for l1 = 0:n - for l2 = l1:n - P = collect(partitions(n, k, l1, l2; only_distinct_parts=true)) + @testset "partitions($n,$k,lb,ub; only_distinct_parts=true)" for n in 0:10, k in 0:n+1 + @testset "partitions($n,$k,$lb,$ub; only_distinct_parts=true)" for lb in 0:n, ub in lb:n + P = collect(partitions(n, k, lb, ub; only_distinct_parts=true)) - # Create the same by filtering all partitions - Q = collect(partitions(n, k, l1, l2)) - filter!( Q->Q==unique(Q), Q ) + # Create the same by filtering all partitions + Q = collect(partitions(n, k, lb, ub)) + filter!( Q->Q==unique(Q), Q ) - # Check that P and Q coincide (up to reordering) - @test length(P) == length(Q) - @test Set(P) == Set(Q) - end - end + # Check that P and Q coincide (up to reordering) + @test length(P) == length(Q) + @test Set(P) == Set(Q) end end ############################################################################ - # partitions(m,n,v,mu) + # partitions(n,k,v,mu) ############################################################################ + @testset "partitions(n,k,v,mu)" begin + # Check argument errors + @test_throws ArgumentError partitions(5,2, [1,2], [1,2,3]) + @test_throws ArgumentError partitions(-1,2, [1,2,3], [1,2,3]) + @test_throws ArgumentError partitions(5,0,[1,2,3], [1,2]) + @test_throws ArgumentError partitions(6,3,[3,2,1],[1,2,3]) #req v strictly increasing + @test_throws ArgumentError partitions(6,3,[3,2,1],[0,2,3]) #mu > 0 + @test_throws ArgumentError partitions(6,3,[0,2,1],[1,2,3]) #v > 0 - # Check argument errors - @test_throws ArgumentError partitions(5,2, [1,2], [1,2,3]) - @test_throws ArgumentError partitions(-1,2, [1,2,3], [1,2,3]) - @test_throws ArgumentError partitions(5,0,[1,2,3], [1,2]) - @test_throws ArgumentError partitions(6,3,[3,2,1],[1,2,3]) #req v strictly increasing - @test_throws ArgumentError partitions(6,3,[3,2,1],[0,2,3]) #mu > 0 - @test_throws ArgumentError partitions(6,3,[0,2,1],[1,2,3]) #v > 0 - - # Issues from https://github.com/oscar-system/Oscar.jl/issues/2043 - @test length(collect(partitions(17, 3, [1, 4], [1,4]))) == 0 - @test collect(partitions(17, 5, [1, 4], [1, 4])) == [ partition(4, 4, 4, 4, 1) ] - @test length(collect(partitions(17, 6, [1, 2], [1, 7]))) == 0 - @test length(collect(partitions(20, 5, [1, 2, 3], [1, 3, 6]))) == 0 + # Issues from https://github.com/oscar-system/Oscar.jl/issues/2043 + @test length(collect(partitions(17, 3, [1, 4], [1,4]))) == 0 + @test collect(partitions(17, 5, [1, 4], [1, 4])) == [ partition(4, 4, 4, 4, 1) ] + @test length(collect(partitions(17, 6, [1, 2], [1, 7]))) == 0 + @test length(collect(partitions(20, 5, [1, 2, 3], [1, 3, 6]))) == 0 - # Issues UT found - @test length(collect(partitions(1, 1, [1], [1]))) == 1 - @test length(collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 1 + # Issues UT found + @test length(collect(partitions(1, 1, [1], [1]))) == 1 + @test length(collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 1 - # Special cases - for n in 0:20 - for k in 0:n + 1 + # Special cases + @testset "Special cases n=$n, k=$k" for n in 0:20, k in 0:n+1 P = collect(partitions(n, k, [i for i in 1:n], [n for i in 1:n])) Q = collect(partitions(n, k)) @test length(P) == length(Q) @@ -174,50 +168,53 @@ @test length(P) == length(Q) @test Set(P) == Set(Q) end - end - # From https://www.maa.org/frank-morgans-math-chat-293-ways-to-make-change-for-a-dollar - @test length(collect(partitions(100, [1, 5, 10, 25, 50]))) == 292 - @test length(collect(partitions(200, [1, 5, 10, 25, 50, 100]))) == 2728 + # From https://www.maa.org/frank-morgans-math-chat-293-ways-to-make-change-for-a-dollar + @test length(collect(partitions(100, [1, 5, 10, 25, 50]))) == 292 + @test length(collect(partitions(200, [1, 5, 10, 25, 50, 100]))) == 2728 - # From Knu11, Exercise 11 on page 408 - @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 6 - @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50]))) == 4562 + # From Knu11, Exercise 11 on page 408 + @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 6 + @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50]))) == 4562 - # From https://oeis.org/A000008 - @test [ length(collect(partitions(n, [1,2,5,10]))) for n in 0:60 ] == - [ 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 16, 19, 22, 25, 28, 31, 34, - 40, 43, 49, 52, 58, 64, 70, 76, 82, 88, 98, 104, 114, 120, 130, 140, - 150, 160, 170, 180, 195, 205, 220, 230, 245, 260, 275, 290, 305, 320, - 341, 356, 377, 392, 413, 434, 455, 476, 497, 518, 546 ] + # From https://oeis.org/A000008 + @test [ length(collect(partitions(n, [1,2,5,10]))) for n in 0:60 ] == + [ 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 16, 19, 22, 25, 28, 31, 34, + 40, 43, 49, 52, 58, 64, 70, 76, 82, 88, 98, 104, 114, 120, 130, 140, + 150, 160, 170, 180, 195, 205, 220, 230, 245, 260, 275, 290, 305, 320, + 341, 356, 377, 392, 413, 434, 455, 476, 497, 518, 546 ] + end ############################################################################ # dominates(P) ############################################################################ - @test dominates(partition([4,2]), partition([3,2,1])) == true - @test dominates(partition([4,1,1]), partition([3,3])) == false - @test dominates(partition([3,3]), partition([4,1,1])) == false - @test dominates(partition([5]), partition([2,2,2])) == false + @testset "dominates(P)" begin + @test dominates(partition([4,2]), partition([3,2,1])) == true + @test dominates(partition([4,1,1]), partition([3,3])) == false + @test dominates(partition([3,3]), partition([4,1,1])) == false + @test dominates(partition([5]), partition([2,2,2])) == false - # Ful97, page 26 - @test dominates( partition(3,1), partition(2,2) ) == true - @test dominates( partition(4,1), partition(3,3) ) == false + # Ful97, page 26 + @test dominates( partition(3,1), partition(2,2) ) == true + @test dominates( partition(4,1), partition(3,3) ) == false + end ############################################################################ # conjugate(P) ############################################################################ + @testset "conjugate(P)" begin + # From Knu11, page 394 + @test conjugate(partition(8,8,8,7,2,1,1)) == partition([7, 5, 4, 4, 4, 4, 4, 3]) - # From Knu11, page 394 - @test conjugate(partition(8,8,8,7,2,1,1)) == partition([7, 5, 4, 4, 4, 4, 4, 3]) - - # From Ful97, page 2 - @test conjugate(partition([6,4,3,1])) == partition([4, 3, 3, 2, 1, 1]) + # From Ful97, page 2 + @test conjugate(partition([6,4,3,1])) == partition([4, 3, 3, 2, 1, 1]) - @test conjugate(partition([])) == partition([]) + @test conjugate(partition([])) == partition([]) - # Check if conjugate is an involution - for p in partitions(10) - @test conjugate(conjugate(p)) == p + # Check if conjugate is an involution + for p in partitions(10) + @test conjugate(conjugate(p)) == p + end end end diff --git a/test/GAP/iso_gap_oscar.jl b/test/GAP/iso_gap_oscar.jl index b303075a8ddd..df6d2da32eb1 100644 --- a/test/GAP/iso_gap_oscar.jl +++ b/test/GAP/iso_gap_oscar.jl @@ -92,7 +92,7 @@ end end @testset "field of rationals, ring of integers" begin - for (R, x, y) in [(GAP.Globals.Rationals, GAP.GapObj(2//3), 1), + for (R, x, y) in [(GAP.Globals.Rationals, GapObj(2//3), 1), (GAP.Globals.Integers, 2, 3), ] iso = Oscar.iso_gap_oscar(R) diff --git a/test/GAP/oscar_to_gap.jl b/test/GAP/oscar_to_gap.jl index 837a03318da8..45c767c83433 100644 --- a/test/GAP/oscar_to_gap.jl +++ b/test/GAP/oscar_to_gap.jl @@ -124,9 +124,9 @@ end # create sort duplicate free list (this is how GAP represents sets) l = sort(unique(coll)) - x = GAP.GapObj(s) - @test x == GAP.GapObj(l) + x = GapObj(s) + @test x == GapObj(l) - x = GAP.GapObj(s; recursive=true) - @test x == GAP.GapObj(l; recursive=true) + x = GapObj(s; recursive=true) + @test x == GapObj(l; recursive=true) end diff --git a/test/Groups/constructors.jl b/test/Groups/constructors.jl index aceed73af4aa..210328d5c58b 100644 --- a/test/Groups/constructors.jl +++ b/test/Groups/constructors.jl @@ -111,7 +111,8 @@ end @test is_cyclic(G) G1 = abelian_group(PermGroup, [2, 3]) @test is_isomorphic(G, G1) - G = abelian_group(PcGroup, [ZZ(2)^70]) +# G = abelian_group(PcGroup, [ZZ(2)^70]) + G = abelian_group(SubPcGroup, [ZZ(2)^70]) # FIXME: a function `free_abelian_group` is not defined in GAPGroups, since it is already defined in Hecke #= diff --git a/test/Groups/group_characters.jl b/test/Groups/group_characters.jl index e9ae2263853a..1e8b6fc13e20 100644 --- a/test/Groups/group_characters.jl +++ b/test/Groups/group_characters.jl @@ -753,7 +753,7 @@ end @testset "access fields in character tables" begin # table without group t = character_table("A5") - @test Oscar.GAPTable(t) === t.GAPTable + @test GapObj(t) === t.GAPTable @test characteristic(t) == t.characteristic @test_throws UndefRefError t.group @test_throws UndefRefError t.isomorphism @@ -761,7 +761,7 @@ end # table with `GAPGroup` group g = symmetric_group(4) t = character_table(g) - @test Oscar.GAPTable(t) === t.GAPTable + @test GapObj(t) === t.GAPTable @test characteristic(t) == t.characteristic @test group(t) === t.group === g @test Oscar.isomorphism_to_GAP_group(t) === t.isomorphism @@ -769,7 +769,7 @@ end # table with `FinGenAbGroup` group g = abelian_group([2, 4]) t = character_table(g) - @test Oscar.GAPTable(t) === t.GAPTable + @test GapObj(t) === t.GAPTable @test characteristic(t) == t.characteristic @test group(t) === t.group === g @test Oscar.isomorphism_to_GAP_group(t) === t.isomorphism diff --git a/test/Groups/homomorphisms.jl b/test/Groups/homomorphisms.jl index a8963636ab53..74e55fed772c 100644 --- a/test/Groups/homomorphisms.jl +++ b/test/Groups/homomorphisms.jl @@ -139,7 +139,7 @@ end @testset "map_word for f.p. groups" begin # Create a free group in GAP in syllable words family, # in order to make the tests. - GAP.Globals.PushOptions(GAP.GapObj(Dict(:FreeGroupFamilyType => GAP.GapObj("syllable")))) + GAP.Globals.PushOptions(GapObj(Dict(:FreeGroupFamilyType => GapObj("syllable")))) FS = free_group(2) # syllable representation GAP.Globals.PopOptions() FL = free_group(2) # letter representation @@ -237,7 +237,8 @@ end end @testset "Finite abelian GAPGroup to FinGenAbGroup" begin - for invs in [[1], [2, 3, 4], [6, 8, 9, 15]], T in [PermGroup, PcGroup, FPGroup] +# for invs in [[1], [2, 3, 4], [6, 8, 9, 15]], T in [PermGroup, PcGroup, FPGroup] + for invs in [[1], [2, 3, 4], [6, 8, 9, 15]], T in [PermGroup, SubPcGroup, FPGroup] G = abelian_group(T, invs) iso = @inferred isomorphism(FinGenAbGroup, G) A = codomain(iso) @@ -256,7 +257,8 @@ end @testset for Agens in [[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, PcGroup, PermGroup] + for T in [FPGroup, SubPcGroup, PermGroup] iso = @inferred isomorphism(T, A) for x in gens(A), y in gens(A) z = x+y @@ -404,6 +406,10 @@ end @test is_injective(f) @test is_surjective(f) + @test_throws ArgumentError isomorphism(PcGroup, S, on_gens = true) + f = isomorphism(PcGroup, G, on_gens = true) + @test [f(x) for x in gens(G)] == gens(codomain(f)) + f = @inferred isomorphism(PcGroup, G) @test codomain(f) isa PcGroup @test domain(f) == G @@ -580,7 +586,9 @@ function test_kernel(G,H,f) K,i = kernel(f) Im = image(f)[1] - @test preimage(f,H)==(G,id_hom(G)) +#TODO: activate these tests as soon as they pass again; +# the point is that comparing the embeddings is done via `===` +# @test preimage(f,H)==(G,id_hom(G)) @test preimage(f,sub(H,[one(H)])[1])==(K,i) z=rand(Im) @test has_preimage_with_preimage(f,z)[1] @@ -696,8 +704,7 @@ end # Create an Oscar group from a group of automorphisms in GAP. G = alternating_group(6) A = automorphism_group(G) - fun = Oscar._get_type(A.X) - B = fun(A.X) + B = Oscar._oscar_group(GapObj(A)) @test B == A @test B !== A @test B.X === A.X diff --git a/test/Groups/libraries.jl b/test/Groups/libraries.jl index 62d388c26c86..dc2bf26cef71 100644 --- a/test/Groups/libraries.jl +++ b/test/Groups/libraries.jl @@ -139,7 +139,10 @@ end @testset "Small groups" begin L = all_small_groups(8) - LG = [abelian_group(PcGroup,[2,4]), abelian_group(PcGroup,[2,2,2]), cyclic_group(8), quaternion_group(8), dihedral_group(8)] +#TODO: As soon as `abelian_group(PcGroup,[2,4])` is supported, +# add it as an example. +# LG = [abelian_group(PcGroup,[2,4]), abelian_group(PcGroup,[2,2,2]), cyclic_group(8), quaternion_group(8), dihedral_group(8)] + LG = [abelian_group(SubPcGroup,[2,4]), abelian_group(PcGroup,[2,2,2]), cyclic_group(8), quaternion_group(8), dihedral_group(8)] @test length(L)==5 @testset for G in LG arr = [i for i in 1:5 if is_isomorphic(L[i],G)] diff --git a/test/Groups/pcgroup.jl b/test/Groups/pcgroup.jl index 081bd5ed1d2f..8fe8515872b7 100644 --- a/test/Groups/pcgroup.jl +++ b/test/Groups/pcgroup.jl @@ -1,3 +1,33 @@ +@testset "constructors for polycyclic groups" begin + # Given a GAP group, `PcGroup` just wraps the given full pc group + # or signals an error. + G = small_group(12, 2) + PCG = PcGroup(GapObj(G)) + @test PCG isa PcGroup + @test GapObj(PCG) === GapObj(G) + + G = derived_subgroup(G)[1] + @test_throws AssertionError PcGroup(GapObj(G)) + + G = symmetric_group(4) + @test_throws AssertionError PcGroup(GapObj(G)) + + # Given a GAP group `G`, `pc_group` may create a new full pc group + # if `G` is a proper subgroup or if its generators are not the defining ones. + G = small_group(12, 2) + PCG = pc_group(GapObj(G)) + @test PCG isa PcGroup + @test GapObj(PCG) === GapObj(G) + + G = derived_subgroup(G)[1] + PCG = pc_group(GapObj(G)) + @test PCG isa PcGroup + @test GapObj(PCG) !== GapObj(G) + + G = symmetric_group(4) + @test_throws ArgumentError pc_group(GapObj(G)) isa PcGroup +end + @testset "create polycyclic groups from collectors" begin # finite polycyclic groups @@ -42,5 +72,5 @@ x = GapObj(one(gg)) cgg = GAP.getbangproperty(x, :collector) @test GAP.Globals.IsMutable(cgg) - @test ! GAP.Globals.IsIdenticalObj(cgg, c.X) + @test cgg !== c.X end diff --git a/test/Groups/quotients.jl b/test/Groups/quotients.jl index 143cc4d4373a..befc9c112334 100644 --- a/test/Groups/quotients.jl +++ b/test/Groups/quotients.jl @@ -1,5 +1,6 @@ @testset "quo for trivial kernel" begin - @testset for G in [symmetric_group(4), special_linear_group(2, 3), special_linear_group(2, 4), free_group(1), abelian_group(PcGroup, [2, 3, 4])] +# @testset for G in [symmetric_group(4), special_linear_group(2, 3), special_linear_group(2, 4), free_group(1), abelian_group(PcGroup, [2, 3, 4])] + @testset for G in [symmetric_group(4), special_linear_group(2, 3), special_linear_group(2, 4), free_group(1), abelian_group(SubPcGroup, [2, 3, 4])] subgens = elem_type(G)[] F, epi = quo(G, subgens) if is_finite(G) @@ -52,14 +53,21 @@ end # otherwise `PcGroup` if finite, `FPGroup` if not # - `maximal_abelian_quotient` with prescribed type: # return a group of this type if possible - for T in [ PermGroup, PcGroup ] +# for T in [ PermGroup, PcGroup ] +#TODO: support `abelian_group(PcGroup, [2, 3, 4])`, using GAP's PcpGroup + for T in [ PermGroup, SubPcGroup ] G = abelian_group(T, [2, 3, 4]) @test maximal_abelian_quotient(G)[1] isa T @test maximal_abelian_quotient(PermGroup, G)[1] isa PermGroup end T = FPGroup G = abelian_group(T, [2, 3, 4]) - @test maximal_abelian_quotient(G)[1] isa PcGroup + # Note that GAP chooses a representation for the max. abelian quotient, + # and this group gets wrapped into an Oscar group. + # Here GAP chooses a pc group, but with noncanonical generators, + # Hence we get a `SubPcGroup` not a `PcGroup`. + # (This is not really satisfactory.) + @test maximal_abelian_quotient(G)[1] isa SubPcGroup @test maximal_abelian_quotient(PermGroup, G)[1] isa PermGroup @test maximal_abelian_quotient(FinGenAbGroup, G)[1] isa FinGenAbGroup G = symmetric_group(4) diff --git a/test/Groups/subgroups_and_cosets.jl b/test/Groups/subgroups_and_cosets.jl index 9bd878678c68..807006b737be 100644 --- a/test/Groups/subgroups_and_cosets.jl +++ b/test/Groups/subgroups_and_cosets.jl @@ -165,8 +165,8 @@ end @test_throws ArgumentError right_transversal(H, G) @test_throws ArgumentError left_transversal(H, G) + G = symmetric_group(5) @testset "set comparison for cosets in PermGroup" begin - G=symmetric_group(5) x = G(cperm([1,2,3])) y = G(cperm([1,4,5])) z = G(cperm([1,2],[3,4])) @@ -308,6 +308,16 @@ end @test schur_multiplier(PcGroup, symmetric_group(4)) isa PcGroup end +@testset "Schur cover" begin + @test order(schur_cover(symmetric_group(4))[1]) == 48 + @test order(schur_cover(alternating_group(5))[1]) == 120 + @test order(schur_cover(dihedral_group(12))[1]) == 24 + + @test schur_cover(symmetric_group(4))[1] isa FPGroup + @test schur_cover(PcGroup, symmetric_group(4))[1] isa PcGroup + @test schur_cover(PermGroup, alternating_group(5))[1] isa PermGroup +end + @testset "Sylow and Hall subgroups" begin G = symmetric_group(4) @@ -360,17 +370,20 @@ end # solvable group G = symmetric_group(4) N = pcore(G, 2)[1] - @test length(complement_classes(G, N)) == 1 + C = @inferred complement_classes(G, N) + @test length(C) == 1 # nonsolvable factor group G = special_linear_group(2, 5) N = center(G)[1] - @test length(complement_classes(G, N)) == 0 + C = @inferred complement_classes(G, N) + @test length(C) == 0 # nonsolvable normal subgroup G = symmetric_group(6) N = derived_subgroup(G)[1] - @test length(complement_classes(G, N)) == 2 + C = @inferred complement_classes(G, N) + @test length(C) == 2 # both normal subgroup and factor group nonsolvable: # check that GAP throws an error @@ -380,6 +393,18 @@ end W = wreath_product(G, G) N = kernel(canonical_projection(W))[1] @test_throws ErrorException complement_classes(W, N) + + # pc group, with complements + G = PcGroup(symmetric_group(4)) + N = pcore(G, 2)[1] + C = @inferred complement_classes(G, N) + @test length(C) == 1 + + # pc group, without complements + G = dihedral_group(8) + N = center(G)[1] + C = @inferred complement_classes(G, N) + @test length(C) == 0 end @testset "Some specific subgroups" begin diff --git a/test/Modules/ModulesGraded.jl b/test/Modules/ModulesGraded.jl index a918dbe09291..fa11bbea94fc 100644 --- a/test/Modules/ModulesGraded.jl +++ b/test/Modules/ModulesGraded.jl @@ -339,6 +339,12 @@ end @test codomain(psi) == N @test is_bijective(phi) @test is_bijective(psi) + I = ideal(Rg, [x^2, x*y, y]) + A, _ = quo(Rg, I) + M = quotient_ring_as_module(A) + mp = presentation(M, minimal = true) + @test rank(mp[0]) == 1 + @test rank(mp[1]) == 2 end diff --git a/test/Modules/UngradedModules.jl b/test/Modules/UngradedModules.jl index 1fdd8df0d03a..48dbce35a93f 100644 --- a/test/Modules/UngradedModules.jl +++ b/test/Modules/UngradedModules.jl @@ -340,6 +340,10 @@ end @test is_canonically_isomorphic(present_as_cokernel(Oscar._old_simplify(E2)[1]), M_coker) @test is_canonically_isomorphic(E3, M_coker) @test iszero(E4) + + # Test that tor, ext don't crash outside of "sensible" arguments + T3 = tor(Q, M, 20) + E5 = ext(Q, M, 20) end @testset "Gröbner bases" begin diff --git a/test/NumberTheory/galthy.jl b/test/NumberTheory/galthy.jl index 3910a12d8ae4..b797bd9d933a 100644 --- a/test/NumberTheory/galthy.jl +++ b/test/NumberTheory/galthy.jl @@ -77,4 +77,10 @@ sample_cycle_structures(G::PermGroup) = Set(cycle_structure(rand_pseudo(G)) for @test all(G -> !an_sn_by_shape(sample_cycle_structures(G),n) || naive_is_giant(G), grps[n] ) end + let # Ehrhart polynomial problems + Qx, x = QQ["x"] + f = 4//45*x^6 + 4//15*x^5 + 14//9*x^4 + 8//3*x^3 + 196//45*x^2 + 46//15*x + 1 + G, = galois_group(f) + @test small_group_identification(G) == (4, 2) + end end diff --git a/test/Rings/AlgClosureFp.jl b/test/Rings/AlgClosureFp.jl index 91931e8fd0c0..2f726d588952 100644 --- a/test/Rings/AlgClosureFp.jl +++ b/test/Rings/AlgClosureFp.jl @@ -55,9 +55,9 @@ end @testset "Printing for $F" for F in [GF(3, 1), Nemo.Native.GF(3, 1)] K = algebraic_closure(F) if F isa FqField - @test sprint(show, "text/plain", K) == "Algebraic Closure of prime field of characteristic 3" + @test sprint(show, "text/plain", K) == "Algebraic closure of prime field of characteristic 3" elseif F isa fqPolyRepField - @test sprint(show, "text/plain", K) == "Algebraic Closure of finite field of degree 1 over GF(3)" + @test sprint(show, "text/plain", K) == "Algebraic closure of finite field of degree 1 over GF(3)" else error("unreachable") end diff --git a/test/Rings/mpoly-graded.jl b/test/Rings/mpoly-graded.jl index fb4e1b7ac88e..af69a35e6189 100644 --- a/test/Rings/mpoly-graded.jl +++ b/test/Rings/mpoly-graded.jl @@ -255,6 +255,7 @@ end M, h = vector_space(base_ring(R), elem_type(R)[], target = R) t = h(zero(M)) + @test dim(M) == 0 @test iszero(t) @test parent(t) == R @@ -262,6 +263,7 @@ end # of the various vector spaces (this used to not work correctly) polys = [x, y, (x+y+z)^3, 2*x - 5*y]; V, VtoPoly = vector_space(QQ, polys) + @test dim(V) == 3 @test all(f -> VtoPoly(preimage(VtoPoly, f)) == f, polys) @test_throws ErrorException preimage(VtoPoly, z) diff --git a/test/Rings/mpoly.jl b/test/Rings/mpoly.jl index b20fb160b19b..b6c695cf5527 100644 --- a/test/Rings/mpoly.jl +++ b/test/Rings/mpoly.jl @@ -300,8 +300,8 @@ end end @testset "Grassmann Plücker Relations" begin - R, x = polynomial_ring(residue_ring(ZZ, 7)[1], "x" => (1:2, 1:3)) - test_ideal = ideal([x[1, 2]*x[2, 2] + 6*x[2, 1]*x[1, 3] + x[1, 1]*x[2, 3]]) + R, x = graded_polynomial_ring(residue_ring(ZZ, 7)[1], "x" => (1:2, 1:3)) + test_ideal =ideal([x[1, 2]*x[2, 2] + 6*x[2, 1]*x[1, 3] + x[1, 1]*x[2, 3]]) @test grassmann_pluecker_ideal(R, 2, 4) == test_ideal end @@ -555,7 +555,22 @@ end @testset "default ordering" begin R, _ = QQ["x", "y", "z"] S, _ = grade(R, [2, 1, 2]) - # test type stability - @inferred default_ordering(R) - @inferred default_ordering(S) + for T in [R, S] + x, y, z = gens(T) + f = x + y^2 + I = ideal(T, [y^2 - z, x - z]) + old_default = @inferred default_ordering(T) + f = with_ordering(T, invlex(T)) do + normal_form(f, I) + end + @test f == normal_form(f, I, ordering = invlex(T)) + @test default_ordering(T) == old_default + + # Make sure the ordering is reset in case of an error + # The `@test_throws` is just here to catch the error + @test_throws ErrorException with_ordering(T, invlex(T)) do + error() + end + @test default_ordering(T) == old_default + end end diff --git a/test/Serialization/Fields.jl b/test/Serialization/Fields.jl index 0d11ec0d275c..5add4cb67519 100644 --- a/test/Serialization/Fields.jl +++ b/test/Serialization/Fields.jl @@ -33,5 +33,11 @@ @test loaded == v end end + + @testset "QQBar" begin + test_save_load_roundtrip(path, QQBarField()) do loaded + @test loaded isa QQBarField + end + end end end diff --git a/test/Serialization/GAP.jl b/test/Serialization/GAP.jl index e132f735fc6e..a44e5e03a162 100644 --- a/test/Serialization/GAP.jl +++ b/test/Serialization/GAP.jl @@ -31,7 +31,7 @@ # subgroup of full free group Fgens = GAP.Globals.GeneratorsOfGroup(F) - U = GAP.Globals.Subgroup(F, GAP.GapObj([Fgens[1]])) + U = GAP.Globals.Subgroup(F, GapObj([Fgens[1]])) test_save_load_roundtrip(path, U) do loaded @test U == loaded end @@ -54,7 +54,7 @@ @test GAP.Globals.GeneratorsOfGroup(loadedU)[1] in loadedF # full group and subgroup together in the same Julia session - V = GAP.Globals.Subgroup(F, GAP.GapObj([Fgens[2]])) + V = GAP.Globals.Subgroup(F, GapObj([Fgens[2]])) v = (F, U, V) test_save_load_roundtrip(path, v) do loaded @test v == loaded @@ -74,14 +74,14 @@ # full f.p. group F = GAP.Globals.FreeGroup(2) Fgens = GAP.Globals.GeneratorsOfGroup(F) - G = F/GAP.GapObj([x^2 for x in Fgens]) + G = F/GapObj([x^2 for x in Fgens]) test_save_load_roundtrip(path, G) do loaded @test G == loaded end # subgroup of full f.p. group Ggens = GAP.Globals.GeneratorsOfGroup(G) - U = GAP.Globals.Subgroup(G, GAP.GapObj([Ggens[1]])) + U = GAP.Globals.Subgroup(G, GapObj([Ggens[1]])) test_save_load_roundtrip(path, U) do loaded @test U == loaded end @@ -104,7 +104,7 @@ @test GAP.Globals.GeneratorsOfGroup(loadedU)[1] in loadedG # full group and subgroup together in the same Julia session - V = GAP.Globals.Subgroup(G, GAP.GapObj([Ggens[2]])) + V = GAP.Globals.Subgroup(G, GapObj([Ggens[2]])) v = (G, U, V) test_save_load_roundtrip(path, v) do loaded @test v == loaded diff --git a/test/Serialization/PolyhedralGeometry.jl b/test/Serialization/PolyhedralGeometry.jl index 0a8c36134ef6..e75f5bee8dcf 100644 --- a/test/Serialization/PolyhedralGeometry.jl +++ b/test/Serialization/PolyhedralGeometry.jl @@ -35,7 +35,15 @@ using Oscar: _integer_variables @test dim(square) == dim(loaded) @test square == loaded end - + + n2 = (QQBarField()(5))^(QQ(4//5)) + c = cube(QQBarField(), 3, -1, n2) + test_save_load_roundtrip(path, square) do loaded + @test n_vertices(square) == n_vertices(loaded) + @test dim(square) == dim(loaded) + @test square == loaded + end + d_hedron = dodecahedron() facets(d_hedron) vertices(d_hedron) @@ -97,7 +105,7 @@ using Oscar: _integer_variables @test feasible_region(LP) == feasible_region(loaded) end end - + @testset "MixedIntegerLinearProgram" begin P = cube(3) MILP = mixed_integer_linear_program( diff --git a/test/Serialization/PolynomialsSeries.jl b/test/Serialization/PolynomialsSeries.jl index 228c9d2a1add..9abbad3ae14f 100644 --- a/test/Serialization/PolynomialsSeries.jl +++ b/test/Serialization/PolynomialsSeries.jl @@ -29,6 +29,7 @@ cases = [ (Frac, 1 // x, x^2, "Fraction Field"), (T, T(1), T(3)^2, "Tropical Semiring"), (FF, FF(1), r, "Default Finite Field"), + (QQBarField(), sqrt(QQBarField()(-7)), QQBarField()(5)^(QQ(4//5)), "QQBar"), (P7, 7 + 3*7^2, 7^5, "Padic Field"), ] diff --git a/test/book/cornerstones/groups/actions.jlcon b/test/book/cornerstones/groups/actions.jlcon index 7746383e5ced..cca4f30a51b5 100644 --- a/test/book/cornerstones/groups/actions.jlcon +++ b/test/book/cornerstones/groups/actions.jlcon @@ -7,11 +7,11 @@ julia> G = dihedral_group(6) Pc group of order 6 julia> U = sub(G, [g for g in gens(G) if order(g) == 2])[1] -Pc group of order 2 +Sub-pc group of order 2 julia> r = right_cosets(G, U) Right cosets of - pc group of order 2 in + sub-pc group of order 2 in pc group of order 6 julia> acting_group(r) diff --git a/test/book/cornerstones/number-theory/galoismod.jlcon b/test/book/cornerstones/number-theory/galoismod.jlcon index 64c37466576c..7643ca5d472b 100644 --- a/test/book/cornerstones/number-theory/galoismod.jlcon +++ b/test/book/cornerstones/number-theory/galoismod.jlcon @@ -88,4 +88,4 @@ julia> fl = is_free(M) false julia> defining_polynomial(K) -x^8 + 105*x^6 + 3465*x^4 + 44100*x^2 + 176400 +x^8 + 735*x^6 + 11340*x^4 + 33075*x^2 + 11025 diff --git a/test/book/specialized/aga-boehm-hoffmann-markwig-traore/auxiliary_code/main.jl b/test/book/specialized/aga-boehm-hoffmann-markwig-traore/auxiliary_code/main.jl index 69c0cbe00270..5e45deaed50a 100644 --- a/test/book/specialized/aga-boehm-hoffmann-markwig-traore/auxiliary_code/main.jl +++ b/test/book/specialized/aga-boehm-hoffmann-markwig-traore/auxiliary_code/main.jl @@ -1,3 +1,3 @@ using Pkg -Pkg.add(url = "https://github.com/singular-gpispace/GromovWitten"; io=devnull) +Pkg.add(url = "https://github.com/singular-gpispace/GromovWitten", rev="d7af772"; io=devnull) using GromovWitten diff --git a/test/book/specialized/holt-ren-tropical-geometry/auxiliary_code/main.jl b/test/book/specialized/holt-ren-tropical-geometry/auxiliary_code/main.jl index f254305d82f7..fedc45d6f5c4 100644 --- a/test/book/specialized/holt-ren-tropical-geometry/auxiliary_code/main.jl +++ b/test/book/specialized/holt-ren-tropical-geometry/auxiliary_code/main.jl @@ -1,6 +1,7 @@ using Pkg -Pkg.add("MixedSubdivisions"; io=devnull) +Pkg.add(name="MixedSubdivisions", version=v"1.1"; io=devnull) import LibGit2 -LibGit2.clone("https://github.com/isaacholt100/generic_root_count", "generic_root_count") +repo = LibGit2.clone("https://github.com/isaacholt100/generic_root_count", "generic_root_count"); +LibGit2.checkout!(repo, "ac17f42d0897d72e378310826b1c47db4d65df36"); include(joinpath(@__DIR__,"generic_root_count","src","main.jl")); diff --git a/test/book/specialized/rose-sturmfels-telen-tropical-implicitization/auxiliary_code/main.jl b/test/book/specialized/rose-sturmfels-telen-tropical-implicitization/auxiliary_code/main.jl index d8901eae6161..920a4259db6d 100644 --- a/test/book/specialized/rose-sturmfels-telen-tropical-implicitization/auxiliary_code/main.jl +++ b/test/book/specialized/rose-sturmfels-telen-tropical-implicitization/auxiliary_code/main.jl @@ -1,3 +1,3 @@ using Pkg -Pkg.add(url = "https://github.com/kemalrose/TropicalImplicitization.jl"; io=devnull) +Pkg.add(url = "https://github.com/kemalrose/TropicalImplicitization.jl", rev="cd4836b"; io=devnull) using TropicalImplicitization diff --git a/test/book/test.jl b/test/book/test.jl index ab3d93086f27..26b9ab891411 100644 --- a/test/book/test.jl +++ b/test/book/test.jl @@ -215,13 +215,13 @@ isdefined(Main, :FakeTerminals) || include(joinpath(pkgdir(REPL),"test","FakeTer copy!(LOAD_PATH, custom_load_path) auxmain = joinpath(Oscar.oscardir, "test/book", chapter, "auxiliary_code", "main.jl") + # run from temp dir + temp = mktempdir() + cd(temp) if isfile(auxmain) # add overlay project for aux file - # and run it from temp dir - temp = mktempdir() Pkg.activate(temp; io=devnull) cp(auxmain,joinpath(temp, "main.jl")) - cd(temp) run_repl_string(mockrepl, """include("$(joinpath(temp,"main.jl"))")\n""") pushfirst!(LOAD_PATH, temp) Pkg.activate("$act_proj"; io=devnull)