From 684c6208419cf93c32473db1696ff1eae454f727 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 2 Jul 2024 07:15:43 +0200 Subject: [PATCH 1/4] move Ball1.jl to subfolder --- src/LazySets.jl | 3 ++- src/Sets/{Ball1.jl => Ball1/Ball1Module.jl} | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename src/Sets/{Ball1.jl => Ball1/Ball1Module.jl} (100%) diff --git a/src/LazySets.jl b/src/LazySets.jl index d31f7a839c..18eed04924 100644 --- a/src/LazySets.jl +++ b/src/LazySets.jl @@ -98,7 +98,8 @@ include("Sets/Universe.jl") include("Sets/EmptySet/EmptySetModule.jl") @reexport using ..EmptySetModule: EmptySet, ∅, _isdisjoint_emptyset -include("Sets/Ball1.jl") +include("Sets/Ball1/Ball1Module.jl") + include("Sets/Ball2.jl") include("Sets/BallInf.jl") include("Sets/Ballp.jl") diff --git a/src/Sets/Ball1.jl b/src/Sets/Ball1/Ball1Module.jl similarity index 100% rename from src/Sets/Ball1.jl rename to src/Sets/Ball1/Ball1Module.jl From 1f495dc5506031c2b5261629ca82bfd56b79db05 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 2 Jul 2024 07:15:43 +0200 Subject: [PATCH 2/4] add Ball1Module --- docs/src/lib/sets/Ball1.md | 7 ++++++- src/LazySets.jl | 1 + src/Sets/Ball1/Ball1Module.jl | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/sets/Ball1.md b/docs/src/lib/sets/Ball1.md index ee561c1425..2c66bd4efe 100644 --- a/docs/src/lib/sets/Ball1.md +++ b/docs/src/lib/sets/Ball1.md @@ -1,11 +1,16 @@ ```@meta -CurrentModule = LazySets +CurrentModule = LazySets.Ball1Module ``` # [Manhattan-norm ball (Ball1)](@id def_Ball1) ```@docs Ball1 +``` + +## Operations + +```@docs center(::Ball1) σ(::AbstractVector, ::Ball1) ρ(::AbstractVector, ::Ball1) diff --git a/src/LazySets.jl b/src/LazySets.jl index 18eed04924..6ab1c2dc1d 100644 --- a/src/LazySets.jl +++ b/src/LazySets.jl @@ -99,6 +99,7 @@ include("Sets/EmptySet/EmptySetModule.jl") @reexport using ..EmptySetModule: EmptySet, ∅, _isdisjoint_emptyset include("Sets/Ball1/Ball1Module.jl") +@reexport using ..Ball1Module: Ball1 include("Sets/Ball2.jl") include("Sets/BallInf.jl") diff --git a/src/Sets/Ball1/Ball1Module.jl b/src/Sets/Ball1/Ball1Module.jl index 3e8315dd65..2676915586 100644 --- a/src/Sets/Ball1/Ball1Module.jl +++ b/src/Sets/Ball1/Ball1Module.jl @@ -1,3 +1,20 @@ +module Ball1Module + +using Reexport + +using ..LazySets: AbstractCentrallySymmetricPolytope, HalfSpace +using Random: AbstractRNG, GLOBAL_RNG +using ReachabilityBase.Distribution: reseed! +using ReachabilityBase.Arrays: argmaxabs +using LinearAlgebra: dot + +@reexport import ..API: center, constraints_list, high, isoperationtype, low, + rand, reflect, vertices_list, ∈, project, scale, ρ, σ, + translate! +@reexport import ..LazySets: ball_norm, radius_ball +@reexport using ..API +using ..LazySets: _high_AbstractBallp, _low_AbstractBallp + export Ball1 """ @@ -368,3 +385,5 @@ end function scale(α::Real, B::Ball1) return Ball1(B.center .* α, B.radius * abs(α)) end + +end # module From ba3099c21bde0ad2d43ec655fa0d5fd562f3b594 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 2 Jul 2024 07:15:43 +0200 Subject: [PATCH 3/4] outsource struct and operations to files --- src/Sets/Ball1/Ball1.jl | 49 ++++ src/Sets/Ball1/Ball1Module.jl | 386 ++--------------------------- src/Sets/Ball1/ball_norm.jl | 4 + src/Sets/Ball1/center.jl | 16 ++ src/Sets/Ball1/constraints_list.jl | 34 +++ src/Sets/Ball1/high.jl | 7 + src/Sets/Ball1/in.jl | 54 ++++ src/Sets/Ball1/isoperationtype.jl | 1 + src/Sets/Ball1/low.jl | 7 + src/Sets/Ball1/project.jl | 3 + src/Sets/Ball1/radius_ball.jl | 3 + src/Sets/Ball1/rand.jl | 34 +++ src/Sets/Ball1/reflect.jl | 22 ++ src/Sets/Ball1/scale.jl | 3 + src/Sets/Ball1/support_function.jl | 26 ++ src/Sets/Ball1/support_vector.jl | 20 ++ src/Sets/Ball1/translate.jl | 29 +++ src/Sets/Ball1/vertices_list.jl | 40 +++ 18 files changed, 370 insertions(+), 368 deletions(-) create mode 100644 src/Sets/Ball1/Ball1.jl create mode 100644 src/Sets/Ball1/ball_norm.jl create mode 100644 src/Sets/Ball1/center.jl create mode 100644 src/Sets/Ball1/constraints_list.jl create mode 100644 src/Sets/Ball1/high.jl create mode 100644 src/Sets/Ball1/in.jl create mode 100644 src/Sets/Ball1/isoperationtype.jl create mode 100644 src/Sets/Ball1/low.jl create mode 100644 src/Sets/Ball1/project.jl create mode 100644 src/Sets/Ball1/radius_ball.jl create mode 100644 src/Sets/Ball1/rand.jl create mode 100644 src/Sets/Ball1/reflect.jl create mode 100644 src/Sets/Ball1/scale.jl create mode 100644 src/Sets/Ball1/support_function.jl create mode 100644 src/Sets/Ball1/support_vector.jl create mode 100644 src/Sets/Ball1/translate.jl create mode 100644 src/Sets/Ball1/vertices_list.jl diff --git a/src/Sets/Ball1/Ball1.jl b/src/Sets/Ball1/Ball1.jl new file mode 100644 index 0000000000..29552a49df --- /dev/null +++ b/src/Sets/Ball1/Ball1.jl @@ -0,0 +1,49 @@ +""" + Ball1{N, VN<:AbstractVector{N}} <: AbstractCentrallySymmetricPolytope{N} + +Type that represents a ball in the 1-norm (also known as the Manhattan norm). +The ball is also known as a +[cross-polytope](https://en.wikipedia.org/wiki/Cross-polytope). + +It is defined as the set + +```math +\\mathcal{B}_1^n(c, r) = \\{ x ∈ ℝ^n : ∑_{i=1}^n |c_i - x_i| ≤ r \\}, +``` +where ``c ∈ ℝ^n`` is its center and ``r ∈ ℝ_+`` its radius. + +### Fields + +- `center` -- center of the ball as a real vector +- `radius` -- radius of the ball as a scalar (``≥ 0``) + +### Examples + +The unit ball in the 1-norm in the plane: + +```jldoctest ball1_constructor +julia> B = Ball1(zeros(2), 1.0) +Ball1{Float64, Vector{Float64}}([0.0, 0.0], 1.0) +julia> dim(B) +2 +``` + +We evaluate the support vector in the North direction: + +```jldoctest ball1_constructor +julia> σ([0.0, 1.0], B) +2-element Vector{Float64}: + 0.0 + 1.0 +``` +""" +struct Ball1{N,VN<:AbstractVector{N}} <: AbstractCentrallySymmetricPolytope{N} + center::VN + radius::N + + # default constructor with domain constraint for radius + function Ball1(center::VN, radius::N) where {N,VN<:AbstractVector{N}} + @assert radius >= zero(N) "the radius must not be negative" + return new{N,VN}(center, radius) + end +end diff --git a/src/Sets/Ball1/Ball1Module.jl b/src/Sets/Ball1/Ball1Module.jl index 2676915586..f6e7488db9 100644 --- a/src/Sets/Ball1/Ball1Module.jl +++ b/src/Sets/Ball1/Ball1Module.jl @@ -17,373 +17,23 @@ using ..LazySets: _high_AbstractBallp, _low_AbstractBallp export Ball1 -""" - Ball1{N, VN<:AbstractVector{N}} <: AbstractCentrallySymmetricPolytope{N} - -Type that represents a ball in the 1-norm (also known as the Manhattan norm). -The ball is also known as a -[cross-polytope](https://en.wikipedia.org/wiki/Cross-polytope). - -It is defined as the set - -```math -\\mathcal{B}_1^n(c, r) = \\{ x ∈ ℝ^n : ∑_{i=1}^n |c_i - x_i| ≤ r \\}, -``` -where ``c ∈ ℝ^n`` is its center and ``r ∈ ℝ_+`` its radius. - -### Fields - -- `center` -- center of the ball as a real vector -- `radius` -- radius of the ball as a scalar (``≥ 0``) - -### Examples - -The unit ball in the 1-norm in the plane: - -```jldoctest ball1_constructor -julia> B = Ball1(zeros(2), 1.0) -Ball1{Float64, Vector{Float64}}([0.0, 0.0], 1.0) -julia> dim(B) -2 -``` - -We evaluate the support vector in the North direction: - -```jldoctest ball1_constructor -julia> σ([0.0, 1.0], B) -2-element Vector{Float64}: - 0.0 - 1.0 -``` -""" -struct Ball1{N,VN<:AbstractVector{N}} <: AbstractCentrallySymmetricPolytope{N} - center::VN - radius::N - - # default constructor with domain constraint for radius - function Ball1(center::VN, radius::N) where {N,VN<:AbstractVector{N}} - @assert radius >= zero(N) "the radius must not be negative" - return new{N,VN}(center, radius) - end -end - -isoperationtype(::Type{<:Ball1}) = false - -_vector_type(B::Ball1{N,VN}) where {N,VN} = VN - -""" - center(B::Ball1) - -Return the center of a ball in the 1-norm. - -### Input - -- `B` -- ball in the 1-norm - -### Output - -The center of the ball in the 1-norm. -""" -function center(B::Ball1) - return B.center -end - -function radius_ball(B::Ball1) - return B.radius -end - -function ball_norm(B::Ball1) - N = eltype(B) - return one(N) -end - -function low(B::Ball1) - return _low_AbstractBallp(B) -end - -function low(B::Ball1, i::Int) - return _low_AbstractBallp(B, i) -end - -function high(B::Ball1) - return _high_AbstractBallp(B) -end - -function high(B::Ball1, i::Int) - return _high_AbstractBallp(B, i) -end - -""" - vertices_list(B::Ball1) - -Return the list of vertices of a ball in the 1-norm. - -### Input - -- `B` -- ball in the 1-norm - -### Output - -A list containing the vertices of the ball in the 1-norm. - -### Notes - -In ``n`` dimensions there are ``2n`` vertices (unless the radius is 0). -""" -function vertices_list(B::Ball1) - # fast evaluation if B has radius 0 - if iszero(B.radius) - return [B.center] - end - VN = _vector_type(B) - vertices = Vector{VN}(undef, 2 * dim(B)) - j = 0 - v = copy(B.center) - @inbounds for i in 1:dim(B) - ci = v[i] - v[i] += B.radius - j += 1 - vertices[j] = copy(v) - v[i] = ci - B.radius - j += 1 - vertices[j] = copy(v) - v[i] = ci # restore old value - end - return vertices -end - -""" - σ(d::AbstractVector, B::Ball1) - -Return the support vector of a ball in the 1-norm in the given direction. - -### Input - -- `d` -- direction -- `B` -- ball in the 1-norm - -### Output - -The support vector in the given direction. -""" -function σ(d::AbstractVector, B::Ball1) - res = copy(B.center) - imax = argmaxabs(d) - res[imax] += sign(d[imax]) * B.radius - return res -end - -""" - ρ(d::AbstractVector, B::Ball1) - -Evaluate the support function of a ball in the 1-norm in the given direction. - -### Input - -- `d` -- direction -- `B` -- ball in the 1-norm - -### Output - -Evaluation of the support function in the given direction. - -### Algorithm - -Let ``c`` and ``r`` be the center and radius of the ball ``B`` in the 1-norm, -respectively. Then: - -```math -ρ(d, B) = ⟨d, c⟩ + r ‖d‖_∞. -``` -""" -function ρ(d::AbstractVector, B::Ball1) - return dot(d, B.center) + B.radius * maximum(abs, d) -end - -""" - ∈(x::AbstractVector, B::Ball1, [failfast]::Bool=false) - -Check whether a given point is contained in a ball in the 1-norm. - -### Input - -- `x` -- point/vector -- `B` -- ball in the 1-norm -- `failfast` -- (optional, default: `false`) optimization for negative answer - -### Output - -`true` iff ``x ∈ B``. - -### Notes - -The default behavior (`failfast == false`) is worst-case optimized, i.e., the -implementation is optimistic and first computes (see below) the whole sum before -comparing to the radius. -In applications where the point is typically far away from the ball, the option -`failfast == true` terminates faster. - -### Algorithm - -Let ``B`` be an ``n``-dimensional ball in the 1-norm with radius ``r`` and let -``c_i`` and ``x_i`` be the ball's center and the vector ``x`` in dimension -``i``, respectively. -Then ``x ∈ B`` iff ``∑_{i=1}^n |c_i - x_i| ≤ r``. - -### Examples - -```jldoctest -julia> B = Ball1([1.0, 1.0], 1.0); - -julia> [0.5, -0.5] ∈ B -false -julia> [0.5, 1.5] ∈ B -true -``` -""" -function ∈(x::AbstractVector, B::Ball1, failfast::Bool=false) - @assert length(x) == dim(B) "a $(length(x))-dimensional vector is " * - "incompatible with a $(dim(B))-dimensional set" - N = promote_type(eltype(x), eltype(B)) - sum = zero(N) - @inbounds for (i, xi) in enumerate(x) - sum += abs(B.center[i] - xi) - if failfast && sum > B.radius - return false - end - end - return sum <= B.radius -end - -""" - rand(::Type{Ball1}; [N]::Type{<:Real}=Float64, [dim]::Int=2, - [rng]::AbstractRNG=GLOBAL_RNG, [seed]::Union{Int, Nothing}=nothing - ) - -Create a random ball in the 1-norm. - -### Input - -- `Ball1` -- type for dispatch -- `N` -- (optional, default: `Float64`) numeric type -- `dim` -- (optional, default: 2) dimension -- `rng` -- (optional, default: `GLOBAL_RNG`) random number generator -- `seed` -- (optional, default: `nothing`) seed for reseeding - -### Output - -A random ball in the 1-norm. - -### Algorithm - -All numbers are normally distributed with mean 0 and standard deviation 1. -Additionally, the radius is nonnegative. -""" -function rand(::Type{Ball1}; - N::Type{<:Real}=Float64, - dim::Int=2, - rng::AbstractRNG=GLOBAL_RNG, - seed::Union{Int,Nothing}=nothing) - rng = reseed!(rng, seed) - center = randn(rng, N, dim) - radius = abs(randn(rng, N)) - return Ball1(center, radius) -end - -""" - constraints_list(P::Ball1) - -Return the list of constraints of a ball in the 1-norm. - -### Input - -- `B` -- ball in the 1-norm - -### Output - -The list of constraints of the ball. - -### Notes - -In ``n`` dimensions there are ``2^n`` constraints (unless the radius is 0). - -### Algorithm - -The constraints can be defined as ``d_i^T (x-c) ≤ r`` for all ``d_i``, where -``d_i`` is a vector with elements ``1`` or ``-1`` in ``n`` dimensions. To span -all possible ``d_i``, the function `Iterators.product` is used. -""" -function constraints_list(B::Ball1) - n = dim(B) - c, r = B.center, B.radius - N = eltype(B) - clist = Vector{HalfSpace{N,Vector{N}}}(undef, 2^n) - for (i, di) in enumerate(Iterators.product([[one(N), -one(N)] for i in 1:n]...)) - d = collect(di) # tuple -> vector - clist[i] = HalfSpace(d, dot(d, c) + r) - end - return clist -end - -""" - translate!(B::Ball1, v::AbstractVector) - -Translate (i.e., shift) a ball in the 1-norm by the given vector, in-place. - -### Input - -- `B` -- ball in the 1-norm -- `v` -- translation vector - -### Output - -The in-place translated ball in the 1-norm. - -### Algorithm - -We add the vector to the center of the ball. - -### Notes - -See also [`translate(::Ball1, ::AbstractVector)`](@ref) for the out-of-place version. -""" -function translate!(B::Ball1, v::AbstractVector) - @assert length(v) == dim(B) "cannot translate a $(dim(B))-dimensional " * - "set by a $(length(v))-dimensional vector" - c = B.center - c .+= v - return B -end - -function project(B::Ball1, block::AbstractVector{Int}; kwargs...) - return Ball1(B.center[block], B.radius) -end - -""" - reflect(B::Ball1) - -Concrete reflection of a ball in the 1-norm `B`, resulting in the reflected set -`-B`. - -### Input - -- `B` -- ball in the 1-norm - -### Output - -The `Ball1` representing `-B`. - -### Algorithm - -If ``B`` has center ``c`` and radius ``r``, then ``-B`` has center ``-c`` and -radius ``r``. -""" -function reflect(B::Ball1) - return Ball1(-center(B), B.radius) -end - -function scale(α::Real, B::Ball1) - return Ball1(B.center .* α, B.radius * abs(α)) -end +include("Ball1.jl") + +include("ball_norm.jl") +include("center.jl") +include("constraints_list.jl") +include("high.jl") +include("in.jl") +include("isoperationtype.jl") +include("low.jl") +include("project.jl") +include("radius_ball.jl") +include("rand.jl") +include("reflect.jl") +include("scale.jl") +include("support_function.jl") +include("support_vector.jl") +include("translate.jl") +include("vertices_list.jl") end # module diff --git a/src/Sets/Ball1/ball_norm.jl b/src/Sets/Ball1/ball_norm.jl new file mode 100644 index 0000000000..5e4b6e8151 --- /dev/null +++ b/src/Sets/Ball1/ball_norm.jl @@ -0,0 +1,4 @@ +function ball_norm(B::Ball1) + N = eltype(B) + return one(N) +end diff --git a/src/Sets/Ball1/center.jl b/src/Sets/Ball1/center.jl new file mode 100644 index 0000000000..c7972de976 --- /dev/null +++ b/src/Sets/Ball1/center.jl @@ -0,0 +1,16 @@ +""" + center(B::Ball1) + +Return the center of a ball in the 1-norm. + +### Input + +- `B` -- ball in the 1-norm + +### Output + +The center of the ball in the 1-norm. +""" +function center(B::Ball1) + return B.center +end diff --git a/src/Sets/Ball1/constraints_list.jl b/src/Sets/Ball1/constraints_list.jl new file mode 100644 index 0000000000..779f59ec68 --- /dev/null +++ b/src/Sets/Ball1/constraints_list.jl @@ -0,0 +1,34 @@ +""" + constraints_list(P::Ball1) + +Return the list of constraints of a ball in the 1-norm. + +### Input + +- `B` -- ball in the 1-norm + +### Output + +The list of constraints of the ball. + +### Notes + +In ``n`` dimensions there are ``2^n`` constraints (unless the radius is 0). + +### Algorithm + +The constraints can be defined as ``d_i^T (x-c) ≤ r`` for all ``d_i``, where +``d_i`` is a vector with elements ``1`` or ``-1`` in ``n`` dimensions. To span +all possible ``d_i``, the function `Iterators.product` is used. +""" +function constraints_list(B::Ball1) + n = dim(B) + c, r = B.center, B.radius + N = eltype(B) + clist = Vector{HalfSpace{N,Vector{N}}}(undef, 2^n) + for (i, di) in enumerate(Iterators.product([[one(N), -one(N)] for i in 1:n]...)) + d = collect(di) # tuple -> vector + clist[i] = HalfSpace(d, dot(d, c) + r) + end + return clist +end diff --git a/src/Sets/Ball1/high.jl b/src/Sets/Ball1/high.jl new file mode 100644 index 0000000000..8fce3c5859 --- /dev/null +++ b/src/Sets/Ball1/high.jl @@ -0,0 +1,7 @@ +function high(B::Ball1) + return _high_AbstractBallp(B) +end + +function high(B::Ball1, i::Int) + return _high_AbstractBallp(B, i) +end diff --git a/src/Sets/Ball1/in.jl b/src/Sets/Ball1/in.jl new file mode 100644 index 0000000000..b424f3e725 --- /dev/null +++ b/src/Sets/Ball1/in.jl @@ -0,0 +1,54 @@ +""" + ∈(x::AbstractVector, B::Ball1, [failfast]::Bool=false) + +Check whether a given point is contained in a ball in the 1-norm. + +### Input + +- `x` -- point/vector +- `B` -- ball in the 1-norm +- `failfast` -- (optional, default: `false`) optimization for negative answer + +### Output + +`true` iff ``x ∈ B``. + +### Notes + +The default behavior (`failfast == false`) is worst-case optimized, i.e., the +implementation is optimistic and first computes (see below) the whole sum before +comparing to the radius. +In applications where the point is typically far away from the ball, the option +`failfast == true` terminates faster. + +### Algorithm + +Let ``B`` be an ``n``-dimensional ball in the 1-norm with radius ``r`` and let +``c_i`` and ``x_i`` be the ball's center and the vector ``x`` in dimension +``i``, respectively. +Then ``x ∈ B`` iff ``∑_{i=1}^n |c_i - x_i| ≤ r``. + +### Examples + +```jldoctest +julia> B = Ball1([1.0, 1.0], 1.0); + +julia> [0.5, -0.5] ∈ B +false +julia> [0.5, 1.5] ∈ B +true +``` +""" +function ∈(x::AbstractVector, B::Ball1, failfast::Bool=false) + @assert length(x) == dim(B) "a $(length(x))-dimensional vector is " * + "incompatible with a $(dim(B))-dimensional set" + N = promote_type(eltype(x), eltype(B)) + sum = zero(N) + @inbounds for (i, xi) in enumerate(x) + sum += abs(B.center[i] - xi) + if failfast && sum > B.radius + return false + end + end + return sum <= B.radius +end diff --git a/src/Sets/Ball1/isoperationtype.jl b/src/Sets/Ball1/isoperationtype.jl new file mode 100644 index 0000000000..3c2b4cd479 --- /dev/null +++ b/src/Sets/Ball1/isoperationtype.jl @@ -0,0 +1 @@ +isoperationtype(::Type{<:Ball1}) = false diff --git a/src/Sets/Ball1/low.jl b/src/Sets/Ball1/low.jl new file mode 100644 index 0000000000..fc015b1c1b --- /dev/null +++ b/src/Sets/Ball1/low.jl @@ -0,0 +1,7 @@ +function low(B::Ball1) + return _low_AbstractBallp(B) +end + +function low(B::Ball1, i::Int) + return _low_AbstractBallp(B, i) +end diff --git a/src/Sets/Ball1/project.jl b/src/Sets/Ball1/project.jl new file mode 100644 index 0000000000..aba10a5602 --- /dev/null +++ b/src/Sets/Ball1/project.jl @@ -0,0 +1,3 @@ +function project(B::Ball1, block::AbstractVector{Int}; kwargs...) + return Ball1(B.center[block], B.radius) +end diff --git a/src/Sets/Ball1/radius_ball.jl b/src/Sets/Ball1/radius_ball.jl new file mode 100644 index 0000000000..4f11d5fb85 --- /dev/null +++ b/src/Sets/Ball1/radius_ball.jl @@ -0,0 +1,3 @@ +function radius_ball(B::Ball1) + return B.radius +end diff --git a/src/Sets/Ball1/rand.jl b/src/Sets/Ball1/rand.jl new file mode 100644 index 0000000000..413ee98533 --- /dev/null +++ b/src/Sets/Ball1/rand.jl @@ -0,0 +1,34 @@ +""" + rand(::Type{Ball1}; [N]::Type{<:Real}=Float64, [dim]::Int=2, + [rng]::AbstractRNG=GLOBAL_RNG, [seed]::Union{Int, Nothing}=nothing + ) + +Create a random ball in the 1-norm. + +### Input + +- `Ball1` -- type for dispatch +- `N` -- (optional, default: `Float64`) numeric type +- `dim` -- (optional, default: 2) dimension +- `rng` -- (optional, default: `GLOBAL_RNG`) random number generator +- `seed` -- (optional, default: `nothing`) seed for reseeding + +### Output + +A random ball in the 1-norm. + +### Algorithm + +All numbers are normally distributed with mean 0 and standard deviation 1. +Additionally, the radius is nonnegative. +""" +function rand(::Type{Ball1}; + N::Type{<:Real}=Float64, + dim::Int=2, + rng::AbstractRNG=GLOBAL_RNG, + seed::Union{Int,Nothing}=nothing) + rng = reseed!(rng, seed) + center = randn(rng, N, dim) + radius = abs(randn(rng, N)) + return Ball1(center, radius) +end diff --git a/src/Sets/Ball1/reflect.jl b/src/Sets/Ball1/reflect.jl new file mode 100644 index 0000000000..9e47cdab74 --- /dev/null +++ b/src/Sets/Ball1/reflect.jl @@ -0,0 +1,22 @@ +""" + reflect(B::Ball1) + +Concrete reflection of a ball in the 1-norm `B`, resulting in the reflected set +`-B`. + +### Input + +- `B` -- ball in the 1-norm + +### Output + +The `Ball1` representing `-B`. + +### Algorithm + +If ``B`` has center ``c`` and radius ``r``, then ``-B`` has center ``-c`` and +radius ``r``. +""" +function reflect(B::Ball1) + return Ball1(-center(B), B.radius) +end diff --git a/src/Sets/Ball1/scale.jl b/src/Sets/Ball1/scale.jl new file mode 100644 index 0000000000..7e56e36431 --- /dev/null +++ b/src/Sets/Ball1/scale.jl @@ -0,0 +1,3 @@ +function scale(α::Real, B::Ball1) + return Ball1(B.center .* α, B.radius * abs(α)) +end diff --git a/src/Sets/Ball1/support_function.jl b/src/Sets/Ball1/support_function.jl new file mode 100644 index 0000000000..d685e19aec --- /dev/null +++ b/src/Sets/Ball1/support_function.jl @@ -0,0 +1,26 @@ +""" + ρ(d::AbstractVector, B::Ball1) + +Evaluate the support function of a ball in the 1-norm in the given direction. + +### Input + +- `d` -- direction +- `B` -- ball in the 1-norm + +### Output + +Evaluation of the support function in the given direction. + +### Algorithm + +Let ``c`` and ``r`` be the center and radius of the ball ``B`` in the 1-norm, +respectively. Then: + +```math +ρ(d, B) = ⟨d, c⟩ + r ‖d‖_∞. +``` +""" +function ρ(d::AbstractVector, B::Ball1) + return dot(d, B.center) + B.radius * maximum(abs, d) +end diff --git a/src/Sets/Ball1/support_vector.jl b/src/Sets/Ball1/support_vector.jl new file mode 100644 index 0000000000..9571e81599 --- /dev/null +++ b/src/Sets/Ball1/support_vector.jl @@ -0,0 +1,20 @@ +""" + σ(d::AbstractVector, B::Ball1) + +Return the support vector of a ball in the 1-norm in the given direction. + +### Input + +- `d` -- direction +- `B` -- ball in the 1-norm + +### Output + +The support vector in the given direction. +""" +function σ(d::AbstractVector, B::Ball1) + res = copy(B.center) + imax = argmaxabs(d) + res[imax] += sign(d[imax]) * B.radius + return res +end diff --git a/src/Sets/Ball1/translate.jl b/src/Sets/Ball1/translate.jl new file mode 100644 index 0000000000..262879e121 --- /dev/null +++ b/src/Sets/Ball1/translate.jl @@ -0,0 +1,29 @@ +""" + translate!(B::Ball1, v::AbstractVector) + +Translate (i.e., shift) a ball in the 1-norm by the given vector, in-place. + +### Input + +- `B` -- ball in the 1-norm +- `v` -- translation vector + +### Output + +The in-place translated ball in the 1-norm. + +### Algorithm + +We add the vector to the center of the ball. + +### Notes + +See also [`translate(::Ball1, ::AbstractVector)`](@ref) for the out-of-place version. +""" +function translate!(B::Ball1, v::AbstractVector) + @assert length(v) == dim(B) "cannot translate a $(dim(B))-dimensional " * + "set by a $(length(v))-dimensional vector" + c = B.center + c .+= v + return B +end diff --git a/src/Sets/Ball1/vertices_list.jl b/src/Sets/Ball1/vertices_list.jl new file mode 100644 index 0000000000..997bd60531 --- /dev/null +++ b/src/Sets/Ball1/vertices_list.jl @@ -0,0 +1,40 @@ +""" + vertices_list(B::Ball1) + +Return the list of vertices of a ball in the 1-norm. + +### Input + +- `B` -- ball in the 1-norm + +### Output + +A list containing the vertices of the ball in the 1-norm. + +### Notes + +In ``n`` dimensions there are ``2n`` vertices (unless the radius is 0). +""" +function vertices_list(B::Ball1) + # fast evaluation if B has radius 0 + if iszero(B.radius) + return [B.center] + end + VN = _vector_type(B) + vertices = Vector{VN}(undef, 2 * dim(B)) + j = 0 + v = copy(B.center) + @inbounds for i in 1:dim(B) + ci = v[i] + v[i] += B.radius + j += 1 + vertices[j] = copy(v) + v[i] = ci - B.radius + j += 1 + vertices[j] = copy(v) + v[i] = ci # restore old value + end + return vertices +end + +_vector_type(B::Ball1{N,VN}) where {N,VN} = VN From f320e537a6023e36cb55eb009503e963a8cf11f4 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 2 Jul 2024 07:15:43 +0200 Subject: [PATCH 4/4] update docs page for Ball1 --- docs/src/lib/sets/Ball1.md | 80 +++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/docs/src/lib/sets/Ball1.md b/docs/src/lib/sets/Ball1.md index 2c66bd4efe..f8e45e8884 100644 --- a/docs/src/lib/sets/Ball1.md +++ b/docs/src/lib/sets/Ball1.md @@ -12,37 +12,87 @@ Ball1 ```@docs center(::Ball1) -σ(::AbstractVector, ::Ball1) -ρ(::AbstractVector, ::Ball1) -∈(::AbstractVector, ::Ball1, ::Bool=false) -vertices_list(::Ball1) -rand(::Type{Ball1}) constraints_list(::Ball1) -translate!(::Ball1, ::AbstractVector) +rand(::Type{Ball1}) reflect(::Ball1) +vertices_list(::Ball1) +∈(::AbstractVector, ::Ball1, ::Bool=false) +ρ(::AbstractVector, ::Ball1) +σ(::AbstractVector, ::Ball1) +translate!(::Ball1, ::AbstractVector) +``` + +```@meta +CurrentModule = LazySets.API +``` + +Undocumented implementations: +* [`high`](@ref high(::LazySet)) +* [`high`](@ref high(::LazySet, ::Int)) +* [`isoperationtype`](@ref isoperationtype(::Type{LazySet})) +* [`low`](@ref low(::LazySet)) +* [`low`](@ref low(::LazySet, ::Int)) +* [`project`](@ref project(::LazySet, ::AbstractVector{Int})) +* [`scale`](@ref scale(::Real, ::LazySet)) +* [`translate`](@ref translate(::LazySet, ::AbstractVector)) +* [`minkowski_sum`](@ref minkowski_sum(::LazySet, ::LazySet)) +```@meta +CurrentModule = LazySets ``` +* [`ball_norm`](@ref ball_norm(::LazySet)) +* [`radius_ball`](@ref radius_ball(::LazySet)) Inherited from [`LazySet`](@ref): -* [`norm`](@ref norm(::LazySet, ::Real)) -* [`radius`](@ref radius(::LazySet, ::Real)) -* [`diameter`](@ref diameter(::LazySet, ::Real)) * [`area`](@ref area(::LazySet)) -* [`singleton_list`](@ref singleton_list(::LazySet)) +* [`complement`](@ref complement(::LazySet)) +* [`concretize`](@ref concretize(::LazySet)) +* [`constraints`](@ref constraints(::LazySet)) +* [`convex_hull`](@ref convex_hull(::LazySet)) +* [`diameter`](@ref diameter(::LazySet, ::Real)) +* [`eltype`](@ref eltype(::Type{<:LazySet})) +* [`eltype`](@ref eltype(::LazySet)) * [`rectify`](@ref rectify(::LazySet)) -* [`low`](@ref low(::LazySet)) -* [`high`](@ref high(::LazySet)) +* [`singleton_list`](@ref singleton_list(::LazySet)) +* [`surface`](@ref surface(::LazySet)) +* [`vertices`](@ref vertices(::LazySet)) +* [`affine_map`](@ref affine_map(::AbstractMatrix, ::LazySet, ::AbstractVector)) +* [`exponential_map`](@ref exponential_map(::AbstractMatrix, ::LazySet)) +* [`is_interior_point`](@ref is_interior_point(::AbstractVector, ::LazySet)) * [`linear_map`](@ref linear_map(::AbstractMatrix, ::LazySet) +* [`norm`](@ref norm(::LazySet, ::Real)) +* [`radius`](@ref radius(::LazySet, ::Real)) * [`translate`](@ref translate(::LazySet, ::AbstractVector)) +* [`cartesian_product`](@ref cartesian_product(::LazySet, ::LazySet)) +* [`≈`](@ref ≈(::LazySet, ::LazySet)) +* [`==`](@ref ==(::LazySet, ::LazySet)) +* [`isequivalent`](@ref isequivalent(::LazySet, ::LazySet)) +* [`⊂`](@ref ⊂(::LazySet, ::LazySet)) +* [`minkowski_difference`](@ref minkowski_difference(::LazySet, ::LazySet)) + +Inherited from [`AbstractPolyhedron`](@ref): +* [`is_polyhedral`](@ref is_polyhedral(::AbstractPolyhedron)) +* [`intersection`](@ref intersection(::AbstractPolyhedron, ::AbstractPolyhedron)) +* [`isdisjoint`](@ref isdisjoint(::AbstractPolyhedron, ::AbstractPolyhedron)) +* [`⊆`](@ref ⊆(::AbstractPolyhedron, ::AbstractPolyhedron)) Inherited from [`AbstractPolytope`](@ref): * [`isbounded`](@ref isbounded(::AbstractPolytope)) +```@meta +CurrentModule = LazySets.API +``` +* [`isboundedtype`](@ref isboundedtype(::Type{LazySet})) +* [`isconvextype`](@ref isconvextype(::Type{LazySet})) +```@meta +CurrentModule = LazySets +``` +* [`isoperation`](@ref isoperation(::AbstractPolytope)) * [`volume`](@ref volume(::AbstractPolytope)) Inherited from [`AbstractCentrallySymmetricPolytope`](@ref): -* [`dim`](@ref dim(::AbstractCentrallySymmetricPolytope)) -* [`isempty`](@ref isempty(::AbstractCentrallySymmetricPolytope)) * [`an_element`](@ref an_element(::AbstractCentrallySymmetricPolytope)) -* [`isuniversal`](@ref isuniversal(::AbstractCentrallySymmetricPolytope{N}, ::Bool=false) where {N}) * [`center`](@ref center(::AbstractCentrallySymmetricPolytope, ::Int)) +* [`dim`](@ref dim(::AbstractCentrallySymmetricPolytope)) * [`extrema`](@ref extrema(::AbstractCentrallySymmetricPolytope)) * [`extrema`](@ref extrema(::AbstractCentrallySymmetricPolytope, ::Int)) +* [`isempty`](@ref isempty(::AbstractCentrallySymmetricPolytope)) +* [`isuniversal`](@ref isuniversal(::AbstractCentrallySymmetricPolytope{N}, ::Bool=false) where {N})