-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathdiscretize.jl
349 lines (266 loc) · 11 KB
/
discretize.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
"""
AbstractDiscretizationAlgorithm
Abstract supertype for all discretization algorithms.
### Note
For implementing a custom discretization algorithm, a type definition
`struct NewDiscretizationAlgorithm <: AbstractDiscretizationAlgorithm end`
and a `_discretize` method
```julia
_discretize(::NewDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix, c::AbstractVector, D::AbstractMatrix)
```
are required.
"""
abstract type AbstractDiscretizationAlgorithm end
"""
ExactDiscretization <: AbstractDiscretizationAlgorithm
Exact discretization algorithm for affine systems.
### Algorithm
This algorithm consists of integrating the continuous differential equation over
a specified time interval to obtain an associated difference equation.
The algorithm applies to any system of the form `x' = Ax + Bu + c + Dw` where the
state matrix `A` is invertible, and other system types, e.g. linear systems
`x' = Ax` which are included in the above formulation.
Without loss of generality, consider a `NoisyAffineControlledContinuousSystem`
with system dynamics ``x' = Ax + Bu + c + Dw``.
The exact discretization is calculated by integrating on both sides of the
continuous ODE over the time span `[t, t + ΔT]`, for a fixed input `u` and fixed
noise realization `w` at time `t`.
The resulting discretization writes as
``x^+ = A^d x + B^d u + c^d + D^d w`` where
``A^d = \\exp^{A ~ ΔT}``,
``B^d = A^{-1}(A^d - I)B``,
``c^d = A^{-1}(A^d - I)c`` and
``D^d = A^{-1}(A^d - I)D``.
The algorithm described above is a well known result from the literature [1].
[1] [Wikipedia](https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models)
"""
struct ExactDiscretization <: AbstractDiscretizationAlgorithm end
"""
EulerDiscretization <: AbstractDiscretizationAlgorithm
Euler discretization algorithm for affine systems.
### Algorithm
This algorithm consists of a first-order approximation to obtain an associated
difference equation. The algorithm applies to any system of the form
`x' = Ax + Bu + c + Dw`, and other system types, e.g. linear systems `x' = Ax`
which are included in the above formulation.
Without loss of generality, consider a `NoisyAffineControlledContinuousSystem`
with system dynamics ``x' = Ax + Bu + c + Dw``.
The Euler discretization is calculated by taking the first-order approximation
of the exact discretization [`ExactDiscretization`](@ref).
The resulting discretization writes as
``x^+ = A^d x + B^d u + c^d + D^d w``
where ``A^d = I + ΔT ~ A``, ``B^d = ΔT ~ B``,
``c^d = ΔT ~ c`` and ``D^d = ΔT ~ D``.
The algorithm described above is a well-known result from the literature [1].
[1] [Wikipedia](https://en.wikipedia.org/wiki/Discretization#Approximations)
"""
struct EulerDiscretization <: AbstractDiscretizationAlgorithm end
"""
discretize(system::AbstractContinuousSystem, ΔT::Real,
algorithm::AbstractDiscretizationAlgorithm=ExactDiscretization(),
constructor=_default_complementary_constructor(system))
Discretization of a `isaffine` `AbstractContinuousSystem` to a
`AbstractDiscreteSystem` with sampling time `ΔT` using the discretization
method `algorithm`.
### Input
- `system` -- an affine continuous system
- `ΔT` -- sampling time
- `algorithm` -- (optional, default: `ExactDiscretization()`) discretization algorithm
- `constructor` -- (optional, default: `_default_complementary_constructor(system)`) construction method
### Output
Returns a discretization of the input system `system` with discretization method
`algorithm` and sampling time `ΔT`.
"""
function discretize(system::AbstractContinuousSystem, ΔT::Real,
algorithm::AbstractDiscretizationAlgorithm=ExactDiscretization(),
constructor=_default_complementary_constructor(system))
(!isaffine(system)) && throw(ArgumentError("system needs to be affine"))
sets(x) = x ∈ [:X, :U, :W]
matrices(x) = x ∈ [:A, :B, :b, :c, :D]
# get all fields from system
fields = collect(fieldnames(typeof(system)))
# get the fields of `system` that are parameters of the affine system dynamics
# i.e., all fields that need to be discretized
cont_values = [getfield(system, f) for f in filter(matrices, fields)]
# compute discretized values of dynamics_params_c
disc_values = _discretize(algorithm, ΔT, cont_values...)
# get the fields of `system` that are sets
set_values = [getfield(system, f) for f in filter(sets, fields)]
# build the new discrete type according to the constructor method with the
# discretized and set values
return constructor(disc_values, set_values)
end
# returns a function with signature 'Vector × Vector -> AbstractDiscreteSystem'
function _default_complementary_constructor(system)
# get corresponding discrete type of `system`
discrete_type = _complementary_type(typename(system))
# build the new discrete type with the discretized and set values
return (disc_values, set_values) -> discrete_type(disc_values..., set_values...)
end
"""
_discretize(::AbstractDiscretizationAlgorithm, ΔT::Real
A::AbstractMatrix, B::AbstractMatrix, c::AbstractVector, D::AbstractMatrix)
Implementation of the discretization algorithm defined by the first input argument
with sampling time `ΔT`.
### Input
- `` -- discretization algorithm, used for dispatch
- `ΔT` -- sampling time
- `A` -- state matrix
- `B` -- input matrix
- `c` -- vector
- `D` -- noise matrix
### Output
Returns a vector containing the discretized input arguments `A`, `B`, `c` and `D`.
### Notes
See [`discretize`](@ref) for more details.
"""
function _discretize(::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix,
B::AbstractMatrix,
c::AbstractVector,
D::AbstractMatrix)
end
function _discretize(::ExactDiscretization, ΔT::Real,
A::AbstractMatrix,
B::AbstractMatrix,
c::AbstractVector,
D::AbstractMatrix)
if rank(A) == size(A, 1)
A_d = exp(A * ΔT)
Matr = inv(A) * (A_d - I)
B_d = Matr * B
c_d = Matr * c
D_d = Matr * D
else
throw(ArgumentError("exact discretization for singular state matrix " *
"(i.e., A is non-invertible) is not implemented; " *
"use the `EulerDiscretization` algorithm"))
end
return [A_d, B_d, c_d, D_d]
end
function _discretize(::EulerDiscretization, ΔT::Real,
A::AbstractMatrix,
B::AbstractMatrix,
c::AbstractVector,
D::AbstractMatrix)
A_d = I + ΔT * A
B_d = ΔT * B
c_d = ΔT * c
D_d = ΔT * D
return [A_d, B_d, c_d, D_d]
end
"""
_discretize(A::AbstractMatrix, ΔT::Real; algorithm=:exact)
Discretize the state matrix `A` with sampling time `ΔT` and discretization method
`algorithm`.
### Input
- `A` -- state matrix
- `ΔT` -- sampling time
- `algorithm` -- (optional, default: `:exact`) discretization algorithm
### Output
Returns a vector containing the discretized input argument `A`.
### Notes
See [`discretize`](@ref) for more details.
"""
function _discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix)
n = size(A, 1)
mzero = spzeros(n, n)
vzero = spzeros(n)
A_d, _, _, _ = _discretize(algorithm, ΔT, A, mzero, vzero, mzero)
return [A_d]
end
"""
_discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix)
Discretize the state matrix `A` and input or noise matrix `B` with sampling time
`ΔT` and discretization method `algorithm`.
### Input
- `algorithm` -- discretization algorithm
- `ΔT` -- sampling time
- `A` -- state matrix
- `B` -- input or noise matrix
### Output
Returns a vector containing the discretized input arguments `A` and `B`.
### Notes
This method signature with two arguments of type `AbstractMatrix` works both for
a noisy system with fields `(:A,:D)` and a controlled system with fields `(:A,:B)`.
See [`discretize`](@ref) for more details.
"""
function _discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix)
n = size(A, 1)
mzero = spzeros(n, n)
vzero = spzeros(n)
A_d, B_d, _, _ = _discretize(algorithm, ΔT, A, B, vzero, mzero)
return [A_d, B_d]
end
"""
_discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix,c::AbstractVector)
Discretize the state matrix `A` and vector `c` with sampling time `ΔT` and
discretization method `algorithm`.
### Input
- `algorithm` -- discretization algorithm
- `ΔT` -- sampling time
- `A` -- state matrix
- `c` -- vector
### Output
Returns a vector containing the discretized input arguments `A` and `c`.
### Notes
See [`discretize`](@ref) for more details.
"""
function _discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, c::AbstractVector)
n = size(A, 1)
mzero = spzeros(n, n)
A_d, _, c_d, _ = _discretize(algorithm, ΔT, A, mzero, c, mzero)
return [A_d, c_d]
end
"""
_discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix, c::AbstractVector)
Discretize the state matrix `A`, input matrix `B` and vector `c` with sampling
time `ΔT` and discretization method `algorithm`.
### Input
- `algorithm` -- discretization algorithm
- `ΔT` -- sampling time
- `A` -- state matrix
- `B` -- input matrix
- `c` -- vector
### Output
Returns a vector containing the discretized input arguments `A`, `B` and `c`.
### Notes
See [`discretize`](@ref) for more details.
"""
function _discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix, c::AbstractVector)
n = size(A, 1)
mzero = spzeros(n, n)
A_d, B_d, c_d, _ = _discretize(algorithm, ΔT, A, B, c, mzero)
return [A_d, B_d, c_d]
end
"""
_discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix, D::AbstractMatrix)
Discretize the state matrix `A`, input matrix `B` and noise matrix `C` with
sampling time `ΔT` and discretization method `algorithm`.
### Input
- `algorithm` -- discretization algorithm
- `ΔT` -- sampling time
- `A` -- state matrix
- `B` -- input matrix
- `D` -- noise matrix
### Output
Returns a vector containing the discretized input arguments `A`, `B` and `D`.
### Notes
See [`discretize`](@ref) for more details.
"""
function _discretize(algorithm::AbstractDiscretizationAlgorithm, ΔT::Real,
A::AbstractMatrix, B::AbstractMatrix, D::AbstractMatrix)
n = size(A, 1)
vzero = spzeros(n)
A_d, B_d, _, D_d = _discretize(algorithm, ΔT, A, B, vzero, D)
return [A_d, B_d, D_d]
end