Skip to content
Open
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
16 changes: 14 additions & 2 deletions src/grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ grid index => quantics
function grididx_to_quantics(g::Grid{d}, grididx::NTuple{d,Int}) where {d}
if g.unfoldingscheme === :fused
return index_to_quantics_fused(grididx, numdigits = g.R, base = g.base)
elseif g.unfoldingscheme === :serial
return fused_to_serial(
index_to_quantics_fused(grididx, numdigits = g.R, base = g.base),
d,
base = g.base,
)
elseif g.unfoldingscheme === :meander
return fused_to_meander(
index_to_quantics_fused(grididx, numdigits = g.R, base = g.base),
d,
base = g.base,
)
else
return fused_to_interleaved(
index_to_quantics_fused(grididx, numdigits = g.R, base = g.base),
Expand Down Expand Up @@ -155,7 +167,7 @@ struct InherentDiscreteGrid{d} <: Grid{d}
step::Union{NTuple{d,Int},Int} = 1,
) where {d}
_rangecheck_R(R; base = base)
unfoldingscheme in (:fused, :interleaved) ||
unfoldingscheme in (:fused, :interleaved, :serial, :meander) ||
error("Invalid unfolding scheme: $unfoldingscheme")
origin_ = origin isa Int ? ntuple(i -> origin, d) : origin
step_ = step isa Int ? ntuple(i -> step, d) : step
Expand Down Expand Up @@ -305,7 +317,7 @@ struct DiscretizedGrid{d} <: Grid{d}
includeendpoint::Bool = false,
) where {d}
_rangecheck_R(R; base = base)
unfoldingscheme in (:fused, :interleaved) ||
unfoldingscheme in (:fused, :interleaved, :serial, :meander) ||
error("Invalid unfolding scheme: $unfoldingscheme")
return new(
R,
Expand Down
171 changes: 169 additions & 2 deletions src/quantics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,118 @@ function deinterleave_dimensions!(
end

"""
Convert a fused quantics representation to an unfused quantics representation
serialize_dimensions(digitlists...)

Sequences the indices of all digitlists into one long digitlist. Use this for
quantics representation of multidimensional objects without fusing indices.
Inverse of [`deserialize_dimensions`](@ref).
"""
function serialize_dimensions(digitlists...)::Vector{Int}
results = Vector{Int}(undef, length(digitlists[1]) * length(digitlists))
return serialize_dimensions!(results, digitlists...)
end


function serialize_dimensions!(
serial_digitlist::AbstractArray{<:Integer},
digitlists...,
)
l = length(digitlists[1])
@inbounds for i in eachindex(digitlists)
copyto!(@view(serial_digitlist[(i-1)*l+1:i*l]), digitlists[i])
end
return serial_digitlist
end

"""
deserialize_dimensions(digitlist, d)

Reverses the sequencing of bits, i.e. yields digitlists for each dimension from
a long serialized digitlist. Inverse of [`serialize_dimensions`](@ref).
"""
function deserialize_dimensions(digitlist, d)
l = length(digitlist) ÷ d
return [digitlist[(i-1)*l+1:i*l] for i = 1:d]
end

function deserialize_dimensions!(
deserialized_digitlists::AbstractArray{<:AbstractArray{I}},
digitlist,
) where {I<:Integer}
d, l = length(deserialized_digitlists), length(deserialized_digitlists[1])
@inbounds for i in Base.OneTo(d)
@. deserialized_digitlists[i] = digitlist[(i-1)*l+1:i*l]
end
return deserialized_digitlists
end

"""
meander_dimensions(digitlists...)

Sequences the indices of all digitlists into one long digitlist. Use this for
quantics representation of multidimensional objects without fusing indices.
Inverse of [`demeander_dimensions`](@ref).
"""
function meander_dimensions(digitlists...)::Vector{Int}
results = Vector{Int}(undef, length(digitlists[1]) * length(digitlists))
meander_dimensions!(results, digitlists...)
return results
end


function meander_dimensions!(
meander_digitlist::AbstractArray{<:Integer},
digitlists...,
)
l = length(digitlists[1])
for (i, digits) in enumerate(digitlists)
if isodd(i)
# odd dimension index (1-based): reverse
@inbounds for j = 1:l
meander_digitlist[(i-1)*l + j] = digits[l - j + 1]
end
else
# even dimension index: normal order
copyto!(@view(meander_digitlist[(i-1)*l+1:i*l]), digits)
end
end
return meander_digitlist
end

"""
demeander_dimensions(digitlist, d)

Reverses the sequencing of bits, i.e. yields digitlists for each dimension from
a long meandered digitlist. Inverse of [`meander_dimensions`](@ref).
"""
function demeander_dimensions(digitlist, d)
result = Vector{Vector{Int}}(undef, d)
l = length(digitlist) ÷ d
@inbounds for i in Base.OneTo(d)
segment = digitlist[(i-1)*l+1:i*l]
result[i] = isodd(i) ? reverse(segment) : segment
end
return result
end

function demeander_dimensions!(
demeandered_digitlists::AbstractArray{<:AbstractArray{I}},
digitlist,
) where {I<:Integer}
d, l = length(demeandered_digitlists), length(demeandered_digitlists[1])
@inbounds for i in Base.OneTo(d)
segment = @view(digitlist[(i-1)*l+1:i*l])
if isodd(i)
demeandered_digitlists[i] = reverse(segment)
else
demeandered_digitlists[i] = Vector(segment)
end
end
return demeandered_digitlists
end

"""
Convert a fused quantics representation to an unfused quantics interleaved representation
"""
function fused_to_interleaved(
digitlist::AbstractVector{T},
Expand All @@ -113,9 +224,29 @@ function fused_to_interleaved(
)::Vector{T} where {T<:Integer}
return interleave_dimensions(unfuse_dimensions(digitlist, d; base = base)...)
end
"""
Convert a fused quantics representation to an unfused quantics serial representation
"""
function fused_to_serial(
digitlist::AbstractVector{T},
d;
base = 2,
)::Vector{T} where {T<:Integer}
return serialize_dimensions(unfuse_dimensions(digitlist, d; base = base)...)
end
"""
Convert a fused quantics representation to an unfused quantics meander representation
"""
function fused_to_meander(
digitlist::AbstractVector{T},
d;
base = 2,
)::Vector{T} where {T<:Integer}
return meander_dimensions(unfuse_dimensions(digitlist, d; base = base)...)
end

"""
Convert an unfused quantics representation to an fused quantics representation
Convert an unfused quantics interleaved representation to an fused quantics representation
"""
function interleaved_to_fused(
digitlist::AbstractVector{T};
Expand All @@ -126,6 +257,30 @@ function interleaved_to_fused(
fuse_dimensions!(fused_digitlist, deinterleave_dimensions(digitlist, d)...)
return fused_digitlist
end
"""
Convert an unfused quantics serial representation to an fused quantics representation
"""
function serial_to_fused(
digitlist::AbstractVector{T};
base::Integer = 2,
d::Int = 1,
)::Vector{T} where {T<:Integer}
fused_digitlist = Vector{T}(undef, length(digitlist) ÷ d)
fuse_dimensions!(fused_digitlist, deserialize_dimensions(digitlist, d)...)
return fused_digitlist
end
"""
Convert an unfused quantics meander representation to an fused quantics representation
"""
function meander_to_fused(
digitlist::AbstractVector{T};
base::Integer = 2,
d::Int = 1,
)::Vector{T} where {T<:Integer}
fused_digitlist = Vector{T}(undef, length(digitlist) ÷ d)
fuse_dimensions!(fused_digitlist, demeander_dimensions(digitlist, d)...)
return fused_digitlist
end

"""
function quantics_to_index_fused(
Expand Down Expand Up @@ -187,6 +342,18 @@ function quantics_to_index(
)::NTuple{d,Int} where {d}
if unfoldingscheme === :fused
return quantics_to_index_fused(digitlist, base = base, dims = dims)
elseif unfoldingscheme === :serial
return quantics_to_index_fused(
serial_to_fused(digitlist; base = base, d = d),
base = base,
dims = dims,
)
elseif unfoldingscheme === :meander
return quantics_to_index_fused(
meander_to_fused(digitlist; base = base, d = d),
base = base,
dims = dims,
)
else
return quantics_to_index_fused(
interleaved_to_fused(digitlist; base = base, d = d),
Expand Down
16 changes: 8 additions & 8 deletions test/grid_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@
end
end

@testset "InherentDiscreteGrid" for unfoldingscheme in [:interleaved, :fused],
@testset "InherentDiscreteGrid" for unfoldingscheme in [:interleaved, :fused, :serial, :meander],
step in [(1, 1, 1), (1, 1, 2)],
origin in [(1, 1, 1), (1, 1, 2)]

m = QuanticsGrids.InherentDiscreteGrid{3}(5, origin; unfoldingscheme, step = step)
@test QuanticsGrids.grid_min(m) == origin
@test QuanticsGrids.grid_step(m) == step

if unfoldingscheme === :interleaved
if unfoldingscheme === :interleaved || unfoldingscheme === :serial || unfoldingscheme === :meander
@test QuanticsGrids.localdimensions(m) == fill(2, 3 * 5)
else
@test QuanticsGrids.localdimensions(m) == fill(2^3, 5)
Expand All @@ -108,7 +108,7 @@
end
end

@testset "DiscretizedGrid" for unfoldingscheme in [:interleaved, :fused]
@testset "DiscretizedGrid" for unfoldingscheme in [:interleaved, :fused, :serial, :meander]
@testset "1D" begin
unfoldingscheme = :interleaved

Expand All @@ -134,7 +134,7 @@
@test QuanticsGrids.grid_step(g) == 0.059375
end

@testset "1D (includeendpoint)" for unfoldingscheme in [:interleaved, :fused]
@testset "1D (includeendpoint)" for unfoldingscheme in [:interleaved, :fused, :serial, :meander]
R = 5
a = 0.0
b = 1.0
Expand All @@ -159,15 +159,15 @@
@test only(QuanticsGrids.quantics_to_origcoord(g, fill(2, R))) == b
end

@testset "2D" for unfoldingscheme in [:interleaved, :fused]
@testset "2D" for unfoldingscheme in [:interleaved, :fused, :serial, :meander]
R = 5
d = 2
a = (0.1, 0.1)
b = (2.0, 2.0)
dx = (b .- a) ./ 2^R
g = QuanticsGrids.DiscretizedGrid{d}(R, a, b; unfoldingscheme)

if unfoldingscheme === :interleaved
if unfoldingscheme === :interleaved || unfoldingscheme === :serial || unfoldingscheme === :meander
@test QuanticsGrids.localdimensions(g) == fill(2, d * R)
else
@test QuanticsGrids.localdimensions(g) == fill(2^d, R)
Expand Down Expand Up @@ -197,7 +197,7 @@
@test_throws "Bound Error:" QuanticsGrids.origcoord_to_grididx(g, (3.0, 3.0))
end

@testset "2D (includeendpoint)" for unfoldingscheme in [:interleaved, :fused]
@testset "2D (includeendpoint)" for unfoldingscheme in [:interleaved, :fused, :serial, :meander]
R = 5
d = 2
a = (0.1, 0.1)
Expand All @@ -213,7 +213,7 @@
@test g.includeendpoint
@test QuanticsGrids.grid_step(g) == dx

if unfoldingscheme === :interleaved
if unfoldingscheme === :interleaved || unfoldingscheme === :serial || unfoldingscheme === :meander
@test QuanticsGrids.localdimensions(g) == fill(2, d * R)
else
@test QuanticsGrids.localdimensions(g) == fill(2^d, R)
Expand Down
40 changes: 40 additions & 0 deletions test/quantics_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import QuanticsGrids: fuse_dimensions, unfuse_dimensions
import QuanticsGrids: quantics_to_index_fused, index_to_quantics
import QuanticsGrids: interleave_dimensions, deinterleave_dimensions
import QuanticsGrids: serialize_dimensions, deserialize_dimensions
import QuanticsGrids: meander_dimensions, demeander_dimensions

@testset "quantics representation" begin
@testset "fuse_dimensions" begin
Expand Down Expand Up @@ -99,5 +101,43 @@
@test deinterleave_dimensions([1, 2, 11, 1, 3, 12, 1, 4, 13, 1, 5, 14], 3) ==
[[1, 1, 1, 1], [2, 3, 4, 5], [11, 12, 13, 14]]
end

@testset "serial dimensions" begin
@test [1, 1, 1, 1] == serialize_dimensions([1, 1, 1, 1])
@test [1, 1, 1, 1, 1, 1, 1, 1] ==
serialize_dimensions([1, 1, 1, 1], [1, 1, 1, 1])
@test [1, 1, 1, 1, 2, 3, 4, 5] ==
serialize_dimensions([1, 1, 1, 1], [2, 3, 4, 5])
@test [1, 1, 1, 1, 2, 3, 4, 5, 11, 12, 13, 14] ==
serialize_dimensions([1, 1, 1, 1], [2, 3, 4, 5], [11, 12, 13, 14])
end

@testset "deserial dimensions" begin
@test deserialize_dimensions([1, 1, 1, 1], 1) == [[1, 1, 1, 1]]
@test deserialize_dimensions([1, 1, 1, 1], 2) == [[1, 1], [1, 1]]
@test deserialize_dimensions([1, 2, 1, 3, 1, 4, 1, 5], 2) ==
[[1, 2, 1, 3], [1, 4, 1, 5]]
@test deserialize_dimensions([1, 2, 11, 1, 3, 12, 1, 4, 13, 1, 5, 14], 3) ==
[[1, 2, 11, 1], [3, 12, 1, 4], [13, 1, 5, 14]]
end

@testset "meander dimensions" begin
@test [1, 1, 1, 1] == meander_dimensions([1, 1, 1, 1])
@test [1, 1, 1, 1, 1, 1, 1, 1] ==
meander_dimensions([1, 1, 1, 1], [1, 1, 1, 1])
@test [1, 1, 1, 1, 2, 3, 4, 5] ==
meander_dimensions([1, 1, 1, 1], [2, 3, 4, 5])
@test [1, 1, 1, 1, 2, 3, 4, 5, 11, 12, 13, 14] ==
meander_dimensions([1, 1, 1, 1], [2, 3, 4, 5], [14, 13, 12, 11])
end

@testset "demeander dimensions" begin
@test demeander_dimensions([1, 1, 1, 1], 1) == [[1, 1, 1, 1]]
@test demeander_dimensions([1, 1, 1, 1], 2) == [[1, 1], [1, 1]]
@test demeander_dimensions([1, 2, 1, 3, 1, 4, 1, 5], 2) ==
[[3, 1, 2, 1], [1, 4, 1, 5]]
@test demeander_dimensions([1, 2, 11, 1, 3, 12, 1, 4, 13, 1, 5, 14], 3) ==
[[1, 11, 2, 1], [3, 12, 1, 4], [14, 5, 1, 13]]
end
end
end
Loading