Homological Algebra

Some OSCAR functions which are fundamental to homological algebra such as the kernel function for module homomorphisms and basic functions for handling chain and cochain complexes are discussed in the module section. Building on these functions, we here introduce further OSCAR functionality supporting computations in homological algebra.

Pruning Modules

prune_with_mapMethod
prune_with_map(M::ModuleFP)

If M is positively graded, return a module N and an isomorphism from N to M such that N is generated by a minimal number of elements.

If M is not (positively) graded, the function still aims at reducing the number of generators.

Examples

julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> Z = R(0)
0

julia> O = R(1)
1

julia> B = [Z Z Z O; w*y w*z-x*y x*z-y^2 Z];

julia> A = transpose(matrix(B))
[0         w*y]
[0   w*z - x*y]
[0   x*z - y^2]
[1           0]

julia> M = graded_cokernel(A)
Graded subquotient of submodule of R^2 generated by
1 -> e[1]
2 -> e[2]
by submodule of R^2 generated by
1 -> w*y*e[2]
2 -> (w*z - x*y)*e[2]
3 -> (x*z - y^2)*e[2]
4 -> e[1]

julia> N, phi = prune_with_map(M);

julia> N
Graded subquotient of submodule of R^1 generated by
1 -> e[1]
by submodule of R^1 generated by
1 -> (-x*z + y^2)*e[1]
2 -> (-w*z + x*y)*e[1]
3 -> w*y*e[1]

julia> phi(first(gens(N)))
e[2]
source

Presentations

presentationMethod
presentation(M::ModuleFP; minimal = false)

Return a (free) presentation of M.

Note

If minimal is true, and M is positively graded, a minimal presentation is returned. If minimal is true, and M is not (positively) graded, the function still aims at returning an ''improved'' presentation.

Examples

julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> Z = R(0)
0

julia> O = R(1)
1

julia> B = [Z Z Z O; w*y w*z-x*y x*z-y^2 Z];

julia> A = transpose(matrix(B))
[0         w*y]
[0   w*z - x*y]
[0   x*z - y^2]
[1           0]

julia> M = graded_cokernel(A)
Graded subquotient of submodule of R^2 generated by
1 -> e[1]
2 -> e[2]
by submodule of R^2 generated by
1 -> w*y*e[2]
2 -> (w*z - x*y)*e[2]
3 -> (x*z - y^2)*e[2]
4 -> e[1]

julia> PM1 = presentation(M)
0 <---- M <---- R^2 <---- R^4

julia> PM2 = presentation(M, minimal = true)
0 <---- M <---- R^1 <---- R^3
julia> Rg, (x, y, z) = graded_polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = graded_free_module(Rg, [1,2,2]);

julia> p = presentation(F)
0 <---- F <---- F <---- 0

julia> p[-2]
Graded free module Rg^0 of rank 0 over Rg

julia> p[-1]
Graded free module Rg^1([-1]) + Rg^2([-2]) of rank 3 over Rg

julia> p[0]
Graded free module Rg^1([-1]) + Rg^2([-2]) of rank 3 over Rg

julia> p[1]
Graded free module Rg^0 of rank 0 over Rg

julia> map(p,-1)
F -> 0
e[1] -> 0
e[2] -> 0
e[3] -> 0
Homogeneous module homomorphism

julia> map(p,0)
F -> F
e[1] -> e[1]
e[2] -> e[2]
e[3] -> e[3]
Homogeneous module homomorphism

julia> map(p,1)
0 -> F
Homogeneous module homomorphism

julia> F = graded_free_module(Rg, 1);

julia> A = Rg[x; y];

julia> B = Rg[x^2; y^3; z^4];

julia> M = SubquoModule(F, A, B);

julia> P = presentation(M)
0 <---- M <---- Rg^2 <---- Rg^5

julia> P[-2]
Graded free module Rg^0 of rank 0 over Rg

julia> P[-1]
Graded subquotient of submodule of F generated by
1 -> x*e[1]
2 -> y*e[1]
by submodule of F generated by
1 -> x^2*e[1]
2 -> y^3*e[1]
3 -> z^4*e[1]

julia> P[0]
Graded free module Rg^2([-1]) of rank 2 over Rg

julia> P[1]
Graded free module Rg^2([-2]) + Rg^1([-3]) + Rg^2([-5]) of rank 5 over Rg

julia> map(P,-1)
M -> 0
x*e[1] -> 0
y*e[1] -> 0
Homogeneous module homomorphism

julia> map(P,0)
Rg^2 -> M
e[1] -> x*e[1]
e[2] -> y*e[1]
Homogeneous module homomorphism

