Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move code from Oscar.jl/experimental/GModule/Misc.jl here #1675

Merged
merged 5 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions src/Map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ function domain end
function codomain end
function image_fn end

function coimage(h::Map)
return quo(domain(h), kernel(h)[1])
end

function check_composable(a::Map, b::Map)
codomain(a) !== domain(b) && error("Incompatible maps")
end
Expand Down
3 changes: 3 additions & 0 deletions src/Matrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ end
Base.IteratorSize(::Type{<:MatrixElem}) = Base.HasShape{2}()
Base.IteratorEltype(::Type{<:MatrixElem}) = Base.HasEltype() # default

Base.pairs(M::MatElem) = Base.pairs(IndexCartesian(), M)
Base.pairs(::IndexCartesian, M::MatElem) = Base.Iterators.Pairs(M, CartesianIndices(axes(M)))

###############################################################################
#
# Block replacement
Expand Down
49 changes: 49 additions & 0 deletions src/Module.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#
###############################################################################

Base.eltype(M::FPModule{T}) where T <: FinFieldElem = elem_type(M)

function zero(M::FPModule{T}) where T <: RingElement
R = base_ring(M)
return M(zero_matrix(R, 1, ngens(M)))
Expand All @@ -27,6 +29,23 @@ function check_parent(M::FPModuleElem{T}, N::FPModuleElem{T}) where T <: RingEle
parent(M) !== parent(N) && error("Incompatible modules")
end

gen(M::FPModule, i::Int) = M[i]
fingolfin marked this conversation as resolved.
Show resolved Hide resolved

fingolfin marked this conversation as resolved.
Show resolved Hide resolved
is_finite(M::FPModule{<:FinFieldElem}) = true

function is_sub_with_data(M::FPModule{T}, N::FPModule{T}) where T <: RingElement
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe rename to is_sub_with_inclusion?

fl = is_submodule(N, M)
if fl
return fl, hom(M, N, elem_type(N)[N(m) for m = gens(M)])
else
return fl, hom(M, N, elem_type(N)[zero(N) for m = gens(M)])
end
end

Base.issubset(M::FPModule{T}, N::FPModule{T}) where T <: RingElement = is_submodule(M, N)

order(M::FPModule{<:FinFieldElem}) = order(base_ring(M))^dim(M)

###############################################################################
#
# Unary operators
Expand Down Expand Up @@ -300,3 +319,33 @@ function rand(rng::AbstractRNG, M::FPModule{T}, vals...) where T <: RingElement
end

rand(M::FPModule, vals...) = rand(Random.GLOBAL_RNG, M, vals...)

###############################################################################
#
# Iteration
#
###############################################################################

Base.length(M::FPModule{T}) where T <: FinFieldElem = Int(order(M))

function Base.iterate(M::FPModule{T}) where T <: FinFieldElem
k = base_ring(M)
if dim(M) == 0
return zero(M), iterate([1])
end
p = Base.Iterators.ProductIterator(Tuple([k for i=1:dim(M)]))
f = iterate(p)
return M(elem_type(k)[f[1][i] for i=1:dim(M)]), (f[2], p)
end

function Base.iterate(M::FPModule{T}, st::Tuple{<:Tuple, <:Base.Iterators.ProductIterator}) where T <: FinFieldElem
n = iterate(st[2], st[1])
if n === nothing
return n
end
return M(elem_type(base_ring(M))[n[1][i] for i=1:dim(M)]), (n[2], st[2])
end

function Base.iterate(::FPModule{<:FinFieldElem}, ::Tuple{Int64, Int64})
return nothing
end
2 changes: 2 additions & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export is_even
export is_exact_type
export is_finite
export is_finite_order
export is_free
export is_gen
export is_hermitian
export is_hessenberg
Expand Down Expand Up @@ -308,6 +309,7 @@ export is_square_with_sqrt
export is_squarefree
export is_submodule
export is_subset
export is_sub_with_data
export is_symmetric
export is_term
export is_term_recursive
Expand Down
9 changes: 9 additions & 0 deletions src/generic/DirectSum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ base_ring(N::DirectSumModule{T}) where T <: RingElement = base_ring(N.m[1])

base_ring(v::DirectSumModuleElem{T}) where T <: RingElement = base_ring(v.parent)

