-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TaylorModels and complex evaluation #127
Comments
Here i describe what i think is a work around, using a toy model, which I write as Julia code: using TaylorModels
a = 1 .. 1;
n(z) = a*z;
d(z) = 1+(a*z)^2;
G(z) = n(z)/d(z); So we have a parameter function G(s, ω; a=a)
aux = 1 + a^2*(s^2-ω^2)
rr = a*s*(aux + 2*a^2*ω^2)
ii = a*ω*(aux - 2*a^2*s^2)
den = 4*a^4*s^2*ω^2 + aux^2
return [rr/den, ii/den]
end (I think the algebra and implementation are correct, but do not trust them!) Now I create set_variables("𝓈 ω", order=10);
sm = TaylorModelN(1, 4, IntervalBox(0..0, 2), IntervalBox(-0.5 .. 0.5, 2));
ωm = TaylorModelN(2, 4, IntervalBox(0..0, 2), IntervalBox(-0.5 .. 0.5, 2)); Now, I compute the Taylor model associated to julia> gm = G(sm, ωm)
2-element Vector{TaylorModelN{2, Interval{Float64}, Float64}}:
[1, 1] 𝓈 + [-1, -1] 𝓈³ + [3, 3] 𝓈 ω² + [-1.53286, 1.53286]
[1, 1] ω + [-3, -3] 𝓈² ω + [1, 1] ω³ + [-1.53286, 1.53286]
julia> evaluate(gm, IntervalBox(0..0, 0.125 .. 0.25))
[-1.53286, 1.53286] × [-1.40591, 1.79848] I think this is the kind of result you are seeking. |
Please let me know if this helps... |
Thanks @lbenet ! I think I understand most of it, but I may need to play around with it some more to understand how the domain of using TaylorModels
a = 0.9 .. 1.1
n(z) = a*z
d(z) = 1 + a*z
G(z) = n(z)/d(z) To make the evaluation of import Base: +, -, *, /, ^
# Not a <:Number for convenience of avoiding ambiguities
struct MyComplex{T}
re::T
im::T
end
𝒾 = MyComplex(0, 1)
Base.real(z::MyComplex) = z.re
Base.imag(z::MyComplex) = z.im
Base.to_power_type(z::MyComplex) = z # For power_by_squaring to work
function Base.inv(z::MyComplex)
c, d = reim(z)
den = (c*c + d*d)
return MyComplex(c/den, -d/den)
end
+(x, z::MyComplex) = MyComplex(x+real(z), imag(z))
*(a, z::MyComplex) = MyComplex(a*real(z), a*imag(z))
*(z::MyComplex, a) = MyComplex(real(z)*a, imag(z)*a)
*(z1::MyComplex, z2::MyComplex) = MyComplex(real(z1)*real(z2) - imag(z1)*imag(z2), real(z1)*imag(z2) + imag(z1)*real(z2))
/(a, z::MyComplex) = a*inv(z)
^(z::MyComplex, n::Integer) = Base.power_by_squaring(z, n)
Base.show(io::IO, z::MyComplex) = print(io, "$(real(z)) + $(imag(z))𝒾") Now we can make G(x, y) = [reim(G(x + y*𝒾))...] The part I'm a little confused about is set_variables("𝓈 ω", order=10);
sm = TaylorModelN(1, 4, IntervalBox(0..0, 2), IntervalBox(-0.5 .. 0.5, 2));
ωm = TaylorModelN(2, 4, IntervalBox(0..0, 2), IntervalBox(-0.5 .. 0.5, 2)); Do we need such wide bounds on the domains? Or is that just for the example? I was thinking those would just be exact, since the uncertainty we're interested in here is mostly the parametric uncertainty of julia> p = IntervalBox(0..0, 0.1..0.1);
julia> sm = TaylorModelN(1, 4, p, p)
[1, 1] 𝓈 + [0, 0]
julia> ωm = TaylorModelN(2, 4, p, p)
[0.0999999, 0.100001] + [1, 1] ω + [0, 0] |
I'll try to address your comments:
The variables
You are right, if you have a purely imaginary input
I think you the implementation of
You can change the domain in each direction as well as the expansion point, so the domains may not be identical, nor the expansion points in each direction, and they indeed may be thinner. Note that, while the expansion point may be an interval, it is better it is as thin as possible.
Since you only care about the parametrical uncertainty of julia> G(im*0.1) # using your definition of `G`
[0.00800316, 0.0120028] + [0.088924, 0.109117]im |
This comment lead me to the question: are you interested in the dependence of |
@lbenet @jonniedie you work too hard! leave something to discuss in https://juliareach.github.io/juliareach-days-3/ 😄 |
Yes, I'm mostly interested in parametric uncertainty. Doing Also, even though parametric uncertainty is the main driver here, there is some value in starting with an interval domain for Ultimately the goal is to have some of the functionality (or at least ability to answer the same questions as) the Robust Control Toolbox in MATLAB. ReachabilityAnalysis.jl does a good job of covering robust design and requirements verification for time-domain requirements, but classical linear controls design/analysis is usually done in the frequency domain (or often a mix of the two). |
Thanks @jonniedie for the explanation on the parametric uncertainty. TL;DR I think I'll use your toy model in the framework I tried to describe initially, which requires using using TaylorModels
n(z, a) = a*z
d(z, a) = 1 + a*z
G(z, a) = n(z, a)/d(z, a)
function G(s, ω, a)
aux = 1 + a*s
rr = a*(s*aux + a*ω^2)
ii = a*(ω*aux - a*s*ω)
den = aux^2+(a*ω)^2
return [rr/den, ii/den]
end
julia> dom_a = 0.5±0.125 # domain for `a`
[0.375, 0.625]
julia> dom_ω = 1±1/16 # domain for ω
[0.9375, 1.0625] Note that
julia> imag( G( dom_ω*im, dom_a) )
[0.243974, 0.591016]
julia> G(0.0, dom_ω, dom_a)[2] # imaginary part, using only reals
[0.243974, 0.591016]
julia> hull(getindex.(G.(0.0, dom_ω, mince(dom_a, 8)),2)) # better
[0.296348, 0.506977] Now, I'll consider julia> set_taylor1_varname("δa") # use `δa` as the displayed variable
julia> a1 = TaylorModel1(3, 0.5, dom_a ) # independent variable a -> 0.5 + δa
0.5 + 1.0 δa + [0, 0]
julia> ga1r, ga1i = G(0..0, dom_ω, a1);
julia> ga1i
[0.344821, 0.459069] + [0.242038, 0.702564] δa + [-1.16514, -0.273615] δa² + [-0.539255, 1.00459] δa³ + [-0.0376585, 0.037394]
julia> ga1i(dom_a-mid(dom_a)) # whole domain for `δa` is evaluated
[0.199175, 0.586246] This result is wider that the one obtained using For the specific julia> G1(ra,ia) = [ra, ia] ./ ((1+ra)^2+ia^2);
julia> dom_aω = dom_a * dom_ω # domain of a*ω
[0.351562, 0.664063]
julia> res = G1(0.0, TaylorModel1(3, mid(dom_aω), dom_aω))[2] # mid(dom_aω) is the mid point of dom_aω
0.40370711824930855 + 0.46903360436370056 δa - 0.699648532292137 δa² + 0.19202799764405293 δa³ + [-0.000369503, 0.000641092] Note that julia> res(dom_aω-mid(dom_aω))
[0.312237, 0.478368] This is much better than the naive interval evaluation of I redo this using both julia> set_variables("δa δω", order=6)
2-element Vector{TaylorN{Float64}}:
1.0 δa + 𝒪(‖x‖⁷)
1.0 δω + 𝒪(‖x‖⁷)
julia> aN = TaylorModelN(1, 3, Interval(mid(dom_a)) × Interval(mid(dom_ω)), dom_a×dom_ω)
[0.5, 0.5] + [1, 1] δa + [0, 0]
julia> ωN = TaylorModelN(2, 3, Interval(mid(dom_a)) × Interval(mid(dom_ω)), dom_a×dom_ω)
[1, 1] + [1, 1] δω + [0, 0]
julia> gNi = G(0.0, ωN, aN)[2]
[0.399999, 0.400001] + [0.479999, 0.480001] δa + [0.239999, 0.240001] δω + [-0.704001, -0.703999] δa² + [-0.224001, -0.223999] δa δω + [-0.176001, -0.175999] δω² + [0.179199, 0.179201] δa³ + [-1.13921, -1.13919] δa² δω + [-0.569601, -0.569599] δa δω² + [0.0223999, 0.0224001] δω³ + [-0.000471446, 0.000761742]
julia> gNi( IntervalBox(dom_a-mid(dom_a),dom_ω - mid(dom_ω)) )
[0.309344, 0.479258] This result is not as good as using the function |
An issue of the approach outlined occurs when |
Looks great! Thanks. It looks like the IntervalLinearAlgebra.jl package will help here as well, as sometimes models are given in state-space form, rather than transfer function, and this analysis for those types of models will involve getting the bounding box of the eigenvalues of the state matrix. So I think this should be enough to get me started on tying these ideas in with ControlSystems.jl to make an interval-based robust control systems package. As soon as I have some free time to work on this, I'll put up a repo and keep everyone posted. |
This issue is motivated by a discussion which was opened in TaylorSeries, see JuliaDiff/TaylorSeries.jl#288
Quoting @jonniedie:
The text was updated successfully, but these errors were encountered: