Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ DocTestSetup = Oscar.doctestsetup()
# [Free Modules](@id free_modules)

In this section, the expression *free module* refers to a free module of finite rank
over a ring of type `MPolyRing`, `MPolyQuoRing`, `MPolyLocRing`, or `MPolyQuoLocRing`.
over a ring of type `MPolyRing`, `MPolyQuoRing`, `MPolyLocRing`, `MPolyQuoLocRing`, `ZZRing`, or `Field`.
More concretely, given a ring $R$ of one of these types, the free $R$-modules considered are of
type $R^p$, where we think of $R^p$ as a free module with a given basis, namely the basis of
standard unit vectors. Accordingly, elements of free modules are represented by coordinate vectors,
Expand Down Expand Up @@ -35,6 +35,11 @@ they are modeled as objects of the concrete type `FreeMod{T} <: AbstractFreeMod{
free_module(R::MPolyRing, n::Int, name::VarName = :e; cached::Bool = false)
```

```@docs
free_module(::Type{<:FreeMod}, R::Union{ZZRing, Field, MPolyRing, MPolyQuoRing, MPolyLocRing, MPolyQuoLocRing},
n::Int, name::VarName = :e; cached::Bool = false)
```

Over graded multivariate polynomial rings and their quotients, there are two basic ways of
creating graded free modules: While the `grade` function allows one to create a graded free module
by assigning a grading to a free module already constructed, the `graded_free_module` function is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ DocTestSetup = Oscar.doctestsetup()

A subquotient is a submodule of a quotient of a free module. In this section, the expression
*subquotient* refers to a subquotient over a ring of type `MPolyRing`, `MPolyQuoRing`,
`MPolyLocRing`, or `MPolyQuoLocRing`. That is, given a ring $R$ of one of these
`MPolyLocRing`, `MPolyQuoLocRing`, `ZZRing`, or `Field`. That is, given a ring $R$ of one of these
types, a subquotient $M$ over $R$ is a module of type

$M = (\text{im } a + \text{im } b)/\text{im } b,$
Expand Down
9 changes: 9 additions & 0 deletions docs/src/CommutativeAlgebra/homological_algebra.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ supporting computations in homological algebra.
```@docs
prune_with_map(M::ModuleFP)
```
## Finiteness and cardinality as a set

```@docs
is_finite(M::SubquoModule{T}) where {T<:Union{ZZRingElem, FieldElem}}
```

```@docs
size(M::SubquoModule{T}) where {T<:Union{ZZRingElem, FieldElem}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stupid question, but: should we use size for this, given that it has a specific meaning in Julia already?

This is why we used order for groups or finite fields instead of size, even though e.g. GAP uses Size primarily (with Order "just" an alias. That said, order is somewhat common as terminology for groups and finite field, but I don't think it is usual for e.g. modules?

If we decide size is a good fit, then perhaps we should ponder whether to also allow it for fields and groups as an alias to order, simply for user convenience / easier discoverability...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also fine with order and it is a better fit. a Shall we change it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course there are also orders in number theory, so I think we should keep size as it is.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meanwhile settled?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with using size here but it would be good if @thofma and @fieker could briefly chime in

```

## Presentations

Expand Down
10 changes: 6 additions & 4 deletions experimental/InjectiveResolutions/src/InjectiveResolutions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import ..Oscar:
_graded_kernel,
_reduce,
_saturation,
_simple_kernel,
annihilator,
coefficient_ring,
coefficients,
cone,
coordinates,
coordinates_atomic,
coordinates_via_transform,
degree,
degree,
dim,
dim,
elem_type,
evaluate,
Expand All @@ -26,20 +25,24 @@ import ..Oscar:
gens,
grading_group,
hyperplanes,
images_of_generators,
in_atomic,
intersect,
inv,
is_normal,
is_pointed,
is_subset,
is_zm_graded,
kernel,
kernel_atomic,
lift_std,
ModuleGens,
normal_form,
one,
oscar_free_module,
oscar_generators,
primitive_generator,
singular_freemodule,
singular_generators,
singular_module,
singular_poly_ring,
Expand All @@ -51,7 +54,6 @@ import ..Oscar:
zero,
zonotope