is_free(M::DirectSumModule) = all(is_free, M.m)
Copy link
Member

Choose a reason for hiding this comment

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

Actually, isn't this mathematically wrong? That is: if all summands are free, then M is free. But M might be free even though its summands are not (keyword: projective modules, etc.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@fieker In which situation was is_free(::DirectSumModule) missing?

Copy link
Member

Choose a reason for hiding this comment

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

@thofma points out:

If it is only applied to finitely generated/presented modules over fields or PIDs, then the method is correct: $M \oplus N$ is free if and only if $M$ and $N$ are free.

This is of course true, but is there a way we can use this knowledge to improve the code above? I mean we could certainly restrict it to modules over fields; or over AA "integers", and AA univariates polynomials over fields, and perhaps a few more. But we can't do this generally here (e.g. not for fields over Nemo.ZZRing). But maybe we don't, it depends on what the application here is?

Copy link
Member

Choose a reason for hiding this comment

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

No, but it is the same for all the functionality of things involving FPModule.

Copy link
Member

Choose a reason for hiding this comment

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

Aha. The documentation indeed says:

# Free Modules and Vector Spaces

AbstractAlgebra allows the construction of free modules of any rank over any
Euclidean ring and the vector space of any dimension over a field. 

but that's the only place in docs/src/free_module.md mentioning Euclidean
and it then proceeds to say

AbstractAlgebra provides generic types for free modules and vector spaces,
via the type `FreeModule{T}` for free modules, where `T`
is the type of the elements of the ring $R$ over which the module is built.

which to makes it not at all clear whether or not e.g. FreeModule{T} is only supposed to be used over Euclidean rings.

And docs/src/direct_sum.md does not mention Euclidean/principal/PID at all.

But docs/src/module.md then goes on to say this

The generic code provided by AbstractAlgebra will only work for modules over
euclidean domains.

which ought to settle it, except it then proceeds in the very next sentence to say

Free modules can be built over both commutative and noncommutative rings. Other
types of module are restricted to fields and euclidean rings.

which again leaves it completely unclear to me what is supposed to work in which case or not.

I think adding an is_free method that knowingly returns wrong results sometimes is problematic. At the very least its docstring then should contain a big warning?

Better would be if we could just restrict it -- e.g. if this only for direct sums over integers or fields it'd be fine. But that begs the question where is even used -- @fieker?

Alternatively I could also live with such a method:

function is_free(M::DirectSumModule)
  f = all(is_free, M.m)
  @req f || is_euclidean(base_ring(M)) "is_free for direct sums over non-euclidean domains not supported"
  return f
end

is_euclidean(::Field) = true
is_euclidean(::ZZRing) = true
is_euclidean(::PolyRing{<:Field}) = true
is_euclidean(::Ring) = error("cannot tell if this ring is Euclidean")


fingolfin marked this conversation as resolved.
Show resolved Hide resolved
dim(M::DirectSumModule{<:FieldElem}) = sum(dim(x) for x = M.m)

number_of_generators(N::DirectSumModule{T}) where T <: RingElement = sum(ngens(M) for M in N.m)

gens(N::DirectSumModule{T}) where T <: RingElement = [gen(N, i) for i = 1:ngens(N)]
Expand Down Expand Up @@ -220,6 +224,11 @@ function direct_sum(m::Vector{<:AbstractAlgebra.FPModule{T}}) where T <: RingEle
return M, inj, pro
end

function direct_sum(M::DirectSumModule{T}, N::DirectSumModule{T}, mp::Vector{ModuleHomomorphism{T}}) where T
Copy link
Collaborator

Choose a reason for hiding this comment

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

this function should have hom in its name. In Oscar, we call the similar function that lifts homs to a tensor product hom_tensor. For direct sums, there is the equivalent for Lie algebra modules as hom_direct_sum, but we can adapt the name of that in Oscar to match this one here. But IMO it should start with hom_*

@assert length(M.m) == length(mp) == length(N.m)
return hom(M, N, cat(map(matrix, mp)..., dims = (1,2)))
end

function ModuleHomomorphism(D::DirectSumModule{T}, A::AbstractAlgebra.FPModule{T}, m::Vector{<:ModuleHomomorphism{T}}) where T <: RingElement
S = summands(D)
length(S) == length(m) || error("map array has wrong length")
Expand Down
2 changes: 2 additions & 0 deletions src/generic/FreeModule.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ function check_parent(m1::FreeModuleElem{T}, m2::FreeModuleElem{T}) where T <: U
parent(m1) !== parent(m2) && error("Incompatible free modules")
end