julia> map(P,1)
Rg^5 -> Rg^2
e[1] -> x*e[1]
e[2] -> -y*e[1] + x*e[2]
e[3] -> y^2*e[2]
e[4] -> z^4*e[1]
e[5] -> z^4*e[2]
Homogeneous module homomorphism
source

Representation as Cokernel

present_as_cokernelFunction
present_as_cokernel(M::SubquoModule, task::Symbol = :none)

Return a subquotient C which is isomorphic to M, and whose generators are the standard unit vectors of its ambient free module.

Additionally,

  • return an isomorphism M $\to$ C if task = :with_morphism,
  • return and cache an isomorphism M $\to$ C if task = :cache_morphism,
  • do none of the above if task = :none (default).

If task = :only_morphism, return only an isomorphism.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> A = R[x; y];

julia> B = R[x^2; y^3; z^4];

julia> M = SubquoModule(A, B)
Subquotient of Submodule with 2 generators
1 -> x*e[1]
2 -> y*e[1]
by Submodule with 3 generators
1 -> x^2*e[1]
2 -> y^3*e[1]
3 -> z^4*e[1]

julia> C = present_as_cokernel(M)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 5 generators
1 -> x*e[1]
2 -> -y*e[1] + x*e[2]
3 -> y^2*e[2]
4 -> z^4*e[1]
5 -> z^4*e[2]
julia> Rg, (x, y, z) = graded_polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = graded_free_module(Rg, 1);

julia> A = Rg[x; y];

julia> B = Rg[x^2; y^3; z^4];

julia> M = SubquoModule(F, A, B);

julia> present_as_cokernel(M, :with_morphism)
(Graded subquotient of submodule of Rg^2 generated by
1 -> e[1]
2 -> e[2]
by submodule of Rg^2 generated by
1 -> x*e[1]
2 -> -y*e[1] + x*e[2]
3 -> y^2*e[2]
4 -> z^4*e[1]
5 -> z^4*e[2], Graded subquotient of submodule of Rg^2 generated by
1 -> e[1]
2 -> e[2]
by submodule of Rg^2 generated by
1 -> x*e[1]
2 -> -y*e[1] + x*e[2]
3 -> y^2*e[2]
4 -> z^4*e[1]
5 -> z^4*e[2] -> M
e[1] -> x*e[1]
e[2] -> y*e[1]
Homogeneous module homomorphism)
source

Free Resolutions

free_resolutionMethod
free_resolution(M::SubquoModule{<:MPolyRingElem}; 
    ordering::ModuleOrdering = default_ordering(M),
    length::Int = 0, algorithm::Symbol = :fres
  )

Return a free resolution of M.

If length != 0, the free resolution is only computed up to the length-th free module. Current options for algorithm are :fres, :nres, and :mres.

Note

The function first computes a presentation of M. It then successively computes higher syzygy modules.

Note
  • If algorithm == mres, and M is positively graded, a minimal free resolution is returned.
  • If algorithm == nres, and M is positively graded, the function proceeds as above except that it starts by computing a presentation which is not neccessarily minimal.

In both cases, if M is not (positively) graded, the function still aims at returning an ''improved'' resolution.

Note

If algorithm == fres, the function relies on an enhanced version of Schreyer's algorithm [EMSS16]. Typically, this is more efficient than the approaches above, but the resulting resolution is far from being minimal.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"])
(Multivariate polynomial ring in 3 variables over QQ, QQMPolyRingElem[x, y, z])

julia> A = R[x; y]
[x]
[y]

julia> B = R[x^2; x*y; y^2; z^4]
[x^2]
[x*y]
[y^2]
[z^4]

julia> M = SubquoModule(A, B)
Subquotient of Submodule with 2 generators
1 -> x*e[1]
2 -> y*e[1]
by Submodule with 4 generators
1 -> x^2*e[1]
2 -> x*y*e[1]
3 -> y^2*e[1]
4 -> z^4*e[1]

julia> fr = free_resolution(M, length=1)
Free resolution of M
R^2 <---- R^6
0         1

julia> is_complete(fr)
false

julia> fr[4]
Free module of rank 0 over R

julia> fr
Free resolution of M
R^2 <---- R^6 <---- R^6 <---- R^2 <---- 0
0         1         2         3         4

julia> is_complete(fr)
true
julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> Z = R(0)
0

julia> O = R(1)
1

julia> B = [Z Z Z O; w*y w*z-x*y x*z-y^2 Z];

julia> A = transpose(matrix(B));

