Gridap.Arrays

Gridap.ArraysModule

This module provides:

  • An extension of the AbstractArray interface 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:

source

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:

These methods can be stressed with the following function

Gridap.Arrays.getindex!Method
getindex!(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 -> 15
source
Gridap.Arrays.array_cacheMethod
array_cache(a::AbstractArray)

Returns a cache object to be used in the getindex! function. It defaults to

array_cache(a::T) where T = nothing

for 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)
end

for 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
end

This 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.

source
Gridap.Arrays.uses_hashMethod
uses_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.

source
Gridap.Arrays.testitemMethod
testitem(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)
source
Gridap.Arrays.test_arrayFunction
test_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.

source

Working with several arrays at once

Gridap.Arrays.getitems!Function
getitems!(c::Tuple,a::Tuple,i...) -> Tuple

Extracts 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)
source
Gridap.Arrays.testitemsFunction
testitems(b::AbstractArray...) -> Tuple

Returns 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)
source

Creting lazy operation trees

Gridap.Arrays.applyMethod
apply(f,a::AbstractArray...) -> AbstractArray

Applies 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]
source
Gridap.Arrays.applyMethod
apply(f::AbstractArray,a::AbstractArray...) -> AbstractArray

Applies 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]
source
Gridap.Arrays.apply_allFunction
apply_all(f::Tuple,a::AbstractArray...) -> Tuple

Numerically 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])
source

Operation kernels

Gridap.Arrays.KernelType

Abstract 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.

source
Gridap.Arrays.apply_kernel!Method
apply_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).

source
Gridap.Arrays.kernel_cacheMethod
kernel_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.

source
Gridap.Arrays.kernel_return_typeMethod
kernel_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...))

source
Gridap.Arrays.test_kernelFunction
test_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.

source

Other functions using kernels

Gridap.Arrays.apply_kernelFunction
apply_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...)
source
Gridap.Arrays.apply_kernels!Function
apply_kernels!(caches::Tuple,fs::Tuple,x...) -> Tuple

Applies 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.

source
Gridap.Arrays.kernel_cachesFunction
kernel_caches(fs::Tuple,x...) -> Tuple

Returns a tuple with the cache corresponding to each kernel in fs for the arguments x....

source

Build-in kernels

Gridap.Arrays.bcastFunction
bcast(f::Function)

Returns a kernel object that represents the "boradcasted" version of the given function f.

source
Gridap.Arrays.elemFunction
elem(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.

source
Gridap.Arrays.contractFunction
contract(f::Function)

Like the dot product between to vectors, but using operation f instead of * between components.

Warning

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
-3
source

Helper functions

Concrete array implementations

CachedArray

Gridap.Arrays.CachedArrayType
mutable 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)
source
Gridap.Arrays.setsize!Function
setsize!(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.

source

CompressedArray

Gridap.Arrays.CompressedArrayType
struct CompressedArray{T,N,A,P} <: AbstractArray{T,N}
  values::A
  ptrs::P
end

Type 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.

source
Gridap.Arrays.CompressedArrayMethod
CompressedArray(values::AbstractArray,ptrs::AbstractArray)

Creates a CompressedArray object by the given arrays of values and ptrs.

source

Table

Gridap.Arrays.TableType
struct Table{T,P} <: AbstractVector{Vector{T}}
  data::Vector{T}
  ptrs::Vector{P}
end

Type representing a list of lists (i.e., a table) in compressed format.

source
Gridap.Arrays.TableMethod
Table(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.

source
Gridap.Arrays.TableMethod
Table(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.

source
Table(a::AbstractArray{<:AbstractArray})

Build a table from a vector of vectors. If the inputs are multidimensional arrays instead of vectors, they are flattened.

source

LocalToGlobalArray

LocalToGlobalPosNegArray

SubVector

Helpers