Skip to content

Commit f1c49b3

Browse files
authored
Merge pull request #2907 from JuliaReach/schillic/1312
#1312 - Add more exponentiation backends
2 parents ef2e31b + 5789959 commit f1c49b3

File tree

10 files changed

+325
-235
lines changed

10 files changed

+325
-235
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ CDDLib = "0.6, 0.7"
2424
Distributions = "0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25"
2525
Documenter = "0.23, 0.24, 0.25, 0.26, 0.27"
2626
Expokit = "0.2"
27+
ExponentialUtilities = "1"
2728
ExprTools = "0.1"
2829
GLPK = "0.11, 0.12, 0.13, 0.14, 0.15"
2930
GLPKMathProgInterface = "0.4, 0.5"
@@ -52,6 +53,7 @@ CDDLib = "3391f64e-dcde-5f30-b752-e11513730f60"
5253
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
5354
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
5455
Expokit = "a1e7a1ef-7a5d-5822-a38c-be74e1bb89f4"
56+
ExponentialUtilities = "d4d017d3-3776-5f7e-afef-a10c40355c18"
5557
GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
5658
IntervalConstraintProgramming = "138f1668-1576-5ad7-91b9-7425abbf3153"
5759
IntervalMatrices = "5c1f47dc-42dd-5697-8aaa-4d102d140ba9"
@@ -68,4 +70,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
6870
WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192"
6971

7072
[targets]
71-
test = ["CDDLib", "Distributions", "Documenter", "Expokit", "GR", "IntervalConstraintProgramming", "IntervalMatrices", "Makie", "Optim", "Polyhedra", "RecipesBase", "StaticArrays", "Symbolics", "TaylorModels", "Test"]
73+
test = ["CDDLib", "Distributions", "Documenter", "Expokit", "ExponentialUtilities", "GR", "IntervalConstraintProgramming", "IntervalMatrices", "Makie", "Optim", "Polyhedra", "RecipesBase", "StaticArrays", "Symbolics", "TaylorModels", "Test"]

docs/src/lib/utils.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ _leq_trig
5454
_random_zero_sum_vector
5555
rectify
5656
require(::Symbol)
57+
require(::Vector{Symbol})
5758
reseed
5859
same_block_structure
5960
substitute
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eval(load_exponentialutilities())

src/LazyOperations/ExponentialMap.jl

Lines changed: 67 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ export SparseMatrixExp,
1010
get_column,
1111
get_columns
1212

13-
function load_expokit()
14-
return quote
15-
16-
using .Expokit: expmv
17-
18-
end end # quote / load_expokit
19-
20-
2113
# --- SparseMatrixExp & ExponentialMap ---
2214

2315
"""
@@ -39,7 +31,7 @@ julia> using SparseArrays
3931
4032
julia> A = sprandn(100, 100, 0.1);
4133
42-
julia> using Expokit
34+
julia> using ExponentialUtilities
4335
4436
julia> E = SparseMatrixExp(A);
4537
@@ -64,10 +56,12 @@ julia> get_columns(E, [10]); # same as get_column(E, 10) but a 100x1 matrix is r
6456
### Notes
6557
6658
This type is provided for use with very large and very sparse matrices.
67-
The evaluation of the exponential matrix action over vectors relies on the
68-
[Expokit](https://github.com/acroy/Expokit.jl) package. Hence, you will have to
69-
install and load this optional dependency to have access to the functionality
70-
of `SparseMatrixExp`.
59+
The evaluation of the exponential matrix action over vectors relies on external
60+
packages such as
61+
[ExponentialUtilities](https://github.com/SciML/ExponentialUtilities.jl) or
62+
[Expokit](https://github.com/acroy/Expokit.jl).
63+
Hence, you will have to install and load such an optional dependency to have
64+
access to the functionality of `SparseMatrixExp`.
7165
"""
7266
struct SparseMatrixExp{N, MN<:AbstractSparseMatrix{N}} <: AbstractMatrix{N}
7367
M::MN
@@ -92,38 +86,39 @@ function size(spmexp::SparseMatrixExp, ax::Int)
9286
return size(spmexp.M, ax)
9387
end
9488