julia> M = graded_cokernel(A)
Graded subquotient of submodule of R^2 generated by
1 -> e[1]
2 -> e[2]
by submodule of R^2 generated by
1 -> w*y*e[2]
2 -> (w*z - x*y)*e[2]
3 -> (x*z - y^2)*e[2]
4 -> e[1]

julia> FM1 = free_resolution(M)
Free resolution of M
R^2 <---- R^7 <---- R^8 <---- R^3 <---- 0
0         1         2         3         4

julia> betti_table(FM1)
       0  1  2  3
-----------------
-1   : -  1  -  -
0    : 2  -  -  -
1    : -  3  3  1
2    : -  3  5  2
-----------------
total: 2  7  8  3


julia> matrix(map(FM1, 1))
[1            0]
[0   -x*z + y^2]
[0   -w*z + x*y]
[0          w*y]
[0        x^2*z]
[0        w*x*z]
[0        w^2*z]

julia> FM2 = free_resolution(M, algorithm = :nres)
Free resolution of M
R^2 <---- R^4 <---- R^4 <---- R^2 <---- 0
0         1         2         3         4

julia> betti_table(FM2)
       0  1  2  3
-----------------
-1   : -  1  -  -
0    : 2  -  -  -
1    : -  3  -  -
2    : -  -  4  2
-----------------
total: 2  4  4  2


julia> matrix(map(FM2, 1))
[1            0]
[0   -x*z + y^2]
[0   -w*z + x*y]
[0          w*y]

julia> FM3 = free_resolution(M, algorithm = :mres)
Free resolution of M
R^1 <---- R^3 <---- R^4 <---- R^2 <---- 0
0         1         2         3         4

julia> betti_table(FM3)
       0  1  2  3
-----------------
0    : 1  -  -  -
1    : -  3  -  -
2    : -  -  4  2
-----------------
total: 1  3  4  2


julia> matrix(map(FM3, 1))
[-x*z + y^2]
[-w*z + x*y]
[       w*y]

Note: Over rings other than polynomial rings, the method will default to a lazy, iterative kernel computation.

source

Betti Tables

Given a $\mathbb Z$-graded multivariate polynomial ring $S$, and given a graded free resolution with finitely generated graded free $S$-modules

\[F_i=\bigoplus_j S(-j) ^{\beta_{ij}},\]

the numbers $\beta_{ij}$ are commonly known as the graded Betti numbers of the resolution. A convenient way of visualizing these numbers is to write a Betti table as in the example below:

       0  1  2  3  
------------------
0    : 1  -  -  -  
1    : -  2  1  -  
2    : -  2  3  1  
------------------
total: 1  4  4  1

A number $i$ in the top row of the table refers to the $i$-th free module $F_i$ of the resolution. More precisely, the column with first entry $i$ lists the number of free generators of $F_i$ in different degrees and, in the bottom row, the total number of free generators (that is, the rank of $F_i$). If $k$ is the first entry of a row containing a number $\beta$ in the column corresponding to $F_i$, then $F_i$ has $\beta$ generators in degree $k+i$. That is, for a free module $F_i$ written as a direct sum as above, $\beta$ is the number $\beta_{ij}$ with $j=k+i$. The explicit example table above indicates, for instance, that $F_2$ has one generator in degree 3 and three generators in degree 4. In total, the diagram corresponds to a graded free resolution of type

\[S \leftarrow S(-2)^2\oplus S(-3)^2 \leftarrow S(-3)\oplus S(-4)^3 \leftarrow S(-5) \leftarrow 0.\]

betti_tableMethod
betti_table(F::FreeResolution)

Given a $\mathbb Z$-graded free resolution F, return the graded Betti numbers of F in form of a Betti table.

Alternatively, use betti.

Examples

julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> I = ideal(R, [x*z, y*z, x*w^2, y*w^2])
ideal(x*z, y*z, w^2*x, w^2*y)

julia> A, _= quo(R, I)
(Quotient of multivariate polynomial ring by ideal with 4 generators, Map from
R to A defined by a julia-function with inverse)

julia> FA  = free_resolution(A)
Free resolution of A
R^1 <---- R^4 <---- R^4 <---- R^1 <---- 0
0         1         2         3         4

julia> betti_table(FA)
       0  1  2  3
------------------
0    : 1  -  -  -
1    : -  2  1  -
2    : -  2  3  1
------------------
total: 1  4  4  1

julia> R, (x, y) = graded_polynomial_ring(QQ, ["x", "y"]);

julia> I = ideal(R, [x, y, x+y]);

