Gridap.Arrays
Gridap.Arrays — ModuleThis module provides:
- An extension of the
AbstractArrayinterface in order to properly deal with mutable caches. - A mechanism to generate lazy arrays resulting from operations between arrays.
- A collection of concrete implementations of
AbstractArray.
The exported names in this module are:
CachedArrayCachedMatrixCachedVectorCompressedArrayKernelLocalToGlobalArrayLocalToGlobalPosNegArraySubVectorTableUNSETappend_ptrsappend_ptrs!append_tables_globallyappend_tables_locallyapplyapply_allapply_kernelapply_kernel!apply_kernels!array_cachearray_cachesbcastcollect1dcontractelemempty_tablefind_inverse_index_mapfind_inverse_index_map!find_local_indexflatten_partitiongenerate_data_and_ptrsget_arrayget_data_eltypeget_local_itemget_ptrs_eltypegetindex!getitems!identity_tableidentity_vectorkernel_cachekernel_cacheskernel_return_typekernel_return_typeslength_to_ptrs!reindexrewind_ptrs!setsize!test_arraytest_kerneltestitemtestitemsuses_hash
Extended AbstractArray interface
When implementing new array types, it can be needed some scratch data (e.g., allocating the output), when recovering an item from an array (typically if the array elements are non-isbits objects). To circumvent this, the user could provide the scratch data needed when getting an item. However, the Julia array interface does not support this approach. When calling a[i], in order to get the element with index i in array a, there is no extra argument for the scratch data. In order to solve this problem, we add new methods to the AbstractArray interface of Julia. We provide default implementations to the new methods, so that any AbstractArray can be used with the extended interface. New array implementations can overload these default implementations to improve performance. The most important among the new methods is getindex!, which allows to recover an item in the array by passing some scratch data.
The new methods are:
getindex!(cache,a::AbstractArray,i...)array_cache(a::AbstractArray)uses_hash(::Type{<:AbstractArray})testitem(a::AbstractArray)
These methods can be stressed with the following function
Gridap.Arrays.getindex! — Methodgetindex!(cache,a::AbstractArray,i...)Returns the item of the array a associated with index i by (possibly) using the scratch data passed in the cache object.
It defaults to
getindex!(cache,a::AbstractArray,i...) = a[i...]Examples
Iterating over an array using the getindex! function
using Gridap.Arrays
a = collect(10:15)
cache = array_cache(a)
for i in eachindex(a)
ai = getindex!(cache,a,i)
println("$i -> $ai")
end
# output
1 -> 10
2 -> 11
3 -> 12
4 -> 13
5 -> 14
6 -> 15Gridap.Arrays.array_cache — Methodarray_cache(a::AbstractArray)Returns a cache object to be used in the getindex! function. It defaults to
array_cache(a::T) where T = nothingfor types T such that uses_hash(T) == Val(false), and
function array_cache(a::T) where T
hash = Dict{UInt,Any}()
array_cache(hash,a)
endfor types T such that uses_hash(T) == Val(true), see the uses_hash function. In the later case, the type T should implement the following signature:
array_cache(hash::Dict,a::AbstractArray)where we pass a dictionary (i.e., a hash table) in the first argument. This hash table can be used to test if the object a has already build a cache and re-use it as follows
id = objectid(a)
if haskey(hash,id)
cache = hash[id] # Reuse cache
else
cache = ... # Build a new cache depending on your needs
hash[id] = cache # Register the cache in the hash table
endThis mechanism is needed, e.g., to re-use intermediate results in complex lazy operation trees. In multi-threading computations, a different hash table per thread has to be used in order to avoid race conditions.
Gridap.Arrays.uses_hash — Methoduses_hash(::Type{<:AbstractArray})This function is used to specify if the type T uses the hash-based mechanism to reuse caches. It should return either Val(true) or Val(false). It defaults to
uses_hash(::Type{<:AbstractArray}) = Val(false)Once this function is defined for the type T it can also be called on instances of T.
Gridap.Arrays.testitem — Methodtestitem(a::AbstractArray{T,N} where N) -> Any
Returns an arbitrary instance of eltype(a). The default returned value is the first entry in the array if length(a)>0 and testvalue(eltype(a)) if length(a)==0 See the testvalue function.
Examples
using Gridap.Arrays
a = collect(3:10)
ai = testitem(a)
b = Int[]
bi = testitem(b)
(ai, bi)
# output
(3, 0)
Gridap.Arrays.test_array — Functiontest_array(
a::AbstractArray{T,N}, b::AbstractArray{S,N},cmp=(==)) where {T,S,N}Checks if the entries in a and b are equal using the comparison function cmp. It also stresses the new methods added to the AbstractArray interface interface.
Working with several arrays at once
Gridap.Arrays.getitems! — Functiongetitems!(c::Tuple,a::Tuple,i...) -> TupleExtracts the i-th entry of all arrays in the tuple a using the caches in the tuple c. The results is a tuple containing each one of the extracted entries.
Example
Iterating over three different arrays simultaneously using getitems!
using Gridap.Arrays
a = collect(0:5)
b = collect(10:15)
c = collect(20:25)
caches = array_caches(a,b,c)
for i in eachindex(a)
s = getitems!(caches,(a,b,c),i)
println("$i -> $s")
end
# output
1 -> (0, 10, 20)
2 -> (1, 11, 21)
3 -> (2, 12, 22)
4 -> (3, 13, 23)
5 -> (4, 14, 24)
6 -> (5, 15, 25)Gridap.Arrays.array_caches — Functionarray_caches(a::AbstractArray...) -> TupleReturns a tuple with the cache of each array in a.
Gridap.Arrays.testitems — Functiontestitems(b::AbstractArray...) -> TupleReturns a tuple with the result of testitem applied to each of the arrays in b.
Examples
using Gridap.Arrays
a = collect(3:10)
b = Int[]
c = Float64[]
d = ones(10)
testitems(a,b,c,d)
# output
(3, 0, 0.0, 1.0)
Creting lazy operation trees
Gridap.Arrays.apply — Methodapply(f,a::AbstractArray...) -> AbstractArrayApplies the kernel f to the entries of the arrays in a (see the definition of Kernel).
The resulting array r is such that r[i] equals to apply_kernel(f,ai...) where ai is the tuple containing the i-th entry of the arrays in a (see function apply_kernel for more details). In other words, the resulting array is numerically equivalent to:
map( (x...)->apply_kernel(f,x...), a...)See the apply_kernel function for details.
Examples
Using a function as kernel
using Gridap.Arrays
a = collect(0:5)
b = collect(10:15)
c = apply(+,a,b)
println(c)
# output
[10, 12, 14, 16, 18, 20]Using a user-defined kernel
using Gridap.Arrays
import Gridap.Arrays: apply_kernel!
a = collect(0:5)
b = collect(10:15)
struct MySum <: Kernel end
apply_kernel!(cache,::MySum,x,y) = x + y
k = MySum()
c = apply(k,a,b)
println(c)
# output
[10, 12, 14, 16, 18, 20]Gridap.Arrays.apply — Methodapply(::Type{T},f,a::AbstractArray...) where TLike apply(f,a::AbstractArray...), but the user provides the element type of the resulting array in order to circumvent type inference.
Gridap.Arrays.apply — Methodapply(f::AbstractArray,a::AbstractArray...) -> AbstractArrayApplies the kernels in the array of kernels f to the entries in the arrays in a.
The resulting array has the same entries as the one obtained with:
map( apply_kernel, f, a...)See the apply_kernel function for details.
Example
"Evaluating" an array of functions
using Gridap.Arrays
f = [+,-,max,min]
a = [1,2,3,4]
b = [4,3,2,1]
c = apply(f,a,b)
println(c)
# output
[5, -1, 3, 1]Gridap.Arrays.apply — Methodapply(::Type{T},f::AbstractArray,a::AbstractArray...) where TLike apply(f::AbstractArray,a::AbstractArray...), but the user provides the element type of the resulting array in order to circumvent type inference.
Gridap.Arrays.apply_all — Functionapply_all(f::Tuple,a::AbstractArray...) -> TupleNumerically equivalent to
tuple( ( apply(fi, a...) for fi in f)... )Examples
using Gridap.Arrays
a = [1,2,3,4]
b = [4,3,2,1]
c = apply_all( (+,-), a, b)
# Equivalent to
# c = ( apply(+,a,b), apply(-,a,b) )
println(c)
# output
([5, 5, 5, 5], [-3, -1, 1, 3])
Operation kernels
Gridap.Arrays.Kernel — TypeAbstract type representing the operations to be used in the apply function.
Derived types must implement the following method:
and optionally these ones:
The kernel interface can be tested with the test_kernel function.
Note that most of the functionality implemented in terms of this interface relies in duck typing. That is, it is not strictly needed to work with types that inherit from Kernel. This is specially useful in order to accommodate existing types into this framework without the need to implement a wrapper type that inherits from Kernel. For instance, a default implementation is available for Function objects. However, we recommend that new types inherit from Kernel.
Gridap.Arrays.apply_kernel! — Methodapply_kernel!(cache,f,x...)Applies the kernel f at the arguments x... using the scratch data provided in the given cache object. The cache object is built with the kernel_cache function using arguments of the same type as in x. In general, the returned value y can share some part of its state with the cache object. If the result of two or more invocations of this function need to be accessed simultaneously (e.g., in multi-threading), create and use various cache objects (e.g., one cache per thread).
Gridap.Arrays.kernel_cache — Methodkernel_cache(f,x...)Returns the cache needed to apply kernel f with arguments of the same type as the objects in x. This function returns nothing by default.
Gridap.Arrays.kernel_return_type — Methodkernel_return_type(f,x...)Returns the type of the result of calling kernel f with arguments of the types of the objects x.
It defaults to typeof(apply_kernel(f,x...))
Gridap.Arrays.test_kernel — Functiontest_kernel(f,x::Tuple,y,cmp=(==))Function used to test if the kernel f has been implemented correctly. f is a kernel object, x is a tuple containing the arguments of the kernel, and y is the expected result. Function cmp is used to compare the computed result with the expected one. The checks are done with the @test macro.
Other functions using kernels
Gridap.Arrays.apply_kernel — Functionapply_kernel(f,x...)apply the kernel f at the arguments in x by creating a temporary cache internally. This functions is equivalent to
cache = kernel_cache(f,x...)
apply_kernel!(cache,f,x...)Gridap.Arrays.apply_kernels! — Functionapply_kernels!(caches::Tuple,fs::Tuple,x...) -> TupleApplies the kernels in the tuple fs at the arguments x... by using the corresponding cache objects in the tuple caches. The result is also a tuple containing the result for each kernel in fs.
Gridap.Arrays.kernel_caches — Functionkernel_caches(fs::Tuple,x...) -> TupleReturns a tuple with the cache corresponding to each kernel in fs for the arguments x....
Gridap.Arrays.kernel_return_types — Functionkernel_return_types(f::Tuple,x...) -> TupleComputes the return types of the kernels in f when called with arguments x.
Build-in kernels
Gridap.Arrays.bcast — Functionbcast(f::Function)Returns a kernel object that represents the "boradcasted" version of the given function f.
Gridap.Arrays.elem — Functionelem(f::Function)Returns a kernel that represents the element-wise version of the operation f It does not broadcast in singleton axes. Thus, allows some performance optimizations with respect to broadcast.
Gridap.Arrays.contract — Functioncontract(f::Function)Like the dot product between to vectors, but using operation f instead of * between components.
not needed any more, to be deleted
Examples
using Gridap.Arrays
k = contract(-)
apply_kernel(k,[1,2],[2,4]) # Equivalent to (1-2) + (2-4)
# output
-3Helper functions
Gridap.Arrays.collect1d — Functioncollect1d(a)Equivalent to
[a[i] for in 1:length(a)]Gridap.Arrays.reindex — Methodreindex(i_to_v::AbstractArray, j_to_i::AbstractArray)Concrete array implementations
CachedArray
Gridap.Arrays.CachedArray — Typemutable struct CachedArray{T, N, A<:AbstractArray{T,N}} <: AbstractArray{T,N}Type providing a re-sizable array.
The size of a CachedArray is changed via the setsize! function.
A CachedArray can be build with the constructors
using Gridap.Arrays
# Create an empty CachedArray
a = CachedArray(Float64,2)
# Resize to new shape (2,3)
setsize!(a,(2,3))
size(a)
# output
(2, 3)Gridap.Arrays.CachedArray — MethodCachedArray(a::AbstractArray)Constructs a CachedArray from a given array.
Gridap.Arrays.CachedArray — MethodCachedArray(T,N)Constructs an empty CachedArray of element type T and N dimensions.
Gridap.Arrays.setsize! — Functionsetsize!(a, s)
Changes the size of the CachedArray a to the size described the the tuple s. After calling setsize!, the array can store uninitialized values.
Gridap.Arrays.CachedMatrix — Typeconst CachedMatrix{T,A} = CachedArray{T,2,A}Gridap.Arrays.CachedVector — Typeconst CachedVector{T,A} = CachedArray{T,1,A}CompressedArray
Gridap.Arrays.CompressedArray — Typestruct CompressedArray{T,N,A,P} <: AbstractArray{T,N}
values::A
ptrs::P
endType representing an array with a reduced set of values. The array is represented by a short array of values, namely the field values, and a large array of indices, namely the field ptrs. The i-th component of the resulting array is defined as values[ptrs[i]]. The type parameters A, and P are restricted to be array types by the inner constructor of this struct.
Gridap.Arrays.CompressedArray — MethodCompressedArray(values::AbstractArray,ptrs::AbstractArray)Creates a CompressedArray object by the given arrays of values and ptrs.
Table
Gridap.Arrays.Table — Typestruct Table{T,P} <: AbstractVector{Vector{T}}
data::Vector{T}
ptrs::Vector{P}
endType representing a list of lists (i.e., a table) in compressed format.
Gridap.Arrays.Table — MethodTable(data::AbstractVector{T},ptrs::AbstractVector{P}) where {T,P}Build a table from the given data and pointers. If the arguments are not of type Vector, they will be converted.
Gridap.Arrays.Table — MethodTable(data::AbstractVector{T},ptrs::AbstractVector{P}) where {T,P}Build a table from the given data and pointers. If the arguments are not of type Vector, they will be converted.
Table(a::AbstractArray{<:AbstractArray})Build a table from a vector of vectors. If the inputs are multidimensional arrays instead of vectors, they are flattened.
Gridap.Arrays.identity_table — MethodGridap.Arrays.empty_table — Methodempty_table(::Type{T},::Type{P}, l::Integer) where {T,P}
empty_table(l::Integer)Gridap.Arrays.get_ptrs_eltype — MethodGridap.Arrays.get_data_eltype — MethodGridap.Arrays.append_tables_globally — MethodGridap.Arrays.append_tables_locally — MethodGridap.Arrays.append_tables_locally — Methodappend_tables_locally(tables::Table...)Gridap.Arrays.rewind_ptrs! — Methodrewind_ptrs!(ptrs)Rewind the given vector of pointers.
Gridap.Arrays.generate_data_and_ptrs — Methoddata, ptrs = generate_data_and_ptrs(vv)Given a vector of vectors, compress it and return the corresponding data and and ptrs
Gridap.Arrays.length_to_ptrs! — Methodlength_to_ptrs!(ptrs)Given a vector of integers, mutate it from length state to pointer state.
Gridap.Arrays.append_ptrs — Methodappend_ptrs(pa,pb)Append two vectors of pointers.
Gridap.Arrays.append_ptrs! — MethodGridap.Arrays.find_inverse_index_map — MethodGridap.Arrays.find_inverse_index_map! — MethodGridap.Arrays.flatten_partition — Methodflatten_partition(a_to_bs::Table,nb::Integer)
flatten_partition(a_to_bs::Table)Gridap.Arrays.find_local_index — Methodfind_local_index(a_to_b, b_to_la_to_a)Gridap.Arrays.get_local_item — Methodget_local_item(a_to_lb_to_b, lb::Integer)Gridap.Arrays.UNSET — ConstantLocalToGlobalArray
Gridap.Arrays.LocalToGlobalArray — TypeGridap.Arrays.LocalToGlobalArray — MethodLocalToGlobalPosNegArray
Gridap.Arrays.LocalToGlobalPosNegArray — TypeGridap.Arrays.LocalToGlobalPosNegArray — MethodSubVector
Gridap.Arrays.SubVector — Typestruct SubVector{T,A<:AbstractVector{T}} <: AbstractVector{T}
vector::A
pini::Int
pend::Int
endHelpers
Gridap.Arrays.identity_vector — Methodidentity_vector(l::Integer)