95-
function get_column(spmexp::SparseMatrixExp{N}, j::Int) where {N}
96-
require(:Expokit; fun_name="get_column")
97-
89+
function get_column(spmexp::SparseMatrixExp{N}, j::Int;
90+
backend=get_exponential_backend()) where {N}
9891
n = size(spmexp, 1)
9992
aux = zeros(N, n)
10093
aux[j] = one(N)
101-
return expmv(one(N), spmexp.M, aux)
94+
return _expmv(backend, one(N), spmexp.M, aux)
10295
end
10396

104-
function get_columns(spmexp::SparseMatrixExp{N}, J::AbstractArray) where {N}
105-
require(:Expokit; fun_name="get_columns")
106-
97+
function get_columns(spmexp::SparseMatrixExp{N}, J::AbstractArray;
98+
backend=get_exponential_backend()) where {N}
10799
n = size(spmexp, 1)
108100
aux = zeros(N, n)
109101
res = zeros(N, n, length(J))
110102
@inbounds for (k, j) in enumerate(J)
111103
aux[j] = one(N)
112-
res[:, k] = expmv(one(N), spmexp.M, aux)
104+
res[:, k] = _expmv(backend, one(N), spmexp.M, aux)
113105
aux[j] = zero(N)
114106
end
115107
return res
116108
end
117109

118110
"""
119-
get_row(spmexp::SparseMatrixExp{N}, i::Int) where {N}
111+
get_row(spmexp::SparseMatrixExp{N}, i::Int;
112+
[backend]=get_exponential_backend()) where {N}
120113
121114
Return a single row of a sparse matrix exponential.
122115
123116
### Input
124117
125-
- `spmexp` -- sparse matrix exponential
126-
- `i` -- row index
118+
- `spmexp` -- sparse matrix exponential
119+
- `i` -- row index
120+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
121+
backend
127122
128123
### Output
129124
@@ -135,25 +130,23 @@ This function uses Julia's `transpose` function to create the result.
135130
The result is of type `Transpose`; in Julia versions older than v0.7, the result
136131
was of type `RowVector`.
137132
"""
138-
function get_row(spmexp::SparseMatrixExp{N}, i::Int) where {N}
139-
require(:Expokit; fun_name="get_row")
140-
133+
function get_row(spmexp::SparseMatrixExp{N}, i::Int;
134+
backend=get_exponential_backend()) where {N}
141135
n = size(spmexp, 1)
142136
aux = zeros(N, n)
143137
aux[i] = one(N)
144-
return transpose(expmv(one(N), transpose(spmexp.M), aux))
138+
return transpose(_expmv(backend, one(N), transpose(spmexp.M), aux))
145139
end
146140

147-
function get_rows(spmexp::SparseMatrixExp{N}, I::AbstractArray{Int}) where {N}
148-
require(:Expokit; fun_name="get_rows")
149-
141+
function get_rows(spmexp::SparseMatrixExp{N}, I::AbstractArray{Int};
142+
backend=get_exponential_backend()) where {N}
150143
n = size(spmexp, 1)
151144
aux = zeros(N, n)
152145
res = zeros(N, length(I), n)
153146
Mtranspose = transpose(spmexp.M)
154147
@inbounds for (k, i) in enumerate(I)
155148
aux[i] = one(N)
156-
res[k, :] = expmv(one(N), Mtranspose, aux)
149+
res[k, :] = _expmv(backend, one(N), Mtranspose, aux)
157150
aux[i] = zero(N)
158151
end
159152
return res
@@ -287,14 +280,16 @@ function dim(em::ExponentialMap)
287280
end
288281