julia> M = quotient_ring_as_module(I);

julia> FM = free_resolution(M, algorithm = :nres);

julia> betti_table(FM)
       0  1  2
---------------
-1   : -  -  1
0    : 1  3  1
---------------
total: 1  3  2
source
minimal_betti_tableMethod
minimal_betti_table(F::FreeResolution{T}; check::Bool=true) where {T<:ModuleFP}

Given a graded free resolution F over a standard $\mathbb Z$-graded multivariate polynomial ring with coefficients in a field, return the Betti table of the minimal free resolution arising from F.

Note

The algorithm proceeds without actually minimizing the resolution.

Examples

julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> I = ideal(R, [w^2-x*z, w*x-y*z, x^2-w*y, x*y-z^2, y^2-w*z]);

julia> A, _ = quo(R, I)
(Quotient of multivariate polynomial ring by ideal with 5 generators, Map from
R to A defined by a julia-function with inverse)

julia> FA = free_resolution(A)
Free resolution of A
R^1 <---- R^5 <---- R^6 <---- R^2 <---- 0
0         1         2         3         4

julia> betti_table(FA)
       0  1  2  3  
------------------
0    : 1  -  -  -  
1    : -  5  5  1  
2    : -  -  1  1  
------------------
total: 1  5  6  2  

julia> minimal_betti_table(FA)
       0  1  2  3  
------------------
0    : 1  -  -  -  
1    : -  5  5  -  
2    : -  -  -  1  
------------------
total: 1  5  5  1  
source
minimal_betti_tableMethod
minimal_betti_table(M::ModuleFP{T}) where {T<:MPolyDecRingElem}
minimal_betti_table(A::MPolyQuoRing{T}) where {T<:MPolyDecRingElem}
minimal_betti_table(I::MPolyIdeal{T}) where {T<:MPolyDecRingElem}

Given a finitely presented graded module M over a standard $\mathbb Z$-graded multivariate polynomial ring with coefficients in a field, return the Betti Table of the minimal free resolution of M. Similarly for A and I.

Examples

julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> I = ideal(R, [w^2-x*z, w*x-y*z, x^2-w*y, x*y-z^2, y^2-w*z]);

julia> A, _ = quo(R, I)
(Quotient of multivariate polynomial ring by ideal with 5 generators, Map from
R to A defined by a julia-function with inverse)

julia> minimal_betti_table(A)
       0  1  2  3
------------------
0    : 1  -  -  -
1    : -  5  5  -
2    : -  -  -  1
------------------
total: 1  5  5  1
source

Castelnuovo-Mumford Regularity

cm_regularityMethod
cm_regularity(M::ModuleFP; check::Bool=true)

Given a finitely presented graded module M over a standard $\mathbb Z$-graded multivariate polynomial ring with coefficients in a field, return the Castelnuovo-Mumford regularity of M.

cm_regularity(I::MPolyIdeal)

Given a (homogeneous) ideal I in a standard $\mathbb Z$-graded multivariate polynomial ring with coefficients in a field, return the Castelnuovo-Mumford regularity of I.

Examples

julia> R, (x, y, z) = graded_polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = graded_free_module(R, 1);

julia> M, _ = quo(F, [x^2*F[1], y^2*F[1], z^2*F[1]])
(Graded subquotient of submodule of F generated by
1 -> e[1]
by submodule of F generated by
1 -> x^2*e[1]
2 -> y^2*e[1]
3 -> z^2*e[1], F -> M
e[1] -> e[1]
Homogeneous module homomorphism)

julia> cm_regularity(M)
3

julia> minimal_betti_table(M)
       0 1 2 3 
--------------
0    : 1 - - - 
1    : - 3 - - 
2    : - - 3 - 
3    : - - - 1 
--------------
total: 1 3 3 1 
julia> R, (w, x, y, z) = graded_polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> I = ideal(R, [-x*z+y^2, x*y-w*z, x^2-w*y]);

julia> cm_regularity(I)
2

julia> A, _ = quo(R, I);

julia> minimal_betti_table(A)
       0 1 2 
------------
0    : 1 - - 
1    : - 3 2 
------------
total: 1 3 2 
source

Homology

homologyMethod
homology(C::ComplexOfMorphisms{<:ModuleFP})

Return the homology of C.

Examples

julia> R, (x,) = polynomial_ring(QQ, ["x"]);

julia> F = free_module(R, 1);

julia> A, _ = quo(F, [x^4*F[1]]);

julia> B, _ = quo(F, [x^3*F[1]]);

