diff --git a/docs/src/lib/interfaces/AbstractAffineMap.md b/docs/src/lib/interfaces/AbstractAffineMap.md index 0b4d864d1c..056b453da4 100644 --- a/docs/src/lib/interfaces/AbstractAffineMap.md +++ b/docs/src/lib/interfaces/AbstractAffineMap.md @@ -109,6 +109,18 @@ CurrentModule = LazySets ```@docs vertices_list(::AbstractAffineMap) ``` +```@meta +CurrentModule = LazySets.API +``` +```@docs; canonical=false +volume(::LazySet) +``` +```@meta +CurrentModule = LazySets +``` +```@docs +volume(::AbstractAffineMap) +``` ```@meta CurrentModule = LazySets.API diff --git a/src/Interfaces/AbstractAffineMap.jl b/src/Interfaces/AbstractAffineMap.jl index 8b1399d9d9..aa61759472 100644 --- a/src/Interfaces/AbstractAffineMap.jl +++ b/src/Interfaces/AbstractAffineMap.jl @@ -270,3 +270,24 @@ end function linear_map(M::AbstractMatrix, am::AbstractAffineMap) return affine_map(M * matrix(am), set(am), M * vector(am)) end + +""" +# Extended help + + volume(am::AbstractAffineMap) + +### Notes + +This implementation requires a dimension-preserving map (i.e., a square matrix). + +### Algorithm + +A square linear map scales the volume of any set by its absolute determinant. +A translation does not affect the volume. +Thus, the volume of `M * X + {v}` is `|det(M)| * volume(X)`. +""" +function volume(am::AbstractAffineMap) + checksquare(matrix(am)) + + return abs(det(matrix(am))) * volume(set(am)) +end diff --git a/test/LazyOperations/AffineMap.jl b/test/LazyOperations/AffineMap.jl index 51a33e0947..9cfa99eb5a 100644 --- a/test/LazyOperations/AffineMap.jl +++ b/test/LazyOperations/AffineMap.jl @@ -74,6 +74,21 @@ for N in [Float64, Rational{Int}, Float32] @test N[0, 1, 5] ∈ M * L + b3 @test N[0, 0, 0] ∉ M * L + b3 + # volume + B = BallInf(N[0, 0], N(1)) + v = N[-1, 0] + M = N[1 0; 0 1] + @test volume(M * B + v) == N(4) + M = N[1 2; 3 4] + @test volume(M * B + v) ≈ N(8) + M = N[-1 -2; -3 -4] + @test volume(M * B + v) ≈ N(8) + M = N[0 0; 0 0] + @test volume(M * B + v) == N(0) + M = N[0 0;] + @test_throws DimensionMismatch volume(M * B + N[1]) + + # ================================== # Type-specific methods # ================================== @@ -84,10 +99,8 @@ for N in [Float64, Rational{Int}, Float32] #@test am_tr isa Translation && am_tr.v == v # two-dimensional case - B2 = BallInf(zeros(N, 2), N(1)) M = N[1 0; 0 2] - v = N[-1, 0] - am = AffineMap(M, B2, v) + am = AffineMap(M, B, v) # list of vertices check vlist = vertices_list(am) @@ -98,7 +111,7 @@ for N in [Float64, Rational{Int}, Float32] @test h ⊆ am && am ⊆ h # concretize - @test concretize(am) == affine_map(M, B2, v) + @test concretize(am) == affine_map(M, B, v) end # tests that only work with Float64 and Float32