Skip to content

Commit

Permalink
vertices iterator for AbstractHyperrectangle
Browse files Browse the repository at this point in the history
  • Loading branch information
schillic committed Aug 12, 2020
1 parent f81f6c0 commit d5a1256
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/src/lib/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ radius(::AbstractHyperrectangle, ::Real=Inf)
σ(::AbstractVector{N}, ::AbstractHyperrectangle{N}) where {N<:Real}
ρ(::AbstractVector{N}, ::AbstractHyperrectangle{N}) where {N<:Real}
∈(::AbstractVector{N}, ::AbstractHyperrectangle{N}) where {N<:Real}
vertices(::AbstractHyperrectangle)
vertices_list(::AbstractHyperrectangle{N}) where {N<:Real}
constraints_list(::AbstractHyperrectangle{N}) where {N<:Real}
high(::AbstractHyperrectangle{N}) where {N<:Real}
Expand Down
129 changes: 125 additions & 4 deletions src/Interfaces/AbstractHyperrectangle.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,128 @@ end
# --- AbstractPolytope interface functions ---


"""
vertices(H::AbstractHyperrectangle)
Construct an iterator over the vertices of a hyperrectangular set.
### Input
- `H` -- hyperrectangular set
### Output
An iterator over the vertices.
### Algorithm
We implement two different iterators, depending on whether the set is flat in
some dimension.
"""
function vertices(H::AbstractHyperrectangle)
n = dim(H)
start = -1
@inbounds for i in 1:n
if iszero(radius_hyperrectangle(H, i))
start = i # found flat dimension
break
end
end
if start == -1
# no flat dimensions -> use more efficient implementation
return HyperrectangleVertexIteratorNonflat(H)
end
flats = falses(n)
flats[start] = true
n_flats = 1
# find remaining flat dimensions
@inbounds for i in (start+1):n
if iszero(radius_hyperrectangle(H, i))
flats[i] = true
n_flats += 1
end
end
return HyperrectangleVertexIteratorFlat(H, flats, n_flats)
end

struct HyperrectangleVertexIteratorNonflat{N, TH<:AbstractHyperrectangle{N}}
H::TH
end

function Base.length(it::HyperrectangleVertexIteratorNonflat)
return 2^(dim(it.H))
end

function Base.eltype(::Type{<:HyperrectangleVertexIteratorNonflat{N}}) where {N}
return Vector{N}
end

# Note: Base.iterate(::HyperrectangleVertexIteratorNonflat) defined later

function Base.iterate(it::HyperrectangleVertexIteratorNonflat, state)
v_it, mask = state

@inbounds for (i, mi) in enumerate(mask)
if mi
# decrement bit vector = flip to false and stop
v_it[i] = low(it.H, i)
mask[i] = false
return (copy(v_it), state)
else
# decrement bit vector = flip back to true and continue
v_it[i] = high(it.H, i)
mask[i] = true
end
end
# no bit flip to false occurred => end of iterator
return nothing
end

struct HyperrectangleVertexIteratorFlat{N, TH<:AbstractHyperrectangle{N}}
H::TH
flats::BitArray
n_flats::Int
end

function Base.length(it::HyperrectangleVertexIteratorFlat)
return 2^it.n_flats
end

function Base.eltype(::Type{<:HyperrectangleVertexIteratorFlat{N}}) where {N}
return Vector{N}
end

function Base.iterate(it::Union{HyperrectangleVertexIteratorNonflat,
HyperrectangleVertexIteratorFlat})
mask = trues(dim(it.H))
v_it = high(it.H)
v = copy(v_it)
state = (v_it, mask)
return (v, state)
end

function Base.iterate(it::HyperrectangleVertexIteratorFlat, state)
v_it, mask = state

@inbounds for (i, mi) in enumerate(mask)
if it.flats[i]
continue
end
if mi
# decrement bit vector = flip to false and stop
v_it[i] = low(it.H, i)
mask[i] = false
return (copy(v_it), state)
else
# decrement bit vector = flip back to true and continue
v_it[i] = high(it.H, i)
mask[i] = true
end
end
# no bit flip to false occurred => end of iterator
return nothing
end

"""
vertices_list(H::AbstractHyperrectangle{N}) where {N<:Real}
Expand Down Expand Up @@ -188,8 +310,7 @@ function vertices_list(H::AbstractHyperrectangle{N}) where {N<:Real}
# the vector will later also contain entries -1
trivector = Vector{Int8}(undef, n)
m = 1
c = center(H)
v = copy(c)
v = copy(center(H))
@inbounds for i in 1:n
ri = radius_hyperrectangle(H, i)
if iszero(ri)
Expand All @@ -210,10 +331,10 @@ function vertices_list(H::AbstractHyperrectangle{N}) where {N<:Real}
for j in 1:length(v)
if trivector[j] == Int8(-1)
trivector[j] = Int8(1)
v[j] = c[j] + radius_hyperrectangle(H, j)
v[j] = high(H, j)
elseif trivector[j] == Int8(1)
trivector[j] = Int8(-1)
v[j] = c[j] - radius_hyperrectangle(H, j)
v[j] = low(H, j)
break
end
end
Expand Down
8 changes: 8 additions & 0 deletions test/unit_Hyperrectangle.jl
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ for N in [Float64, Rational{Int}, Float32]
P = linear_map(Diagonal(N[1, 2, 3, 4]), overapproximate(H1 * H1))
@test P isa Zonotope

# vertices
H = Hyperrectangle(N[1, 2], N[3, 4]) # non-flat
@test ispermutation(collect(vertices(H)), vertices_list(H)) &&
ispermutation(vertices_list(H), [N[-2, -2], [4, -2], [-2, 6], [4, 6]])
H = Hyperrectangle(N[1, 2], N[3, 0]) # flat
@test ispermutation(collect(vertices(H)), vertices_list(H)) &&
ispermutation(vertices_list(H), [N[-2, 2], [4, 2]])

# check that vertices_list is computed correctly if the hyperrectangle
# is "degenerate"/flat, i.e., its radius contains zeros
# these tests would crash if all 2^100 vertices were computed (see #92)
Expand Down

0 comments on commit d5a1256

Please sign in to comment.