Repository: JuliaReach/ReachabilityAnalysis.jl (julia)

Issue:
Duck typing LGG09 options
eg.  accept `alg = LGG09(δ=1e-3, template=[e25, -e25])`, compare to 

```julia
e25 = zeros(48); e25[25] = 1.0
alg = LGG09(δ=1e-3, template=CustomDirections([e25, -e25]))
sol = solve(problem, T=20.0, alg=alg);

@btime solve($problem, T=20.0, alg=$alg);
    78.247 ms (160564 allocations: 32.46 MiB)

Pull request description:
Closes #206.

Patch:
diff --git a/src/Algorithms/BFFPSV18/BFFPSV18.jl b/src/Algorithms/BFFPSV18/BFFPSV18.jl
index 1f83ef2bd6..eff1112756 100644
--- a/src/Algorithms/BFFPSV18/BFFPSV18.jl
+++ b/src/Algorithms/BFFPSV18/BFFPSV18.jl
@@ -83,7 +83,7 @@ function BFFPSV18(; δ::N,
                     lazy_input::Bool=false
                  ) where {N, ST, IDX, PT, AM}
 
-    setrep  = _concretize_setrep(setrep, N)
+    setrep = _concretize_setrep(setrep, N)
     block_indices, row_blocks, column_blocks = _parse_opts(setrep, vars, dim, partition)
     spval = Val(sparse)
     return BFFPSV18{N, setrep, AM, IDX, typeof(block_indices), typeof(row_blocks),
@@ -117,77 +117,6 @@ function _parse_opts(::Type{<:Hyperrectangle}, vars, dim, partition)
     return block_indices, row_blocks, column_blocks
 end
 
-#=
-# Example:
-# alg = BFFPSV18(δ=1e-3, setrep=Interval, vars=[2], dim=4);
-function BFFPSV18(; δ::N,
-                    setrep::Type{<:Interval},
-                    vars::IDX,
-                    dim::Integer,
-                    partition::PT=-1, # ignored
-                    approx_model::AM=Forward(sih=:concrete, exp=:base, setops=:lazy),
-                    sparse::Bool=false,
-                    lazy_initial_set::Bool=false,
-                    lazy_input::Bool=false
-                 ) where {N, IDX, PT, AM}
-
-    if setrep == Interval # default concretization
-        setrep = Interval{N, IntervalArithmetic.Interval{N}}
-    end
-
-    block_indices = collect(vars)
-    row_blocks = [[i] for i in vars]
-    column_blocks = [[i] for i in 1:dim]
-    #partition = [1 for _ in 1:dim]
-    spval = Val(sparse)
-
-    return BFFPSV18{N, setrep, AM, IDX, typeof(block_indices), typeof(row_blocks),
-                    typeof(column_blocks), typeof(spval)}(δ, approx_model,
-                    vars, block_indices, row_blocks, column_blocks, spval,
-                    lazy_initial_set, lazy_input)
-end
-
-function BFFPSV18(; δ::N,
-                    setrep::Type{<:Hyperrectangle},
-                    vars::IDX,
-                    partition::PT,
-                    dim::Integer=-1, # can be deduced from the partitions
-                    approx_model::AM=Forward(sih=:concrete, exp=:base, setops=:lazy),
-                    sparse::Bool=false,
-                    lazy_initial_set::Bool=false,
-                    lazy_input::Bool=false
-                 ) where {N, IDX, PT, AM}
-
-    if setrep == Hyperrectangle # default concretization
-        setrep = Hyperrectangle{N, Vector{N}, Vector{N}}
-    end
-
-    column_blocks = copy(partition)
-
-    block_indices = Vector{Int}()
-    row_blocks = Vector{Vector{Int}}()
-
-    # TODO error checks
-    for v in vars
-        for (j, bj) in enumerate(column_blocks)
-            if v ∈ bj && j ∉ block_indices
-                push!(block_indices, j)
-                push!(row_blocks, bj)
-                break
-            end
-        end
-    end
-
-    spval = Val(sparse)
-
-    return BFFPSV18{N, setrep, AM, IDX, typeof(block_indices), typeof(row_blocks),
-                    typeof(column_blocks), typeof(spval)}(δ, approx_model,
-                    vars, block_indices, row_blocks, column_blocks, spval,
-                    lazy_initial_set, lazy_input)
-end
-=#
-
-
 # getter functions
 step_size(alg::BFFPSV18) = alg.δ
 numtype(::BFFPSV18{N}) where {N} = N
diff --git a/src/Algorithms/LGG09/LGG09.jl b/src/Algorithms/LGG09/LGG09.jl
index e90c96883d..27692c2495 100644
--- a/src/Algorithms/LGG09/LGG09.jl
+++ b/src/Algorithms/LGG09/LGG09.jl
@@ -45,9 +45,14 @@ function LGG09(; δ::N,
                template::TN,
                static::Bool=false,
                threaded::Bool=false) where {N, AM, TN}
-    return LGG09(δ, approx_model, template, Val(static), threaded)
+    dirs = _get_template(template)
+    return LGG09(δ, approx_model, dirs, Val(static), threaded)
 end
 
+_get_template(template::AbstractDirections) = template
+_get_template(template::AbstractVector{N}) where {N<:Number} = CustomDirections([template])
+_get_template(template::AbstractVector{VT}) where {N<:Number, VT<:AbstractVector{N}} = CustomDirections(template)
+
 step_size(alg::LGG09) = alg.δ
 numtype(::LGG09{N}) where {N} = N
 setrep(alg::LGG09) = setrep(alg.template)


Test patch:
diff --git a/test/algorithms/LGG09.jl b/test/algorithms/LGG09.jl
index 7a4edf1b71..2a8d2ca0af 100644
--- a/test/algorithms/LGG09.jl
+++ b/test/algorithms/LGG09.jl
@@ -1,3 +1,5 @@
+using LazySets.Arrays: SingleEntryVector
+
 @testset "LGG09 algorithm" begin
     # one-dimensional
     prob, tspan = exponential_1d()
@@ -14,4 +16,14 @@
     sol = solve(prob, tspan=tspan, LGG09(δ=0.01, template=box5d))
     @test setrep(sol) == HPolyhedron{Float64, Vector{Float64}}
     @test dim(sol) == 5
+
+    # case where the template directions is as single vector
+    dirs = fill(0.0, 5); dirs[1] = 1.0 # e1
+    alg = LGG09(δ=0.01, template=dirs)
+    alg.template == CustomDirections([dirs])
+
+    # case where the template directions is a vector of vectors
+    dirs = [SingleEntryVector(1, 5, 1.0), SingleEntryVector(1, 5, -1.0)]
+    alg = LGG09(δ=0.01, template=dirs)
+    alg.template == CustomDirections(dirs)
 end