julia> a = hom(A, B, [x^2*B[1]]);

julia> b = hom(B, B, [x^2*B[1]]);

julia> C = ComplexOfMorphisms(ModuleFP, [a, b]);

julia> H = homology(C)
3-element Vector{SubquoModule{QQMPolyRingElem}}:
 Subquotient of Submodule with 1 generator
1 -> x*e[1]
by Submodule with 1 generator
1 -> x^4*e[1]
 Subquotient of Submodule with 1 generator
1 -> x*e[1]
by Submodule with 2 generators
1 -> x^3*e[1]
2 -> x^2*e[1]
 Subquotient of Submodule with 1 generator
1 -> e[1]
by Submodule with 2 generators
1 -> x^3*e[1]
2 -> x^2*e[1]
source
homologyMethod
homology(C::ComplexOfMorphisms{<:ModuleFP}, i::Int)

Return the i-th homology module of C.

Examples

julia> R, (x,) = polynomial_ring(QQ, ["x"]);

julia> F = free_module(R, 1);

julia> A, _ = quo(F, [x^4*F[1]]);

julia> B, _ = quo(F, [x^3*F[1]]);

julia> a = hom(A, B, [x^2*B[1]]);

julia> b = hom(B, B, [x^2*B[1]]);

julia> C = ComplexOfMorphisms(ModuleFP, [a, b]);

julia> H = homology(C, 1)
Subquotient of Submodule with 1 generator
1 -> x*e[1]
by Submodule with 2 generators
1 -> x^3*e[1]
2 -> x^2*e[1]
source

Hom and Ext

homMethod
hom(M::ModuleFP, N::ModuleFP; algorithm::Symbol=:maps)

Return the module Hom(M,N) as an object of type SubquoModule.

Additionally, if H is that object, return the map which sends an element of H to the corresponding homomorphism M $\to$N.

The keyword algorithm can be set to :maps for the default algorithm or to :matrices for an alternative based on matrices.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = FreeMod(R, 2);

julia> V = [x*F[1], y^2*F[2]];

julia> M = quo_object(F, V)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y^2*e[2]

julia> H = hom(M, M)[1]
hom of (M, M)

julia> gens(H)
2-element Vector{SubquoModuleElem{QQMPolyRingElem}}:
 (e[1] -> e[1])
 (e[2] -> e[2])

julia> relations(H)
4-element Vector{FreeModElem{QQMPolyRingElem}}:
 x*(e[1] -> e[1])
 y^2*(e[1] -> e[2])
 x*(e[2] -> e[1])
 y^2*(e[2] -> e[2])
source
element_to_homomorphismMethod
element_to_homomorphism(f::ModuleFPElem)

If f is an element of a module created via hom(M,N), for some modules M and N, return the homomorphism M $\to$ N corresponding to f.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = FreeMod(R, 2);

julia> V = [x*F[1], y^2*F[2]];

julia> M = quo_object(F, V)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y^2*e[2]

julia> H = hom(M, M)[1];

julia> gens(H)
2-element Vector{SubquoModuleElem{QQMPolyRingElem}}:
 (e[1] -> e[1])
 (e[2] -> e[2])

julia> relations(H)
4-element Vector{FreeModElem{QQMPolyRingElem}}:
 x*(e[1] -> e[1])
 y^2*(e[1] -> e[2])
 x*(e[2] -> e[1])
 y^2*(e[2] -> e[2])

julia> a = element_to_homomorphism(H[1]+y*H[2])
Map with following data
Domain:
=======
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y^2*e[2]
Codomain:
=========
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y^2*e[2]

julia> matrix(a)
[1   0]
[0   y]
source
homomorphism_to_elementMethod
homomorphism_to_element(H::ModuleFP, a::ModuleFPHom)

If the module H is created via hom(M,N), for some modules M and N, and a: M $\to$ N is a homomorphism, then return the element of H corresponding to a.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = FreeMod(R, 2);

julia> V = [x*F[1], y^2*F[2]];

julia> M = quo_object(F, V)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y^2*e[2]

julia> H = hom(M, M)[1];

julia> gens(H)
2-element Vector{SubquoModuleElem{QQMPolyRingElem}}:
 (e[1] -> e[1])
 (e[2] -> e[2])

julia> relations(H)
4-element Vector{FreeModElem{QQMPolyRingElem}}:
 x*(e[1] -> e[1])
 y^2*(e[1] -> e[2])
 x*(e[2] -> e[1])
 y^2*(e[2] -> e[2])

julia> W =  [M[1], y*M[2]];

julia> a = hom(M, M, W);