import ..Oscar.Singular:
FreeModule,
has_global_ordering,
Expand Down
48 changes: 44 additions & 4 deletions experimental/InjectiveResolutions/src/ModuleFunctionality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
return res
end

function coordinates_atomic(a::FreeModElem{T}, M::SubModuleOfFreeModule; task=:auto) where {T <: MonoidAlgebraElem}
if task != :auto && task != :via_transform
error("Only task=:via_transform is supported for MonoidAlgebra.")

Check warning on line 62 in experimental/InjectiveResolutions/src/ModuleFunctionality.jl

View check run for this annotation

Codecov / codecov/patch

experimental/InjectiveResolutions/src/ModuleFunctionality.jl#L62

Added line #L62 was not covered by tests
end
std, _ = lift_std(M)
return coordinates_via_transform(a, std)
end

function lift_std(M::ModuleGens{T}) where {T <: MonoidAlgebraElem}
R = base_ring(M)
G,Trans_mat = Singular.lift_std(singular_generators(M)) # When Singular supports reduction add it also here
Expand All @@ -76,6 +84,28 @@
return mg, mat
end

function coordinates_via_transform(a::FreeModElem{T}, generators::ModuleGens{T}) where {T <: MonoidAlgebraElem}
A = get_attribute(generators, :transformation_matrix)
A === nothing && error("No transformation matrix in the Gröbner basis.")
if iszero(a)
return sparse_row(base_ring(parent(a)))

Check warning on line 91 in experimental/InjectiveResolutions/src/ModuleFunctionality.jl

View check run for this annotation

Codecov / codecov/patch

experimental/InjectiveResolutions/src/ModuleFunctionality.jl#L91

Added line #L91 was not covered by tests
end
if !is_global(generators.ordering)
error("Ordering is not global")

Check warning on line 94 in experimental/InjectiveResolutions/src/ModuleFunctionality.jl

View check run for this annotation

Codecov / codecov/patch

experimental/InjectiveResolutions/src/ModuleFunctionality.jl#L94

Added line #L94 was not covered by tests
end
@assert generators.isGB
S = singular_generators(generators)
S.isGB = generators.isGB
b = ModuleGens([a], singular_freemodule(generators))
s, _ = Singular.lift(S, singular_generators(b))
if Singular.ngens(s) == 0 || iszero(s[1])
error("The free module element is not liftable to the given generating system.")

Check warning on line 102 in experimental/InjectiveResolutions/src/ModuleFunctionality.jl

View check run for this annotation

Codecov / codecov/patch

experimental/InjectiveResolutions/src/ModuleFunctionality.jl#L102

Added line #L102 was not covered by tests
end
Rx = base_ring(generators)
coords_wrt_groebner_basis = sparse_row(Rx, s[1], 1:ngens(generators))
return coords_wrt_groebner_basis * sparse_matrix(A)
end

