Skip to content

Commit 1a92bf0

Browse files
committed
Expand the common code for ideal implementations
This includes the addition of a `DefaultIdealSet` struct type which can be used with zero overhead, and hopefully can simply be used as parent type for all ideal implementations (in Hecke, Oscar or wherever) down the road. Also semi-documented the interfaces to be implemented (the list is surely incomplete but better than nothing).
1 parent 3abc3d0 commit 1a92bf0

File tree

4 files changed

+138
-39
lines changed

4 files changed

+138
-39
lines changed

src/AbstractAlgebra.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ function check_parent(a, b, throw::Bool = true)
223223
return flag
224224
end
225225

226+
function check_base_ring(a, b)
227+
base_ring(a) === base_ring(b) || error("base rings do not match")
228+
end
229+
226230
include("algorithms/LaurentPoly.jl")
227231
include("algorithms/FinField.jl")
228232
include("algorithms/GenericFunctions.jl")

src/ConcreteTypes.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
###############################################################################
22
#
3-
# Mat space
3+
# Parent types
44
#
55
###############################################################################
66

7+
# parent type for matrices
78
struct MatSpace{T <: NCRingElement} <: Module{T}
89
base_ring::NCRing
910
nrows::Int
@@ -16,3 +17,10 @@ struct MatSpace{T <: NCRingElement} <: Module{T}
1617
return new{T}(R, r, c)
1718
end
1819
end
20+
21+
# parent type for two-sided ideals
22+
struct DefaultIdealSet{T <: NCRingElement} <: IdealSet{T}
23+
base_ring::NCRing
24+
25+
DefaultIdealSet(R::S) where {S <: NCRing} = new{elem_type(S)}(R)
26+
end

src/Ideal.jl

Lines changed: 120 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
11
###############################################################################
22
#
3-
# Ideal constructor
3+
# Ideal.jl : Generic functionality for two-sided ideals
4+
#
5+
#
6+
# A the very least, an implementation of an Ideal subtype should provide the
7+
# following methods:
8+
# - ideal_type(::Type{MyRingType}) = MyIdealType
9+
# - ideal(R::MyRingType, xs::Vector{MyRingElemType})::MyIdealType
10+
# - base_ring(I::MyIdealType)
11+
# - gen(I::MyIdealType, k::Int)
12+
# - gens(I::MyIdealType)
13+
# - ngens(I::MyIdealType)
14+
# - Base.in(v::MyRingElemType, I::MyIdealType)
15+
# - ...
16+
#
17+
# Many other functions are then automatically derived from these.
18+
###############################################################################
19+
20+
###############################################################################
21+
#
22+
# Type and parent functions
23+
#
24+
###############################################################################
25+
26+
# fundamental interface, to be documented
27+
ideal_type(x) = ideal_type(typeof(x))
28+
ideal_type(T::DataType) = throw(MethodError(ideal_type, (T,)))
29+
30+
#
31+
parent(I::Ideal) = DefaultIdealSet(base_ring(I))
32+
33+
parent_type(::Type{<:Ideal{T}}) where {T} = DefaultIdealSet{T}
34+
35+
#
36+
base_ring(S::DefaultIdealSet) = S.base_ring::base_ring_type(S)
37+
38+
base_ring_type(::Type{<:IdealSet{T}}) where {T} = parent_type(T)
39+
40+
elem_type(::Type{<:IdealSet{T}}) where {T} = ideal_type(parent_type(T))
41+
42+
###############################################################################
43+
#
44+
# Ideal constructors
445
#
546
###############################################################################
647

7-
# We assume that the function
8-
# ideal(R::T, xs::Vector{U})
9-
# with U === elem_type(T) is implemented by anyone implementing ideals
10-
# for AbstractAlgebra rings.
11-
# The functions in this file extend the interface for `ideal`.
48+
# All constructors ultimately delegate to a method
49+
# ideal(R::T, xs::Vector{U}) where T <: NCRing
50+
# and U === elem_type(T)
51+
ideal(R::T, xs::Vector{S}) where {T <: NCRing, S <: NCRingElement} = ideal_type(T)(R, xs)
1252

1353
# the following helper enables things like `ideal(R, [])` or `ideal(R, [1])`
1454
# the type check ensures we don't run into an infinite recursion
@@ -21,6 +61,66 @@ function ideal(R::NCRing, x, y...; kw...)
2161
return ideal(R, elem_type(R)[R(z) for z in [x, y...]]; kw...)
2262
end
2363

