PatternVectors.jl

Peculiar Vector Patterns.
View on GitHub Star

About this Package

PatternVectors.jl is a Julia package containing some useful array representation for peculiar one dimensional arrays patterns.
It currently contains the following pattern types:

  • ZeroPattern: convenient representation for vectors of the form:

[0,0,0,0,0,0...]
  • FillPattern: convenient representation for vectors of the form:

[a,a,a,a,a,a...]
  • InitialValuePattern: convenient representation for vectors of the form:

[a,b,b,b,b,b...]
  • FinalValuePattern: convenient representation for vectors of the form:

[a,a,a,a,a,a...,b]
  • PaddedFillPattern: convenient representation for vectors of the form:

[x,a,a,a,a,a...,y]
  • EvenOddPattern: convenient representation for vectors of the form:

[a,b,a,b,a,b...]
  • PaddedEvenOddPattern: convenient representation for vectors of the form:

[x,a,b,a,b,a,b...,y]

Defining a new pattern is easy and fully supported by the library. The module is standalone.

Getting Started

EvenOddPattern

To build a EvenOddPattern one needs to provide:

  • the value for odd indices

  • the value for even indices

The various values must be of the same type and the length must be greater than one. The way to build an PatternVector with such pattern is the following:

using PatternVectors
value_odd=0.2
value_even=2.3
pattern=EvenOddPattern(value_odd,value_even)
length_av=7
x_av=PatternVector(length_av,pattern)
7-element PatternVector{Float64, EvenOddPattern{Float64}}:
 0.2
 2.3
 0.2
 2.3
 0.2
 2.3
 0.2

PaddedEvenOddPattern

To build a PaddedEvenOddPattern one needs to provide:

  • the initial value

  • the value for even indices

  • the value for odd indices

  • the final value

The various values must be of the same type and the length must be greater than three. The way to build an PatternVector with such pattern is the following:

using PatternVectors
initial_value=0.2
value_odd=-0.2
value_even=2.3
final_value=1.3
length_av=7
x_av=PatternVector(length_av,PaddedEvenOddPattern(initial_value,value_even,value_odd,final_value))
7-element PatternVector{Float64, PaddedEvenOddPattern{Float64}}:
  0.2
  2.3
 -0.2
  2.3
 -0.2
  2.3
  1.3

Operation on Pattern Vectors

The following applies:

  • PatternVector is closed under getindex for range of integers.

using PatternVectors
apv=PatternVector(70,PaddedEvenOddPattern(0.2,-2.0,4.0,2.3))
z_small=apv[1:7:50]
z_small
8-element PatternVector{Float64, PaddedEvenOddPattern{Float64}}:
  0.2
 -2.0
  4.0
 -2.0
  4.0
 -2.0
  4.0
 -2.0
  • Any scalar unary function applied directly to PatternVectors will produce an array of the same type.

using PatternVectors
pattern=EvenOddPattern(0.2,-2.0)
av=PatternVector(10,pattern)
z_sin=@. sin(av)
z_sin
10-element PatternVector{Float64, EvenOddPattern{Float64}}:
  0.19866933079506122
 -0.9092974268256817
  0.19866933079506122
 -0.9092974268256817
  0.19866933079506122
 -0.9092974268256817
  0.19866933079506122
 -0.9092974268256817
  0.19866933079506122
 -0.9092974268256817

Operation between Pattern Vectors

The following applies:

  • Binary scalar functions between PatternVectors will produce a PatternVector.

  • Binary scalar functions between PatternVector and any other type deriving from AbstractArray will produce an array of the other type.

using PatternVectors
x=PatternVector(10,EvenOddPattern(0.2,2.3))
y=randn(10)
z=PatternVector(10,PaddedEvenOddPattern(0.2,-2.0,4.0,2.3))
@. sin(x)*y+z
10-element Vector{Float64}:
  0.32817625107853676
 -1.3788214462744897
  3.9659606993768657
 -0.519856306112737
  3.973769783108124
 -2.8958987002014878
  4.057899244162901
 -2.8914103848792068
  3.7010276383543568
  2.311659379063692

Performances Comparison

Here the common usages of the package are tested.

Simple multiplication

using PatternVectors, BenchmarkTools
n=10_000
x=PatternVector(n,EvenOddPattern(0.2,2.3))
y=randn(n)
x_c=collect(x)
@btime @. $x*$y;
@btime @. $x_c*$y;
  2.632 μs (3 allocations: 78.20 KiB)
  3.345 μs (3 allocations: 78.20 KiB)

Flipping sign based on index and sum

using PatternVectors, BenchmarkTools, LinearAlgebra
n=10_000
function f_std_scalar(f_x)
	N=length(f_x)
	sum_z=zero(eltype(f_x))
	for i in 1:N
		w=ifelse(isodd(i),1,-1)
		sum_z+=@views @inbounds w*f_x[i]
	end
	return sum_z
end