is_free(M::FreeModule) = true

@doc raw"""
rank(M::FreeModule{T}) where T <: Union{RingElement, NCRingElem}

Expand Down
2 changes: 2 additions & 0 deletions src/generic/Map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ end

Base.inv(f::AbstractAlgebra.Map(AbstractAlgebra.IdentityMap)) = f

matrix(phi::IdentityMap{<:AbstractAlgebra.FPModule}) = identity_matrix(base_ring(domain(phi)), dim(domain(phi)))

################################################################################
#
# FunctionalMap
Expand Down
5 changes: 5 additions & 0 deletions src/generic/Misc/Poly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ function roots(R::Field, f::PolyRingElem)
return roots(f1)
end

function roots(a::FinFieldElem, i::Int)
_, x = polynomial_ring(parent(a), cached = false)
return roots(x^i-a)
end

function sturm_sequence(f::PolyRingElem{<:FieldElem})
g = f
h = derivative(g)
Expand Down
53 changes: 53 additions & 0 deletions src/generic/ModuleHomomorphism.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,44 @@ inverse_mat(f::Map(ModuleIsomorphism)) = f.inverse_matrix

inverse_image_fn(f::Map(ModuleIsomorphism)) = f.inverse_image_fn

###############################################################################
#
# Unary operators
#
###############################################################################

Base.:-(a::ModuleHomomorphism) = hom(domain(a), codomain(a), -matrix(a))

###############################################################################
#
# Binary operators
#
###############################################################################

Base.:*(a::T, b::ModuleHomomorphism{T}) where {T} = hom(domain(b), codomain(b), a * matrix(b))
Base.:*(a::T, b::ModuleIsomorphism{T}) where {T} = hom(domain(b), codomain(b), a * matrix(b))
Base.:+(a::ModuleHomomorphism, b::ModuleHomomorphism) = hom(domain(a), codomain(a), matrix(a) + matrix(b))
Base.:-(a::ModuleHomomorphism, b::ModuleHomomorphism) = hom(domain(a), codomain(a), matrix(a) - matrix(b))

###############################################################################
#
# Comparison
#
###############################################################################

function Base.:(==)(a::Union{ModuleHomomorphism, ModuleIsomorphism}, b::Union{ModuleHomomorphism, ModuleIsomorphism})
domain(a) === domain(b) || return false
codomain(a) === codomain(b) || return false
return matrix(a) == matrix(b)
end

function Base.hash(a::Union{ModuleHomomorphism, ModuleIsomorphism}, h::UInt)
h = hash(domain(a), h)
h = hash(codomain(a), h)
h = hash(matrix(a), h)
return h
end

###############################################################################
#
# String I/O
Expand Down Expand Up @@ -68,6 +106,10 @@ function Base.inv(f::Map(ModuleIsomorphism))
return ModuleIsomorphism{T}(codomain(f), domain(f), inverse_mat(f), matrix(f))
end

function Base.inv(f::ModuleHomomorphism)
return hom(codomain(f), domain(f), inv(matrix(f)))
end

###############################################################################
#
# Call overload
Expand Down Expand Up @@ -139,3 +181,14 @@ function ModuleIsomorphism(M1::AbstractAlgebra.FPModule{T},
end
return ModuleIsomorphism{T}(M1, M2, M, M_inv)
end

function hom(V::AbstractAlgebra.Module, W::AbstractAlgebra.Module, v::Vector{<:ModuleElem}; check::Bool = true)
if ngens(V) == 0
return ModuleHomomorphism(V, W, zero_matrix(base_ring(V), ngens(V), ngens(W)))
end
return ModuleHomomorphism(V, W, reduce(vcat, [x.v for x = v]))
end

function hom(V::AbstractAlgebra.Module, W::AbstractAlgebra.Module, v::MatElem; check::Bool = true)
return ModuleHomomorphism(V, W, v)
end
1 change: 1 addition & 0 deletions src/generic/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export inverse_mat
export invmod
export is_compatible
export is_divisible_by
export is_free
export is_homogeneous
export is_power
export is_rimhook
Expand Down
Loading