Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/AbstractAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
10 changes: 9 additions & 1 deletion src/ConcreteTypes.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
###############################################################################
#
# Mat space
# Parent types
#
###############################################################################

# parent type for matrices
struct MatSpace{T <: NCRingElement} <: Module{T}
base_ring::NCRing
nrows::Int
Expand All @@ -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
146 changes: 129 additions & 17 deletions src/Ideal.jl
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)])
Copy link
Member

@thofma thofma Jun 17, 2025

Choose a reason for hiding this comment

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

This is wrong for two-sided ideals of noncommutative rings.

end

###############################################################################
#
# Ad hoc binary operations
#
###############################################################################

function *(R::Ring, x::RingElement)
return ideal(R, x)
end
Expand All @@ -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)])
Copy link
Member Author

Choose a reason for hiding this comment

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

so here, if we assume Ideal means "two sided", and also "there is one (two-sided) ideal type per ring", and also ideal(R, xs) produces that ideal type, then we can write here

Suggested change
return similar(I, [v*p for v in gens(I)])
return ideal(base_ring(I), [v*p for v in gens(I)])

and indeed that's what I did before the second commit in this PR, and I'd be happy to back to it if we can agree on this scheme.

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
86 changes: 9 additions & 77 deletions src/generic/Ideal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]

###############################################################################
#
Expand Down Expand Up @@ -2094,33 +2098,14 @@ end

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

function ==(I::Ideal{T}, J::Ideal{T}) where T <: RingElement
return gens(I) == gens(J)
end

###############################################################################
#
# Containment
# Membership
#
###############################################################################

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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

###############################################################################
#
Expand Down
Loading