function f_std_scalar_2(f_x)
	N=length(f_x)
	sum_z=zero(eltype(f_x))
	for i in 1:N
		if(isodd(i))
			sum_z+=@views @inbounds f_x[i]
		else
			sum_z-=@views @inbounds f_x[i]
		end
	end
	return sum_z
end

function f_std_vec(f_x)
	N=length(f_x)
	idx=1:N
	W=@. ifelse(isodd(idx),1,-1)
	return sum(W.*f_x)
end

function f_std_vec_linear_algebra(f_x)
	N=length(f_x)
	idx=1:N
	W=@. ifelse(isodd(idx),1,-1)
	return dot(W,f_x)
end

function f_apv(f_x)
	N=length(f_x)
	W=PatternVector(n,EvenOddPattern(1,-1))
	return sum(W.*f_x)
end
x=randn(n)
f_x=@. sin(x)+x*cos(x)
@btime f_std_scalar($f_x);
@btime f_std_scalar_2($f_x);
@btime f_std_vec($f_x);
@btime f_std_vec_linear_algebra($f_x);
@btime f_apv($f_x);
  9.267 μs (0 allocations: 0 bytes)
  9.267 μs (0 allocations: 0 bytes)
  6.870 μs (6 allocations: 156.39 KiB)
  12.985 μs (2 allocations: 78.16 KiB)
  3.608 μs (3 allocations: 78.20 KiB)

Simpson Integration

using PatternVectors, BenchmarkTools,LinearAlgebra
n2=10_000
function f_simpson_std_scalar(f_x)
	N=length(f_x)
	sum_z=zero(eltype(f_x))
	for i in 1:N
		w=ifelse(i==1,1/3,ifelse(i==N,1/3,4/3))
		sum_z+=@views @inbounds w*f_x[i]
	end
	return sum_z
end

function f_simpson_std_vec(f_x)
	N=length(f_x)
	idx=1:N
	W=@. ifelse(idx==1,1/3,ifelse(idx==N,1/3,4/3))
	return sum(W.*f_x)
end

function f_simpson_std_vec_linear_algebra(f_x)
	N=length(f_x)
	idx=1:N
	W=@. ifelse(idx==1,1/3,ifelse(idx==N,1/3,4/3))
	return dot(W,f_x)
end

function f_simpson_apv(f_x)
	N=length(f_x)
	W=PatternVector(N,PaddedEvenOddPattern(1/3,4/3,4/3,1/3))
	return sum(W.*f_x)
end

function f_simpson_apv_linear_algebra(f_x)
	N=length(f_x)
	W=PatternVector(N,PaddedEvenOddPattern(1/3,4/3,4/3,1/3))
	return dot(W,f_x)
end

x2=randn(n2)
f_x2=@. sin(x2)+x2*cos(x2)
@btime f_simpson_std_scalar($f_x2);
@btime f_simpson_std_vec($f_x2);
@btime f_simpson_std_vec_linear_algebra($f_x2);
@btime f_simpson_apv($f_x2);
@btime f_simpson_apv_linear_algebra($f_x2);
  9.267 μs (0 allocations: 0 bytes)
  7.051 μs (6 allocations: 156.39 KiB)
  4.224 μs (3 allocations: 78.20 KiB)
  4.065 μs (3 allocations: 78.20 KiB)
  9.277 μs (0 allocations: 0 bytes)

To be noticed the performance improvements thanks to the usage of PatternVector, and to be noticed that the first function proposed is not compatible with the CUDA.jl stack.

GPU Support

The main objective of this library is to write code without iterating on indices for both cpu and gpu support. The main class is PatternVector which, unlike the standard arrays in julia, it is an immutable object, hence we can send it to gpu without issues, and being in most of the cases (up to the client) a lightweight object, this sending operation is efficient.

New Patterns Definition

In most of the cases the provided patterns suffice the applications. In case you need a new pattern, the following steps are needed:

  • Define a container (namely MyNewPattern{T}) inheriting from AbstractPattern{T} where T is the data type you are going to store in the array.

  • Define a constructor for it.

  • Implement the interface:

    • pattern_minimum_size: define the minimum size for your type

    • getindex_pattern: define the logic to extract scalars

    • getindex_pattern_range: define the logic to extract ranges

    • materialize_pattern: define the way to compute broadcasted functions with the new pattern

In case you need to mix your pattern with other existing patterns, you will have to specify we is the more general pattern able to store both:

  • determine_mixed_pattern(::Type{T}, ::Type{V}) where {T <: MyNewPattern{M}, V <: MyOldPattern{N}} where {M, N} = MyWinningPattern{promote_type(M, N)}

And you are ready to go!

In case you want to use your new pattern in AD applications you will have to provide two additional implementations:

  • ChainRulesCore.rrule(::Type{MyNewPattern}, args...): the rrule for the constructor of your newly defined pattern.

  • pattern_to_vector_pullback: the rrule to convert from pattern to array.

For more details have a look at the already implemented types.