64+
function ideal(x::NCRingElement; kw...)
65+
return ideal(parent(x), x; kw...)
66+
end
67+
68+
function ideal(xs::AbstractVector{T}; kw...) where T<:NCRingElement
69+
@req !is_empty(xs) "Empty collection, cannot determine parent ring. Try ideal(ring, xs) instead of ideal(xs)"
70+
return ideal(parent(xs[1]), xs; kw...)
71+
end
72+
73+
###############################################################################
74+
#
75+
# Basic predicates
76+
#
77+
###############################################################################
78+
79+
iszero(I::Ideal) = all(iszero, gens(I))
80+
81+
function is_subset(I::T, J::T) where {T <: Ideal}
82+
I === J && return true
83+
check_base_ring(I, J)
84+
return all(x in J for x in gens(I))
85+
end
86+
87+
###############################################################################
88+
#
89+
# Comparison
90+
#
91+
###############################################################################
92+
93+
function Base.:(==)(I::T, J::T) where {T <: Ideal}
94+
return is_subset(I, J) && is_subset(J, I)
95+
end
96+
97+
function Base.:hash(I::T, h::UInt) where {T <: Ideal}
98+
h = hash(base_ring(I), h)
99+
return h
100+
end
101+
102+
###############################################################################
103+
#
104+
# Binary operations
105+
#
106+
###############################################################################
107+
108+
function Base.:+(I::T, J::T) where {T <: Ideal}
109+
check_base_ring(I, J)
110+
return ideal(base_ring(I), vcat(gens(I), gens(J)))
111+
end
112+
113+
function Base.:*(I::T, J::T) where {T <: Ideal}
114+
check_base_ring(I, J)
115+
return ideal(base_ring(I), [x*y for x in gens(I) for y in gens(J)])
116+
end
117+
118+
###############################################################################
119+
#
120+
# Ad hoc binary operations
121+
#
122+
###############################################################################
123+
24124
function *(R::Ring, x::RingElement)
25125
return ideal(R, x)
26126
end
@@ -29,19 +129,22 @@ function *(x::RingElement, R::Ring)
29129
return ideal(R, x)
30130
end
31131

32-
function ideal(x::NCRingElement; kw...)
33-
return ideal(parent(x), x; kw...)
132+
function *(I::Ideal{T}, p::T) where T <: RingElement
133+
R = base_ring(I)
134+
iszero(p) && return ideal(R, T[])
135+
return ideal(R, [v*p for v in gens(I)])
34136
end
35137

36-
function ideal(xs::AbstractVector{T}; kw...) where T<:NCRingElement
37-
@req !is_empty(xs) "Empty collection, cannot determine parent ring. Try ideal(ring, xs) instead of ideal(xs)"
38-
return ideal(parent(xs[1]), xs; kw...)
138+
function *(p::T, I::Ideal{T}) where T <: RingElement
139+
return I*p
39140
end
40141

41-
iszero(I::Ideal) = all(iszero, gens(I))
42-
43-
base_ring_type(::Type{<:IdealSet{T}}) where T <: RingElement = parent_type(T)
142+
function *(I::Ideal{T}, p::S) where {S <: RingElement, T <: RingElement}
143+
R = base_ring(I)
144+
iszero(p*one(R)) && return ideal(R, T[])
145+
return ideal(R, [v*p for v in gens(I)])
146+
end
44147

45-
# fundamental interface, to be documented
46-
ideal_type(x) = ideal_type(typeof(x))
47-
ideal_type(T::DataType) = throw(MethodError(ideal_type, (T,)))
148+
function *(p::S, I::Ideal{T}) where {S <: RingElement, T <: RingElement}
149+
return I*p
150+
end

src/generic/Ideal.jl

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ parent_type(::Type{Ideal{S}}) where S <: RingElement = IdealSet{S}
3434
3535
Return a list of generators of the ideal `I` in reduced form and canonicalised.
3636
"""
37-
gens(I::Ideal{T}) where T <: RingElement = I.gens
37+
gens(I::Ideal) = I.gens
38+
39+
number_of_generators(I::Ideal) = length(I.gens)
40+
41+
gen(I::Ideal, i::Int) = I.gens[i]
3842

3943
###############################################################################
4044
#
@@ -2204,26 +2208,6 @@ function intersect(I::Ideal{T}, J::Ideal{T}) where {U <: RingElement, T <: Abstr
22042208
return Ideal(S, GInt)
22052209
end
22062210

2207-
###############################################################################
2208-
#
2209-
# Binary operations
2210-
#
2211-
###############################################################################
2212-
2213-
function +(I::Ideal{T}, J::Ideal{T}) where T <: RingElement
2214-
R = base_ring(I)
2215-
G1 = gens(I)
2216-
G2 = gens(J)
2217-
return Ideal(R, vcat(G1, G2))
2218-
end
2219-
2220-
function *(I::Ideal{T}, J::Ideal{T}) where T <: RingElement
2221-
R = base_ring(I)
2222-
G1 = gens(I)
2223-
G2 = gens(J)
2224-
return Ideal(R, [v*w for v in G1 for w in G2])
2225-
end
2226-
22272211
###############################################################################
22282212
#
22292213
# Ad hoc binary operations

0 commit comments

Comments
 (0)