ArrayPadding.jl Pads arrays of any dimension with various border options including constants, periodic, symmetric, mirror and smooth. Can control amount of padding applied to the left and right side of each dimension. Fully differentiable (compatible with Zygote.jl
Flux.jl
)
Is your approach more performant or more flexible than GitHub - JuliaArrays/PaddedViews.jl: Add virtual padding to the edges of an array?
PaddedViews.jl only supports padding of constants TMK
Your current approach creates a new array.
I wonder if you can add a feature to have a view of the data.
A bigger challenge, if possible, to have such view with no run time overhead.
Yeah it’s tricky doing virtual padding for non-constant border options. There’s always PaddedViews.jl for virtual padding constants. Also a key goal is being fully differentiable (with Zygote.jl Flux.jl) and GPU compatible - so virtual indexing was deemed too much trouble vs a clean eager implementation.
As a suggestion, we call Base.cat
which potentially can be modified to virtually concatenate arrays instead of allocating memory - probably cleanest way to do virtual indexing
Could you elaborate?
Also, I didn’t see the option to have inplace functionality.
Namely given a pre allocated buffer to use.
malloc in my pkg results from calling Base.cat
of the original array and its sliced views. If cat
were lazy, then my pkg wouldn’t alloc new array. I think LazyArrays.jl
has lazy vcat
hcat
but not cat
in general unless I’m mistaken. A lazy non-allocating version of cat
might benefit other folks too
@pxshen Thanks for creating the package, it indeed fulfills the gap from PaddedViews.jl. Have tried mirror padding size (1,1) as follows, and it worked as expected. Size (2,2) apparently failed, as I expected a 9x9 matrix output, but function returned same 5x5 matrix:
julia> a = reshape(1:25, (5,5))
5×5 reshape(::UnitRange{Int64}, 5, 5) with eltype Int64:
1 6 11 16 21
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
julia> pad(a, :mirror, (1,1))
7×7 Matrix{Int64}:
7 2 7 12 17 22 17
6 1 6 11 16 21 16
7 2 7 12 17 22 17
8 3 8 13 18 23 18
9 4 9 14 19 24 19
10 5 10 15 20 25 20
9 4 9 14 19 24 19
julia> pad(a, :mirror, (2,2))
5×5 Matrix{Int64}:
1 6 11 16 21
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
Would appreciate your advise. Thanks.
Thanks for finding bug for mirror and symmetric - fixed and pushed - might take few hours to update on general registry. Also if there’s enough need I can make the package non-allocating / virtually indexed.
A virtual / non allocating mode would be great.
So the user can chose.
I wonder if it also can be done with no overhead for the elements which are not in the boundary.
pad
now takes lazy=true
for non-allocating virtually indexed result. It uses LazyArrays.jl
so only up to 2d arrays are supported in lazy mode. I think there’s minor indexing overhead
Also added :replicate
border option
Lazy mode (using LazyArrays.jl underneath) doesn’t work with Zygote.jl autodiff. I’ll try fixing this
Actually I think the cleanest way to avoid both allocation and indexing overhead is to preallocate the bigger array as has been mentioned by Roy. For that I added pad!
which pads inwards and mutates original array in place. docs updated. the effective border is set back by the padding amount when evaluating various border options. It’s still AD (automatic differentiation) compatible thru Zygote.bufferfrom
internally
Sorry - for mutating pad!
, Zygote.gradient
is wrong - will get fixed this wk
For pad!
I decided to let user allocate Zygote.Buffer
if AD is needed (see Github page for example). This is an extra allocation to track mutations. pad!
doesn’t malloc otherwise
A few design questions:
Any reason behind the choice to make PaddedArray
not a subtype of AbstractArray
? Also why is not not a parametric struct?
I.e. why do you define it to be
struct PaddedArray
a
l
r
function PaddedArray(a, l=left(a), r=right(a))
new(a, l, r)
end
end
instead of
struct PaddedArray{T,N,ArrayType} <: AbstractArray{T,N}
a::ArrayType
l::Vector{Int}
r::Vector{Int}
function PaddedArray(a::A, l=left(a), r=right(a)) where {T,N,A <: AbstractArray{T,N}}
new{T,N,A}(a, l, r)
end
end
or something along those lines?
Thanks PaddedArray
is actually a private type that I use to track the amount of padding in some experiments but it’s not used by other functions and shouldn’t have been exported. Your version is the right way - it’s just that parametric types mess up Zygote
sometimes causing "need custom adjoints " errors
ah ok, I see now that pad
just returns a regular Julia array! That makes sense