289282
"""
290-
σ(d::AbstractVector, em::ExponentialMap)
283+
σ(d::AbstractVector, em::ExponentialMap; [backend]=get_exponential_backend())
291284
292285
Return the support vector of the exponential map.
293286
294287
### Input
295288
296-
- `d` -- direction
297-
- `em` -- exponential map
289+
- `d` -- direction
290+
- `em` -- exponential map
291+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
292+
backend
298293
299294
### Output
300295
@@ -305,28 +300,25 @@ If the direction has norm zero, the result depends on the wrapped set.
305300
306301
If ``E = \\exp(M)⋅S``, where ``M`` is a matrix and ``S`` is a set, it
307302
follows that ``σ(d, E) = \\exp(M)⋅σ(\\exp(M)^T d, S)`` for any direction ``d``.
308-
309-
We allow sparse direction vectors, but will convert them to dense vectors to be
310-
able to use `expmv`.
311303
"""
312-
function σ(d::AbstractVector, em::ExponentialMap)
313-
require(:Expokit; fun_name="σ")
314-
304+
function σ(d::AbstractVector, em::ExponentialMap;
305+
backend=get_exponential_backend())
315306
N = promote_type(eltype(d), eltype(em))
316-
d_dense = d isa Vector ? d : Vector(d)
317-
v = expmv(one(N), transpose(em.spmexp.M), d_dense) # v <- exp(M^T) * d
318-
return expmv(one(N), em.spmexp.M, σ(v, em.X)) # res <- exp(M) * σ(v, S)
307+
v = _expmv(backend, one(N), transpose(em.spmexp.M), d) # v <- exp(M^T) * d
308+
return _expmv(backend, one(N), em.spmexp.M, σ(v, em.X)) # res <- exp(M) * σ(v, S)
319309
end
320310

321311
"""
322-
ρ(d::AbstractVector, em::ExponentialMap)
312+
ρ(d::AbstractVector, em::ExponentialMap; [backend]=get_exponential_backend())
323313
324314
Return the support function of the exponential map.
325315
326316
### Input
327317
328-
- `d` -- direction
329-
- `em` -- exponential map
318+
- `d` -- direction
319+
- `em` -- exponential map
320+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
321+
backend
330322
331323
### Output
332324
@@ -336,16 +328,11 @@ The support function in the given direction.
336328
337329
If ``E = \\exp(M)⋅S``, where ``M`` is a matrix and ``S`` is a set, it
338330
follows that ``ρ(d, E) = ρ(\\exp(M)^T d, S)`` for any direction ``d``.
339-
340-
We allow sparse direction vectors, but will convert them to dense vectors to be
341-
able to use `expmv`.
342331
"""
343-
function ρ(d::AbstractVector, em::ExponentialMap)
344-
require(:Expokit; fun_name="ρ")
345-
332+
function ρ(d::AbstractVector, em::ExponentialMap;
333+
backend=get_exponential_backend())
346334
N = promote_type(eltype(d), eltype(em))
347-
d_dense = d isa Vector ? d : Vector(d)
348-
v = expmv(one(N), transpose(em.spmexp.M), d_dense) # v <- exp(M^T) * d
335+
v = _expmv(backend, one(N), transpose(em.spmexp.M), d) # v <- exp(M^T) * d
349336
return ρ(v, em.X)
350337
end
351338

@@ -354,14 +341,16 @@ function concretize(em::ExponentialMap)
354341
end
355342

356343
"""
357-
∈(x::AbstractVector, em::ExponentialMap)
344+
∈(x::AbstractVector, em::ExponentialMap; [backend]=get_exponential_backend())
358345
359346
Check whether a given point is contained in an exponential map of a set.
360347
361348
### Input
362349
363-
- `x` -- point/vector
364-
- `em` -- exponential map of a set
350+
- `x` -- point/vector
351+
- `em` -- exponential map of a set
352+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
353+
backend
365354
366355
### Output
367356
@@ -387,24 +376,26 @@ julia> [1.0, 1.0] ∈ em
387376
true
388377
```
389378
"""
390-
function (x::AbstractVector, em::ExponentialMap)
391-
require(:Expokit; fun_name="")
392-
379+
function (x::AbstractVector, em::ExponentialMap;
380+
backend=get_exponential_backend())
393381
@assert length(x) == dim(em) "a vector of length $(length(x)) is " *
394382
"incompatible with a set of dimension $(dim(em))"
395383
N = promote_type(eltype(x), eltype(em))
396-
y = expmv(-one(N), em.spmexp.M, x)
384+
y = _expmv(backend, -one(N), em.spmexp.M, x)
397385
return y em.X
398386
end
399387

