diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index d59f18268e..7dafa5ba65 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -223,6 +223,10 @@ function check_parent(a, b, throw::Bool = true) return flag end +function check_base_ring(a, b) + base_ring(a) === base_ring(b) || error("base rings do not match") +end + include("algorithms/LaurentPoly.jl") include("algorithms/FinField.jl") include("algorithms/GenericFunctions.jl") diff --git a/src/ConcreteTypes.jl b/src/ConcreteTypes.jl index b1dbdc4740..0c16f5a7ca 100644 --- a/src/ConcreteTypes.jl +++ b/src/ConcreteTypes.jl @@ -1,9 +1,10 @@ ############################################################################### # -# Mat space +# Parent types # ############################################################################### +# parent type for matrices struct MatSpace{T <: NCRingElement} <: Module{T} base_ring::NCRing nrows::Int @@ -16,3 +17,10 @@ struct MatSpace{T <: NCRingElement} <: Module{T} return new{T}(R, r, c) end end + +# parent type for two-sided ideals +struct DefaultIdealSet{T <: NCRingElement} <: IdealSet{T} + base_ring::NCRing + + DefaultIdealSet(R::S) where {S <: NCRing} = new{elem_type(S)}(R) +end diff --git a/src/Ideal.jl b/src/Ideal.jl index e2f7f60a97..4cc9cd4540 100644 --- a/src/Ideal.jl +++ b/src/Ideal.jl @@ -1,14 +1,54 @@ ############################################################################### # -# Ideal constructor +# Ideal.jl : Generic functionality for two-sided ideals +# +# +# A the very least, an implementation of an Ideal subtype should provide the +# following methods: +# - ideal_type(::Type{MyRingType}) = MyIdealType +# - ideal(R::MyRingType, xs::Vector{MyRingElemType})::MyIdealType +# - base_ring(I::MyIdealType) +# - gen(I::MyIdealType, k::Int) +# - gens(I::MyIdealType) +# - ngens(I::MyIdealType) +# - Base.in(v::MyRingElemType, I::MyIdealType) +# - ... +# +# Many other functions are then automatically derived from these. +############################################################################### + +############################################################################### +# +# Type and parent functions +# +############################################################################### + +# fundamental interface, to be documented +ideal_type(x) = ideal_type(typeof(x)) +ideal_type(T::DataType) = throw(MethodError(ideal_type, (T,))) + +# +parent(I::Ideal) = DefaultIdealSet(base_ring(I)) + +parent_type(::Type{<:Ideal{T}}) where {T} = DefaultIdealSet{T} + +# +base_ring(S::DefaultIdealSet) = S.base_ring::base_ring_type(S) + +base_ring_type(::Type{<:IdealSet{T}}) where {T} = parent_type(T) + +elem_type(::Type{<:IdealSet{T}}) where {T} = ideal_type(parent_type(T)) + +############################################################################### +# +# Ideal constructors # ############################################################################### -# We assume that the function -# ideal(R::T, xs::Vector{U}) -# with U === elem_type(T) is implemented by anyone implementing ideals -# for AbstractAlgebra rings. -# The functions in this file extend the interface for `ideal`. +# All constructors ultimately delegate to a method +# ideal(R::T, xs::Vector{U}) where T <: NCRing +# and U === elem_type(T) +ideal(R::T, xs::Vector{S}) where {T <: NCRing, S <: NCRingElement} = ideal_type(T)(R, xs) # the following helper enables things like `ideal(R, [])` or `ideal(R, [1])` # the type check ensures we don't run into an infinite recursion @@ -21,6 +61,77 @@ function ideal(R::NCRing, x, y...; kw...) return ideal(R, elem_type(R)[R(z) for z in [x, y...]]; kw...) end +function ideal(x::NCRingElement; kw...) + return ideal(parent(x), x; kw...) +end + +function ideal(xs::AbstractVector{T}; kw...) where T<:NCRingElement + @req !is_empty(xs) "Empty collection, cannot determine parent ring. Try ideal(ring, xs) instead of ideal(xs)" + return ideal(parent(xs[1]), xs; kw...) +end + +function Base.similar(I::T, xs::Vector) where {T <: Ideal} + R = base_ring(I) + @assert T === ideal_type(R) + return ideal(R, xs) +end + +############################################################################### +# +# Basic predicates +# +############################################################################### + +iszero(I::Ideal) = all(iszero, gens(I)) + +@doc raw""" + Base.issubset(I::T, J::T) where {T <: Ideal} + +Return `true` if the ideal `I` is a subset of the ideal `J`. +""" +function Base.issubset(I::T, J::T) where {T <: Ideal} + I === J && return true + check_base_ring(I, J) + return all(in(J), gens(I)) +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function Base.:(==)(I::T, J::T) where {T <: Ideal} + return is_subset(I, J) && is_subset(J, I) +end + +function Base.:hash(I::T, h::UInt) where {T <: Ideal} + h = hash(base_ring(I), h) + return h +end + +############################################################################### +# +# Binary operations +# +############################################################################### + +function Base.:+(I::T, J::T) where {T <: Ideal} + check_base_ring(I, J) + return similar(I, vcat(gens(I), gens(J))) +end + +function Base.:*(I::T, J::T) where {T <: Ideal} + check_base_ring(I, J) + return similar(I, [x*y for x in gens(I) for y in gens(J)]) +end + +############################################################################### +# +# Ad hoc binary operations +# +############################################################################### + function *(R::Ring, x::RingElement) return ideal(R, x) end @@ -29,19 +140,20 @@ function *(x::RingElement, R::Ring) return ideal(R, x) end -function ideal(x::NCRingElement; kw...) - return ideal(parent(x), x; kw...) +function *(I::Ideal{T}, p::T) where T <: RingElement + iszero(p) && return similar(I, T[]) + return similar(I, [v*p for v in gens(I)]) end -function ideal(xs::AbstractVector{T}; kw...) where T<:NCRingElement - @req !is_empty(xs) "Empty collection, cannot determine parent ring. Try ideal(ring, xs) instead of ideal(xs)" - return ideal(parent(xs[1]), xs; kw...) +function *(p::T, I::Ideal{T}) where T <: RingElement + return I*p end -iszero(I::Ideal) = all(iszero, gens(I)) - -base_ring_type(::Type{<:IdealSet{T}}) where T <: RingElement = parent_type(T) +function *(I::Ideal{T}, p::S) where {S <: RingElement, T <: RingElement} + iszero(p*one(R)) && return similar(I, T[]) + return similar(I, [v*p for v in gens(I)]) +end -# fundamental interface, to be documented -ideal_type(x) = ideal_type(typeof(x)) -ideal_type(T::DataType) = throw(MethodError(ideal_type, (T,))) +function *(p::S, I::Ideal{T}) where {S <: RingElement, T <: RingElement} + return I*p +end diff --git a/src/generic/Ideal.jl b/src/generic/Ideal.jl index bede8b7db1..0363f06fc5 100644 --- a/src/generic/Ideal.jl +++ b/src/generic/Ideal.jl @@ -34,7 +34,11 @@ parent_type(::Type{Ideal{S}}) where S <: RingElement = IdealSet{S} Return a list of generators of the ideal `I` in reduced form and canonicalised. """ -gens(I::Ideal{T}) where T <: RingElement = I.gens +gens(I::Ideal) = I.gens + +number_of_generators(I::Ideal) = length(I.gens) + +gen(I::Ideal, i::Int) = I.gens[i] ############################################################################### # @@ -2094,17 +2098,7 @@ end ############################################################################### # -# Comparison -# -############################################################################### - -function ==(I::Ideal{T}, J::Ideal{T}) where T <: RingElement - return gens(I) == gens(J) -end - -############################################################################### -# -# Containment +# Membership # ############################################################################### @@ -2112,15 +2106,6 @@ function Base.in(v::T, I::Ideal{T}) where T <: RingElement return is_zero(normal_form(v, I)) end -@doc raw""" - Base.issubset(I::Ideal{T}, J::Ideal{T}) where T <: RingElement - -Return `true` if the ideal `I` is a subset of the ideal `J`. -""" -function Base.issubset(I::Ideal{T}, J::Ideal{T}) where T <: RingElement - return all(in(J), gens(I)) -end - ############################################################################### # # Intersection @@ -2204,61 +2189,6 @@ function intersect(I::Ideal{T}, J::Ideal{T}) where {U <: RingElement, T <: Abstr return Ideal(S, GInt) end -############################################################################### -# -# Binary operations -# -############################################################################### - -function +(I::Ideal{T}, J::Ideal{T}) where T <: RingElement - R = base_ring(I) - G1 = gens(I) - G2 = gens(J) - return Ideal(R, vcat(G1, G2)) -end - -function *(I::Ideal{T}, J::Ideal{T}) where T <: RingElement - R = base_ring(I) - G1 = gens(I) - G2 = gens(J) - return Ideal(R, [v*w for v in G1 for w in G2]) -end - -############################################################################### -# -# Ad hoc binary operations -# -############################################################################### - -function *(I::Ideal{T}, p::T) where T <: RingElement - R = base_ring(I) - G = gens(I) - if iszero(p) - return Ideal(R, T[]) - end - p = divexact(p, canonical_unit(p)) - return Ideal(R, [v*p for v in G]) -end - -function *(p::T, I::Ideal{T}) where T <: RingElement - return I*p -end - -function *(I::Ideal{T}, p::S) where {S <: RingElement, T <: RingElement} - R = base_ring(I) - G = gens(I) - if iszero(p*one(R)) - return Ideal(R, T[]) - end - V = [v*p for v in G] - V = [divexact(v, canonical_unit(v)) for v in V] - return Ideal(R, V) -end - -function *(p::S, I::Ideal{T}) where {S <: RingElement, T <: RingElement} - return I*p -end - ############################################################################### # # Ideal reduction in Euclidean domain @@ -2310,7 +2240,9 @@ function Ideal(R::Ring, v::T, vs::T...) where T <: RingElement end Ideal(R::Ring) = Ideal(R, elem_type(R)[]) -Ideal(R::Ring, V::Vector{Any}) = Ideal(R, elem_type(R)[R(v) for v in V]) +Ideal(R::Ring, V::Vector) = Ideal(R, elem_type(R)[R(v) for v in V]) + +Base.similar(I::Ideal, V::Vector) = Ideal(base_ring(I), V) ############################################################################### #