Circulant Tensors
A circulant tensor is a construct similar to a filtering matrix. It creates "views" into the parent array of the indices of the kernel such that if the circulant tensor is contracted at the starting dimensions with the kernel it produces the correlation result. This means that it has the combined dimensionality of the array and the kernel.
TransferFunctions.CirculantTensor — TypeCirculantTensor{T,AA,N,KI,II} <: AbstractArray{T,N}N-dimensional circulant tensor of an M-dimensional array A with kernel indices kern.
The dimensionality N is equal to 2M, where the first M dimensions have interior filtering coordinates ct.interior given by the kernel axes ct.kern and the axes of A and the tail M dimensions have the kernel coordinates ct.kern.
A correlation of array A with the kernel Kcan be obtained by outer tensor contraction over the tail M dimensions of the circulant(A,K) with K.
See also FilteringMatrix, BorderArray circulant
TransferFunctions.circulant — Functioncirculant(A, K, border)Construct a CirculantTensor of A with the kernel K such that tensor contraction with the front indices with K outputs the filtered array of A with K.
If border is specified, the array is padded with the strategy border so that the full extent of the array A is kept in the contraction output.
julia> circulant(1:9, -1:1)
3×6 circulant(::UnitRange{Int64}, (Base.OneTo(3),)) with eltype Int64 with indices Base.OneTo(3)×1:6:
2 3 4 5 6 7
3 4 5 6 7 8
4 5 6 7 8 9
julia> circulant(reshape(1:25, (5,5)), (-1:1, -1:1)); # too large to show
julia> circulant(reshape(1:25, (5,5)), OAs.OffsetArray(ones(3,3), -1:1, -1:1)); # too large to show
julia> c = circulant(reshape(1:25, (5,5)), (-1:1, -1:1), :replicate); # too large to show
julia> size(c)
(3, 3, 5, 5)TransferFunctions.contract — Functioncontract(F,K)Contract the tensor F at the first ndims(K) dimensions with the kernel K
K = OffsetArray(ones(51,51) ./ 51^2, -25:25, -25:25) # origin (0,0) must be contained in the kernel
A_CT = circulant(A, K, :replicate)
A_corr_K = TransferFunctions.contract(A_CT, K)
We can see that the first ndims(K) slices of the circulant tensor are simply patches (or views) of the parent array.
A_indices = CartesianIndices(A)[begin:(length(A)÷10):end][2:(end-1)]
patches = [A_CT[:,:, I] for I in A_indices]
At the patch with index (511, 256) you can see that the parent array is extended with :replicate borders.