Skip to content

Commit

Permalink
revise AffineMap constructors and add examples
Browse files Browse the repository at this point in the history
  • Loading branch information
schillic committed Aug 21, 2019
1 parent 2d70831 commit 01f5075
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 46 deletions.
2 changes: 0 additions & 2 deletions docs/src/lib/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,6 @@ linear_map(::AbstractMatrix{N}, ::Translation{N}) where {N<:Real}

```@docs
AffineMap
*(::AbstractMatrix{N}, ::AffineMap{N}) where {N<:Real}
*(::N, ::AffineMap{N}) where {N<:Real}
dim(::AffineMap)
σ(::AbstractVector{N}, ::AffineMap{N}) where {N<:Real}
ρ(::AbstractVector{N}, ::AffineMap{N}) where {N<:Real}
Expand Down
135 changes: 91 additions & 44 deletions src/AffineMap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,58 @@ An affine map is the composition of a linear map and a translation. This type is
parametric in the coefficients of the linear map, `NM`, which may be different from
the numeric type of the wrapped set (`N`). However, the numeric type of the
translation vector should be `NM`.
### Examples
For the examples we create a ``3×2`` matrix, a two-dimensional unit square, and
a three-dimensional vector.
Then we combine them in an `AffineMap`.
```jldoctest constructors
julia> A = [1 2; 1 3; 1 4]; X = BallInf([0, 0], 1); b2 = [1, 2]; b3 = [1, 2, 3];
julia> AffineMap(A, X, b3)
LinearMap{Int64,BallInf{Int64},Int64,Array{Int64,2}}([1 2; 1 3; 1 4], BallInf{Int64}([0, 0], 1))
```
For convenience, `A` does not need to be a matrix but we also allow to use
`UniformScaling`s resp. scalars (interpreted as a scaling, i.e., a scaled
identity matrix).
Scaling by ``1`` is ignored and simplified to a pure `Translation`.
```jldoctest constructors
julia> am = AffineMap(2I, X, b2)
AffineMap{Int64,BallInf{Int64},Int64,Diagonal{Int64,Array{Int64,1}},Array{Int64,1}}([2 0; 0 2], BallInf{Int64}([0, 0], 1), [1, 2])
julia> AffineMap(2, X, b2) == am
true
julia> AffineMap(1, X, b2)
Translation{Int64,Array{Int64,1},BallInf{Int64}}(BallInf{Int64}([0, 0], 1), [1, 2])
```
Applying a linear map to an `AffineMap` object combines the two maps into a new
`AffineMap` instance.
Again we can make use of the conversion for convenience.
```jldoctest constructors
julia> B = [2 0; 0 2]; am2 = B * am
AffineMap{Int64,BallInf{Int64},Int64,Array{Int64,2},Array{Int64,1}}([4 0; 0 4], BallInf{Int64}([0, 0], 1), [2, 4])
julia> 2 * am == am2
true
```
The `AffineMap` applied to a `ZeroSet` or an `EmptySet` is simplified
automatically.
```jldoctest constructors
julia> AffineMap(A, ZeroSet{Int}(2), b3)
Singleton{Int64,Array{Int64,1}}([1, 2, 3])
julia> AffineMap(A, EmptySet{Int}(), b3)
EmptySet{Int64}()
```
"""
struct AffineMap{N<:Real, S<:LazySet{N}, NM, MAT<:AbstractMatrix{NM},
VN<:AbstractVector{NM}} <: LazySet{N}
Expand All @@ -35,71 +87,66 @@ struct AffineMap{N<:Real, S<:LazySet{N}, NM, MAT<:AbstractMatrix{NM},
v::VN

# default constructor with dimension match check
function AffineMap{N, S, NM, MAT, VN}(M::MAT, X::S, v::VN) where {N<:Real,
S<:LazySet{N}, NM, MAT<:AbstractMatrix{NM}, VN<:AbstractVector{NM}}
function AffineMap(M::MAT, X::S, v::VN) where {N<:Real, S<:LazySet{N}, NM,
MAT<:AbstractMatrix{NM},
VN<:AbstractVector{NM}}

@assert dim(X) == size(M, 2) "a matrix of size $(size(M)) cannot be " *
"applied to a set of dimension $(dim(X))"

@assert size(M, 1) == length(v) "a map with output dimension $(size(M, 1)) " *
"is incompatible with the dimension of the translation vector, $(length(v))"
@assert size(M, 1) == length(v) "a map with output dimension " *
"$(size(M, 1)) is incompatible with a translation vector of " *
"dimension $(length(v))"

return new{N, S, NM, MAT, VN}(M, X, v)
end
end

# convenience constructor without type parameter
AffineMap(M::MAT, X::S, v::VN) where {N<:Real, S<:LazySet{N}, NM,
MAT<:AbstractMatrix{NM}, VN<:AbstractVector{NM}} = AffineMap{N, S, NM, MAT, VN}(M, X, v)

# convenience constructor from the identity: a pure translation
# convenience constructor from a UniformScaling
function AffineMap(M::UniformScaling, X::LazySet, v::AbstractVector)
return Translation(X, v)
return AffineMap(M.λ, X, v)
end

# ============================
# Arithmetic functions
# ============================

"""
```
*(M::AbstractMatrix{N}, am::AffineMap{N}) where {N<:Real}
```
Transform an affine map under matrix multiplication.
### Input
- `M` -- matrix
- `am` -- affine map
# convenience constructor from a scalar
function AffineMap::N, X::LazySet, v::AbstractVector) where {N<:Real}
if α == one(N)
return Translation(X, v)
end
return AffineMap(Diagonal(fill(α, length(v))), X, v)
end

### Output
# simplification for a LinearMap
function LinearMap(map, am::AffineMap)
return AffineMap(map * am.M, am.X, map * am.v)
end

A lazy affine map, i.e. an `AffineMap`, such that the new map is related to the
old one through `am.M ↦ M * am.M` and `am.v ↦ M * am.v`.
"""
function LinearMap(M::AbstractMatrix{N}, am::AffineMap{N}) where {N<:Real}
# disambiguation
function LinearMap(M::AbstractMatrix, am::AffineMap)
return AffineMap(M * am.M, am.X, M * am.v)
end

"""
```
*(α::N, am::AffineMap{N}) where {N<:Real}
```
Return the affine map scaled by a given number.
function LinearMap::Real, am::AffineMap)
return AffineMap* am.M, am.X, α * am.v)
end

### Input
# ZeroSet is "almost absorbing" for the linear map (only the dimension changes)
# such that only the translation vector remains
function AffineMap(M::AbstractMatrix{N}, Z::ZeroSet{N}, v::AbstractVector{N}
) where {N<:Real}
@assert dim(Z) == size(M, 2) "a matrix of size $(size(M)) cannot be " *
"applied to a set of dimension $(dim(Z))"

- `α` -- scalar
- `am` -- affine map
@assert size(M, 1) == length(v) "a map with output dimension " *
"$(size(M, 1)) is incompatible with a translation vector of " *
"dimension $(length(v))"

### Output
return Singleton(v)
end

The scaled affine map.
"""
function *::N, am::AffineMap{N}) where {N<:Real}
return AffineMap* am.M, am.X, α * am.v)
# EmptySet is absorbing for LinearMap
function AffineMap(M::AbstractMatrix{N}, ∅::EmptySet{N}, v::AbstractVector{N}
) where {N<:Real}
return
end

# ============================
Expand Down

0 comments on commit 01f5075

Please sign in to comment.