Skip to content

Commit a883365

Browse files
committed
Faster perm(::Vector{Int}) & application of permutations
Before: julia> make_test_data(n) = (symmetric_group(n), vcat([n],1:n-1)); julia> testit(pi) = [ i^pi for i in 1:degree(pi) ]; julia> G, L = make_test_data(1000); pi = G(L); @b testit(pi) 8.222 μs (492 allocs: 15.703 KiB) julia> G, L = make_test_data(10000); pi = G(L); @b testit(pi) 98.916 μs (9492 allocs: 244.328 KiB) julia> G, L = make_test_data(100000); pi = G(L); @b testit(pi) 1.069 ms (99492 allocs: 2.299 MiB) julia> G, L = make_test_data(1000); @b $G($L) 12.834 μs (4 allocs: 10.016 KiB) julia> G, L = make_test_data(10000); @b $G($L) 124.500 μs (4 allocs: 97.906 KiB) julia> G, L = make_test_data(100000); @b $G($L) 1.235 ms (4 allocs: 1.145 MiB) After: julia> make_test_data(n) = (symmetric_group(n), vcat([n],1:n-1)); julia> testit(pi) = [ i^pi for i in 1:degree(pi) ]; julia> G, L = make_test_data(1000); pi = G(L); @b testit(pi) 1.041 μs (3 allocs: 8.062 KiB) julia> G, L = make_test_data(10000); pi = G(L); @b testit(pi) 9.833 μs (3 allocs: 96.062 KiB) julia> G, L = make_test_data(100000); pi = G(L); @b testit(pi) 104.625 μs (3 allocs: 800.062 KiB) julia> G, L = make_test_data(1000); @b $G($L) 1.905 μs (2 allocs: 2.000 KiB) julia> G, L = make_test_data(10000); @b $G($L) 16.458 μs (2 allocs: 19.641 KiB) julia> G, L = make_test_data(100000); @b $G($L) 159.458 μs (2 allocs: 390.766 KiB)
1 parent 2819c8d commit a883365

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

src/Groups/perm.jl

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,24 +323,51 @@ true
323323
```
324324
"""
325325
function perm(g::PermGroup, L::AbstractVector{<:IntegerUnion})
326-
x = GAPWrap.PermList(GapObj(L;recursive=true))
326+
x = _make_gap_perm(degree(g), L)
327327
@req x !== GAP.Globals.fail "the list does not describe a permutation"
328-
@req (length(L) <= degree(g) && x in GapObj(g)) "the element does not embed in the group"
328+
@req x in GapObj(g) "the element does not embed in the group"
329329
return PermGroupElem(g, x)
330330
end
331331

332332
perm(g::PermGroup, L::AbstractVector{<:ZZRingElem}) = perm(g, [Int(y) for y in L])
333333

334334
function (g::PermGroup)(L::AbstractVector{<:IntegerUnion})
335-
x = GAPWrap.PermList(GapObj(L;recursive=true))
336-
@req (length(L) <= degree(g) && x in GapObj(g)) "the element does not embed in the group"
335+
x = _make_gap_perm(degree(g), L)
336+
@req x in GapObj(g) "the element does not embed in the group"
337337
return PermGroupElem(g, x)
338338
end
339339

340+
function _make_gap_perm(deg::Int, L::Vector{<:IntegerUnion})
341+
@req 0 <= deg "degree is $deg, must be non-negative"
342+
@req deg < 2^32 "degree is $deg, must be less than 2^32"
343+
@req length(L) <= deg "length(L) = $(length(L)) exceeds degree bound $deg"
344+
if deg < 2^16
345+
x = _make_gap_perm(UInt16, deg, L)
346+
else
347+
x = _make_gap_perm(UInt32, deg, L)
348+
end
349+
return x
350+
end
351+
352+
function _make_gap_perm(::Type{T}, deg::Int, L::Vector{<:IntegerUnion}) where {T <: Union{UInt16, UInt32}}
353+
if T === UInt16
354+
perm = @ccall GAP.libgap.NEW_PERM2(deg::Cint)::GapObj
355+
else
356+
perm = @ccall GAP.libgap.NEW_PERM4(deg::Cint)::GapObj
357+
end
358+
addr = Ptr{T}(GAP.ADDR_OBJ(perm)+8) # skip 8 byte header
359+
360+
for i in 1:length(L)
361+
unsafe_store!(addr, UInt64(L[i]) - 1, i)
362+
end
363+
for i in 1+length(L):deg
364+
unsafe_store!(addr, i - 1, i)
365+
end
366+
return perm
367+
end
368+
340369
(g::PermGroup)(L::AbstractVector{<:ZZRingElem}) = g([Int(y) for y in L])
341370

342-
# cperm stands for "cycle permutation", but we can change name if we want
343-
# takes as input a list of vectors (not necessarily disjoint)
344371
@doc raw"""
345372
cperm(L::AbstractVector{<:T}...) where T <: IntegerUnion
346373
cperm(L::AbstractVector{<:AbstractVector{T}}) where T <: IntegerUnion
@@ -501,9 +528,24 @@ Base.Vector(x::PermGroupElem, n::Int = x.parent.deg) = Vector{Int}(x,n)
501528
#evaluation function
502529
(x::PermGroupElem)(n::IntegerUnion) = n^x
503530

504-
^(n::T, x::PermGroupElem) where T <: IntegerUnion = T(GAP.Obj(n)^GapObj(x))
531+
function ^(n::T, g::PermGroupElem) where T <: IntegerUnion
532+
return fits(Int, n) ? T(Int(n), g) : n
533+
end
534+
535+
function ^(n::Int, g::PermGroupElem)
536+
@req n > 0 "permutations only act on positive integers"
537+
large = (GAP.TNUM_OBJ(GapObj(g)) == GAP.T_PERM4)
538+
deg = GAP.SIZE_OBJ(GapObj(g)) - 8 # subtract 9 byte header
539+
deg >>= large ? 2 : 1
540+
n > deg && return n
505541

506-
^(n::Int, x::PermGroupElem) = (n^GapObj(x))::Int
542+
addr = GAP.ADDR_OBJ(GapObj(g))+8 # skip 8 byte header
543+
if large
544+
return Int(unsafe_load(Ptr{UInt32}(addr), n))+1
545+
else
546+
return Int(unsafe_load(Ptr{UInt16}(addr), n))+1
547+
end
548+
end
507549

508550

509551
@doc raw"""

test/Groups/gsets.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
@test unwrap(x) == omega
136136
@test unwrap(omega) == omega
137137
@test x^g == Omega(permuted(omega, g)) # action via `^` is defined for `x`
138-
@test_throws ErrorException omega^g # ... but not for the unwrapped object
138+
@test_throws ArgumentError omega^g # ... but not for the unwrapped object
139139
orb = orbit(x)
140140
@test length(orb) == 6
141141
@test isa(orb, GSet)

0 commit comments

Comments
 (0)