function sparse_row(
A::MonoidAlgebra{<:FieldElem, <:MPolyRing},
svec::Singular.svector, rng::AbstractUnitRange
Expand All @@ -98,14 +128,24 @@
return SubquoModule(sub, s)
end

function kernel(
function kernel_atomic(
h::FreeModuleHom{<:FreeMod{T}, <:FreeMod{T}, Nothing}
) where {S<:FieldElem, T <: MonoidAlgebraElem{S}}
is_zero(h) && return sub(domain(h), gens(domain(h)))
is_graded(h) && return _graded_kernel(h)
return _simple_kernel(h)
F = domain(h)
G = codomain(h)
gens_h = images_of_generators(h)
mod_gens = ModuleGens(gens_h, G, default_ordering(G))
M = syzygy_module(mod_gens)
v = [F(coordinates(repres(w))) for w in gens(M) if !is_zero(w)]
return sub(F, v)
end

function in_atomic(a::FreeModElem{T}, M::SubModuleOfFreeModule) where {S<:FieldElem, T<:MonoidAlgebraElem{S}}
F = ambient_free_module(M)
return iszero(reduce(a, standard_basis(M, ordering=default_ordering(F))))
end


### Additional adjustments to get the graded aspects to run
function annihilator(N::SubquoModule{T}) where T <: MonoidAlgebraElem
R = base_ring(N)
Expand Down
31 changes: 31 additions & 0 deletions src/Modules/UngradedModules/FreeMod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,37 @@ function FreeMod(R::AdmissibleModuleFPRing, names::Vector{Symbol}; cached::Bool=
return FreeMod{elem_type(R)}(length(names), R, names)
end


@doc raw"""
free_module(::Type{<:FreeMod}, R::Union{ZZRing, Field, MPolyRing, MPolyQuoRing, MPolyLocRing, MPolyQuoLocRing},
n::Int, name::VarName = :e; cached::Bool = false)

Construct a free module of rank `n` over the ring `R` using a sparse implementation
compatible with the generic modules framework, in cases where the `free_module` constructor
returns a free module with a dense implementation.

The string `name` specifies how the basis vectors are printed.

# Examples
```jldoctest
julia> F = free_module(FreeMod, ZZ, 3, "f")
Free module of rank 3 over integer ring

julia> F[1]
f[1]

julia> K = GF(7);

julia> FK = free_module(FreeMod, K, 2)
Free module of rank 2 over K

julia> FK[1]
e[1]
```
"""
free_module(::Type{<:FreeMod}, R::Union{ZZRing, Field, MPolyRing, MPolyQuoRing, MPolyLocRing, MPolyQuoLocRing},
n::Int, name::VarName = :e; cached::Bool = false) = FreeMod(R, n, name, cached=cached)

@doc raw"""
free_module(R::MPolyRing, p::Int, name::VarName = :e; cached::Bool = false)
free_module(R::MPolyQuoRing, p::Int, name::VarName = :e; cached::Bool = false)
Expand Down
45 changes: 22 additions & 23 deletions src/Modules/UngradedModules/FreeModuleHom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -450,40 +450,39 @@

```
"""
function kernel(h::FreeModuleHom{<:FreeMod, <:FreeMod}) #ONLY for free modules...
error("not implemented for modules over rings of type $(typeof(base_ring(domain(h))))")
end

# The following function is part of the requirement of atomic functions to be implemented
# in order to have the modules run over a specific type of ring. The documentation of this is
# pending and so far only orally communicated by Janko Boehm.
#
# The concrete method below uses Singular as a backend to achieve its task. In order
# to have only input which Singular can actually digest, we restrict the signature
# to those cases. The method used to be triggered eventually also for rings which
# did not have a groebner basis backend in Singular, but Singular did not complain.
# This lead to false results without notification. By restricting the signature,
# the user gets the above error message instead.
function kernel(
h::FreeModuleHom{<:FreeMod{T}, <:FreeMod{T}, Nothing}
) where {S<: Union{ZZRingElem, <:FieldElem}, T <: MPolyRingElem{S}}
function kernel(h::FreeModuleHom{<:FreeMod, <:FreeMod})
is_zero(h) && return sub(domain(h), gens(domain(h)))
is_graded(h) && return _graded_kernel(h)
return _simple_kernel(h)
return kernel_atomic(h) # explicitly call kernel_atomic
end

function kernel_atomic(h::FreeModuleHom{<:FreeMod, <:FreeMod})
error("not implemented for modules over rings of type $(typeof(base_ring(domain(h))))")

Check warning on line 460 in src/Modules/UngradedModules/FreeModuleHom.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/FreeModuleHom.jl#L459-L460

Added lines #L459 - L460 were not covered by tests
end

function _simple_kernel(h::FreeModuleHom{<:FreeMod, <:FreeMod})
function kernel_atomic(h::FreeModuleHom{<:FreeMod{T}, <:FreeMod{T}, Nothing}) where {S<:Union{ZZRingElem, FieldElem}, T<:MPolyRingElem{S}}
F = domain(h)
G = codomain(h)
g = images_of_generators(h)
b = ModuleGens(g, G, default_ordering(G))
M = syzygy_module(b)
gens_h = images_of_generators(h)
mod_gens = ModuleGens(gens_h, G, default_ordering(G))
M = syzygy_module(mod_gens)
v = elem_type(F)[F(coordinates(repres(w))) for w in gens(M) if !is_zero(w)]
return sub(F, v)
end

@attr Any function kernel_ctx(h::FreeModuleHom{<:FreeMod{T}, <:FreeMod{T}, Nothing}) where {T<:Union{ZZRingElem, FieldElem}}
solve_init(matrix(h))
end

function kernel_atomic(h::FreeModuleHom{<:FreeMod{T}, <:FreeMod{T}, Nothing}) where {T<:Union{ZZRingElem, FieldElem}}
K = kernel(kernel_ctx(h); side=:left)
F = domain(h)
v = [F(sparse_row(K[j:j, :])) for j in 1:nrows(K)]
return sub(F, v)
end

function _graded_kernel(h::FreeModuleHom{<:FreeMod, <:FreeMod})
I, inc = _simple_kernel(h)
I, inc = kernel_atomic(h)
@assert is_graded(I)
@assert is_homogeneous(inc)
return I, inc
Expand Down
23 changes: 13 additions & 10 deletions src/Modules/UngradedModules/FreeResolutions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -543,35 +543,38 @@
return FreeResolution(cc)
end

function free_resolution(M::SubquoModule{T}) where {T<:RingElem}
function free_resolution(M::SubquoModule{T}; length::Int=0) where {T<:RingElem}
# This generic code computes a free resolution in a lazy way.
# We start out with a presentation of M and implement
# an iterative fill function to compute every higher term
# We start out with a presentation of M and implement
# an iterative fill function to compute every higher term
# on request.
R = base_ring(M)
p = presentation(M)
p.fill = function(C::Hecke.ComplexOfMorphisms, k::Int)
# TODO: Use official getter and setter methods instead
# of messing manually with the internals of the complex.
for i in first(chain_range(C)):k-1
N = domain(map(C, i))
min_index = first(chain_range(C))
target_index = length == 0 ? k-1 : max(min_index - (length - 1), k-1)

Check warning on line 555 in src/Modules/UngradedModules/FreeResolutions.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/FreeResolutions.jl#L554-L555

Added lines #L554 - L555 were not covered by tests

if iszero(N) # Fill up with zero maps
for i in min_index:target_index
N = domain(map(C, i))
if iszero(N)

Check warning on line 559 in src/Modules/UngradedModules/FreeResolutions.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/FreeResolutions.jl#L557-L559

Added lines #L557 - L559 were not covered by tests
C.complete = true
phi = hom(N, N, elem_type(N)[]; check=false)
pushfirst!(C.maps, phi)
continue
end

K, inc = kernel(map(C, i))
nz = findall(!is_zero, gens(K))
F = FreeMod(R, length(nz))
phi = hom(F, C[i], iszero(length(nz)) ? elem_type(C[i])[] : inc.(gens(K)[nz]); check=false)
pushfirst!(C.maps, phi)
if length != 0 && abs(i - min_index) + 1 >= length
C.complete = false
break

Check warning on line 572 in src/Modules/UngradedModules/FreeResolutions.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/FreeResolutions.jl#L570-L572

Added lines #L570 - L572 were not covered by tests
end
end
return first(C.maps)
end
return p
return FreeResolution(p)
end


Expand Down
12 changes: 12 additions & 0 deletions src/Modules/UngradedModules/Methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@
return F.default_ordering::ModuleOrdering{typeof(F)}
end

function default_ordering(F::FreeMod{T}) where {T<:Union{ZZRingElem, FieldElem}}
if !isdefined(F, :default_ordering)
if iszero(F)
F.default_ordering = ModuleOrdering(F, Orderings.ModOrdering(Int[], :lex))

Check warning on line 247 in src/Modules/UngradedModules/Methods.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/Methods.jl#L244-L247

Added lines #L244 - L247 were not covered by tests
else
F.default_ordering = lex(gens(F))

Check warning on line 249 in src/Modules/UngradedModules/Methods.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/Methods.jl#L249

Added line #L249 was not covered by tests
end
end
return F.default_ordering::ModuleOrdering{typeof(F)}

Check warning on line 252 in src/Modules/UngradedModules/Methods.jl

View check run for this annotation

Codecov / codecov/patch

src/Modules/UngradedModules/Methods.jl#L252

Added line #L252 was not covered by tests
end


##############################
#TODO: move to Singular.jl ?

Expand Down
Loading
Loading