400388
"""
401-
vertices_list(em::ExponentialMap{N}) where {N}
389+
vertices_list(em::ExponentialMap{N};
390+
[backend]=get_exponential_backend()) where {N}
402391
403392
Return the list of vertices of a (polytopic) exponential map.
404393
405394
### Input
406395
407-
- `em` -- exponential map
396+
- `em` -- exponential map
397+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
398+
backend
408399
409400
### Output
410401
@@ -415,16 +406,15 @@ A list of vertices.
415406
We assume that the underlying set `X` is polytopic.
416407
Then the result is just the exponential map applied to the vertices of `X`.
417408
"""
418-
function vertices_list(em::ExponentialMap{N}) where {N}
419-
require(:Expokit; fun_name="vertices_list")
420-
409+
function vertices_list(em::ExponentialMap{N};
410+
backend=get_exponential_backend()) where {N}
421411
# collect low-dimensional vertices lists
422412
vlist_X = vertices_list(em.X)
423413

424414
# create resulting vertices list
425415
vlist = Vector{Vector{N}}(undef, length(vlist_X))
426416
@inbounds for (i, v) in enumerate(vlist_X)
427-
vlist[i] = expmv(one(N), em.spmexp.M, v)
417+
vlist[i] = _expmv(backend, one(N), em.spmexp.M, v)
428418
end
429419

430420
return vlist
@@ -558,14 +548,17 @@ function dim(eprojmap::ExponentialProjectionMap)
558548
end
559549

560550
"""
561-
σ(d::AbstractVector, eprojmap::ExponentialProjectionMap)
551+
σ(d::AbstractVector, eprojmap::ExponentialProjectionMap;
552+
[backend]=get_exponential_backend())
562553
563554
Return the support vector of a projection of an exponential map.
564555
565556
### Input
566557
567558
- `d` -- direction
568559
- `eprojmap` -- projection of an exponential map
560+
- `backend` -- (optional; default: `get_exponential_backend()`) exponentiation
561+
backend
569562
570563
### Output
571564
@@ -577,22 +570,17 @@ If the direction has norm zero, the result depends on the wrapped set.
577570
If ``S = (L⋅M⋅R)⋅X``, where ``L`` and ``R`` are matrices, ``M`` is a matrix
578571
exponential, and ``X`` is a set, it follows that
579572
``σ(d, S) = L⋅M⋅R⋅σ(R^T⋅M^T⋅L^T⋅d, X)`` for any direction ``d``.
580-
581-
We allow sparse direction vectors, but will convert them to dense vectors to be
582-
able to use `expmv`.
583573
"""
584-
function σ(d::AbstractVector, eprojmap::ExponentialProjectionMap)
585-
require(:Expokit; fun_name="σ")
586-
587-
d_dense = d isa Vector ? d : Vector(d)
588-
daux = transpose(eprojmap.projspmexp.L) * d_dense
574+
function σ(d::AbstractVector, eprojmap::ExponentialProjectionMap;
575+
backend=get_exponential_backend())
576+
daux = transpose(eprojmap.projspmexp.L) * d
589577
N = promote_type(eltype(d), eltype(eprojmap))
590-
aux1 = expmv(one(N), transpose(eprojmap.projspmexp.spmexp.M), daux)
578+
aux1 = _expmv(backend, one(N), transpose(eprojmap.projspmexp.spmexp.M), daux)
591579
daux = _At_mul_B(eprojmap.projspmexp.R, aux1)
592580
svec = σ(daux, eprojmap.X)
593581

594582
aux2 = eprojmap.projspmexp.R * svec
595-
daux = expmv(one(N), eprojmap.projspmexp.spmexp.M, aux2)
583+
daux = _expmv(backend, one(N), eprojmap.projspmexp.spmexp.M, aux2)
596584
return eprojmap.projspmexp.L * daux
597585
end
598586

src/LazySets.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ include("Utils/require.jl")
5151
include("Utils/helper_functions.jl")
5252
include("Utils/macros.jl")
5353
include("Utils/iterators.jl")
54+
include("Utils/matrix_exponential.jl")
5455

5556
# ==================
5657
# Abstract set types

0 commit comments

Comments
 (0)