julia> is_welldefined(a)
true

julia> matrix(a)
[1   0]
[0   y]

julia> m = homomorphism_to_element(H, a)
(e[1] -> e[1]) + y*(e[2] -> e[2])
source
extMethod
ext(M::ModuleFP, N::ModuleFP, i::Int)

Return $\text{Ext}^i(M,N)$.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = FreeMod(R, 1);

julia> V = [x*F[1], y*F[1]];

julia> M = quo_object(F, V)
Subquotient of Submodule with 1 generator
1 -> e[1]
by Submodule with 2 generators
1 -> x*e[1]
2 -> y*e[1]

julia> ext(M, M, 0)
Subquotient of Submodule with 1 generator
1 -> (e[1] -> e[1])
by Submodule with 2 generators
1 -> y*(e[1] -> e[1])
2 -> x*(e[1] -> e[1])

julia> ext(M, M, 1)
Subquotient of Submodule with 2 generators
1 -> (e[1] -> e[1])
2 -> (e[2] -> e[1])
by Submodule with 4 generators
1 -> y*(e[1] -> e[1])
2 -> x*(e[1] -> e[1])
3 -> y*(e[2] -> e[1])
4 -> x*(e[2] -> e[1])

julia> ext(M, M, 2)
Subquotient of Submodule with 1 generator
1 -> (e[1] -> e[1])
by Submodule with 2 generators
1 -> y*(e[1] -> e[1])
2 -> x*(e[1] -> e[1])

julia> ext(M, M, 3)
Submodule with 0 generators
represented as subquotient with no relations.
source

Tensorproduct and Tor

tensor_productMethod
tensor_product(M::ModuleFP...; task::Symbol = :none)

Given a collection of modules, say, $M_1, \dots, M_n$ over a ring $R$, return $M_1\otimes_R \cdots \otimes_R M_n$.

If task = :map, additionally return the map which sends a tuple $(m_1,\dots, m_n)$ of elements $m_i\in M_i$ to the pure tensor $m_1\otimes\dots\otimes m_n$.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = free_module(R, 1);

julia> A = R[x; y];

julia> B = R[x^2; y^3; z^4];

julia> M = SubquoModule(F, A, B);

julia> gens(M)
2-element Vector{SubquoModuleElem{QQMPolyRingElem}}:
 x*e[1]
 y*e[1]

julia> T, t = tensor_product(M, M; task = :map);

julia> gens(T)
4-element Vector{SubquoModuleElem{QQMPolyRingElem}}:
 x^2*e[1] \otimes e[1]
 x*y*e[1] \otimes e[1]
 x*y*e[1] \otimes e[1]
 y^2*e[1] \otimes e[1]

julia> domain(t)
parent of tuples of type Tuple{SubquoModuleElem{QQMPolyRingElem}, SubquoModuleElem{QQMPolyRingElem}}

julia> t((M[1], M[2]))
x*y*e[1] \otimes e[1]
source
torMethod
tor(M::ModuleFP, N::ModuleFP, i::Int)

Return $\text{Tor}_i(M,N)$.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> A = R[x; y];

julia> B = R[x^2; y^3; z^4];

julia> M = SubquoModule(A, B);

julia> F = free_module(R, 1);

julia> Q, _ = quo(F, [x*F[1]]);

julia> T0 = tor(Q, M, 0)
Subquotient of Submodule with 2 generators
1 -> x*e[1] \otimes e[1]
2 -> y*e[1] \otimes e[1]
by Submodule with 4 generators
1 -> x^2*e[1] \otimes e[1]
2 -> y^3*e[1] \otimes e[1]
3 -> z^4*e[1] \otimes e[1]
4 -> x*y*e[1] \otimes e[1]

julia> T1 = tor(Q, M, 1)
Subquotient of Submodule with 2 generators
1 -> x*e[1] \otimes e[1]
2 -> x*y*e[1] \otimes e[1]
by Submodule with 3 generators
1 -> x^2*e[1] \otimes e[1]
2 -> y^3*e[1] \otimes e[1]
3 -> z^4*e[1] \otimes e[1]

julia> T2 =  tor(Q, M, 2)
Submodule with 0 generators
represented as subquotient with no relations.
source

Fitting Ideals

fitting_idealMethod
fitting_ideal(M::ModuleFP{T}, i::Int) where T <: MPolyRingElem

Return the i-th Fitting ideal of M.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = free_module(R, 2);

julia> o = zero(R)
0

julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x])
[x^3 - y^2           0]
[        0   x^3 - y^2]
[     -x^2           y]
[       -y           x]

julia> M = quo_object(F,U)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 4 generators
1 -> (x^3 - y^2)*e[1]
2 -> (x^3 - y^2)*e[2]
3 -> -x^2*e[1] + y*e[2]
4 -> -y*e[1] + x*e[2]

julia> fitting_ideal(M, -1)
Ideal generated by
  0

julia> fitting_ideal(M, 0)
Ideal generated by
  x^3 - y^2

julia> fitting_ideal(M, 1)
Ideal generated by
  y
  x

julia> fitting_ideal(M, 2)
Ideal generated by
  1
source

Flatness

Checking flatness in OSCAR relies on characterizing flatness in terms of Fitting ideals.

is_flatMethod
is_flat(M::ModuleFP{T}) where T <: MPolyRingElem

Return true if M is flat, false otherwise.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = free_module(R, 2);

julia> o = zero(R)
0

julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x])
[x^3 - y^2           0]
[        0   x^3 - y^2]
[     -x^2           y]
[       -y           x]

julia> M = quo_object(F,U)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 4 generators
1 -> (x^3 - y^2)*e[1]
2 -> (x^3 - y^2)*e[2]
3 -> -x^2*e[1] + y*e[2]
4 -> -y*e[1] + x*e[2]

julia> is_flat(M)
false
source
non_flat_locusMethod
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) is non-flat iff the prime ideal contains the returned ideal.

Examples

julia> R, (x, y) = polynomial_ring(QQ, ["x", "y"]);

julia> F = free_module(R, 2);

julia> o = zero(R)
0

julia> U = matrix([x^3-y^2 o; o x^3-y^2; -x^2 y; -y x])
[x^3 - y^2           0]
[        0   x^3 - y^2]
[     -x^2           y]
[       -y           x]

julia> M = quo_object(F,U)
Subquotient of Submodule with 2 generators
1 -> e[1]
2 -> e[2]
by Submodule with 4 generators
1 -> (x^3 - y^2)*e[1]
2 -> (x^3 - y^2)*e[2]
3 -> -x^2*e[1] + y*e[2]
4 -> -y*e[1] + x*e[2]

julia> non_flat_locus(M)
Ideal generated by
  x^3 - y^2
source

Regular Sequence Test

is_regular_sequenceMethod
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.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = free_module(R, 1);

julia> V =  [x*z-z, x*y-y, x]
3-element Vector{QQMPolyRingElem}:
 x*z - z
 x*y - y
 x

julia> is_regular_sequence(V, F)
false

julia> W = [x*z-z, x, x*y-y]
3-element Vector{QQMPolyRingElem}:
 x*z - z
 x
 x*y - y

julia> is_regular_sequence(W, F)
true
source

Koszul Complex

koszul_matrixMethod
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)$.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> V = gens(R)
3-element Vector{QQMPolyRingElem}:
 x
 y
 z

julia> koszul_matrix(V, 3)
[x   y   z]

julia> koszul_matrix(V, 2)
[-y   -z    0]
[ x    0   -z]
[ 0    x    y]

julia> koszul_matrix(V, 1)
[ z]
[-y]
[ x]
source
koszul_complexMethod
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)$.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> V = gens(R)
3-element Vector{QQMPolyRingElem}:
 x
 y
 z

julia> K = koszul_complex(V);

julia> matrix(map(K, 2))
[-y   -z    0]
[ x    0   -z]
[ 0    x    y]

julia> Kd = hom(K, free_module(R, 1));

julia> matrix(map(Kd, 1))
[-y    x   0]
[-z    0   x]
[ 0   -z   y]
source

Koszul Homology

koszul_homologyMethod
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 Koszul complex defined by $f_1, \dots, f_r$.

Note

See [GP08] or [DL06] for the definition of the Koszul complex.

Examples

julia> R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]);

julia> F = free_module(R, 1);

julia> V =  [x*y, x*z, y*z]
3-element Vector{QQMPolyRingElem}:
 x*y
 x*z
 y*z

julia> koszul_homology(V, F, 0)
Subquotient of Submodule with 1 generator
1 -> e[1]^e[2]^e[3]
by Submodule with 3 generators
1 -> y*z*e[1]^e[2]^e[3]
2 -> -x*z*e[1]^e[2]^e[3]
3 -> x*y*e[1]^e[2]^e[3]

julia> koszul_homology(V, F, 1)
Subquotient of Submodule with 2 generators
1 -> y*e[1]^e[3] \otimes e[1] + z*e[2]^e[3] \otimes e[1]
2 -> x*e[1]^e[2] \otimes e[1] - z*e[2]^e[3] \otimes e[1]
by Submodule with 3 generators
1 -> -x*z*e[1]^e[2] \otimes e[1] - y*z*e[1]^e[3] \otimes e[1]
2 -> x*y*e[1]^e[2] \otimes e[1] - y*z*e[2]^e[3] \otimes e[1]
3 -> x*y*e[1]^e[3] \otimes e[1] + x*z*e[2]^e[3] \otimes e[1]

julia> koszul_homology(V, F, 2)
Subquotient of Submodule with 1 generator
1 -> x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1]
by Submodule with 1 generator
1 -> x*y*e[1] \otimes e[1] + x*z*e[2] \otimes e[1] + y*z*e[3] \otimes e[1]
julia> R, (w, x, y, z) = polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> TC = ideal(R, [x*z-y^2, w*z-x*y, w*y-x^2]);

julia> F = free_module(R, 1);

julia> koszul_homology(gens(TC), F, 0)
Subquotient of Submodule with 1 generator
1 -> e[1]^e[2]^e[3]
by Submodule with 3 generators
1 -> (w*y - x^2)*e[1]^e[2]^e[3]
2 -> (-w*z + x*y)*e[1]^e[2]^e[3]
3 -> (x*z - y^2)*e[1]^e[2]^e[3]

julia> koszul_homology(gens(TC), F, 1)
Subquotient of Submodule with 2 generators
1 -> z*e[1]^e[2] \otimes e[1] + y*e[1]^e[3] \otimes e[1] + x*e[2]^e[3] \otimes e[1]
2 -> y*e[1]^e[2] \otimes e[1] + x*e[1]^e[3] \otimes e[1] + w*e[2]^e[3] \otimes e[1]
by Submodule with 3 generators
1 -> (-w*z + x*y)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[1]^e[3] \otimes e[1]
2 -> (x*z - y^2)*e[1]^e[2] \otimes e[1] + (-w*y + x^2)*e[2]^e[3] \otimes e[1]
3 -> (x*z - y^2)*e[1]^e[3] \otimes e[1] + (w*z - x*y)*e[2]^e[3] \otimes e[1]

julia> koszul_homology(gens(TC), F, 2)
Subquotient of Submodule with 1 generator
1 -> (-x*z + y^2)*e[1] \otimes e[1] + (-w*z + x*y)*e[2] \otimes e[1] + (-w*y + x^2)*e[3] \otimes e[1]
by Submodule with 1 generator
1 -> (x*z - y^2)*e[1] \otimes e[1] + (w*z - x*y)*e[2] \otimes e[1] + (w*y - x^2)*e[3] \otimes e[1]
source

Depth

The computation of depth in OSCAR relies on expressing depth in terms of Koszul cohomology.

depthMethod
depth(I::MPolyIdeal{T}, M::ModuleFP{T}) where T <: MPolyRingElem

Return the depth of I on M.

Examples

julia> R, (w, x, y, z) = polynomial_ring(QQ, ["w", "x", "y", "z"]);

julia> TC = ideal(R, [x*z-y^2, w*z-x*y, w*y-x^2]);

julia> dim(TC)
2

julia> F = free_module(R, 1);

julia> U = collect(gen(TC, i)*F[1] for i in 1:ngens(TC));

julia> M, _ = quo(F, U);

julia> I = ideal(R, gens(R))
Ideal generated by
  w
  x
  y
  z

julia> depth(I, M)
2
julia> S, x, y = polynomial_ring(QQ, "x" => 1:3, "y" => 1:5);

julia> W = [y[1]-x[1]^2,  y[2]-x[2]^2,   y[3]-x[3]^2, y[4]-x[2]*(x[1]-x[3]),  y[5]-(x[1]-x[2])*x[3]];

julia> J = eliminate(ideal(S, W), x);

julia> R, y = polynomial_ring(QQ, "y" => 1:5);

julia> W = append!(repeat([zero(R)], 3), gens(R))
8-element Vector{QQMPolyRingElem}:
 0
 0
 0
 y[1]
 y[2]
 y[3]
 y[4]
 y[5]

julia> P = hom(S, R, W);

julia> VP4 = P(J);

julia> dim(VP4)
3

julia> F = free_module(R, 1);

julia> U = collect(gen(VP4, i)*F[1] for i in 1:ngens(VP4));

julia> M, _ = quo(F, U);

julia> I = ideal(R, gens(R))
Ideal generated by
  y[1]
  y[2]
  y[3]
  y[4]
  y[5]

julia> depth(I, M)
1
source