Open Libbum opened 4 years ago
You can extract the rhs from the SINDy result and turn it into a separate equation manually via MTK. I guess if we automate this, we would run into an error creating the iip function, but I didn't tried this yet.
A workaround would be to allow users to modify the result via pushing eqs.
But I am open for discussion here ;)
Thanks @AlCap23. For the moment I'd be happy to manually do it, if it seems like what I need here is an edge case.
What (where) is the iip function? I'm not familiar enough with the process or codebase to know what you mean by that. I guess it could be this section in the basis function?
If so, you're hunch about failing there is probably correct.
If I manually build an ODESystem
:
@derivatives D'~independent_variable(b)
vs = similar(b.variables)
dvs = similar(b.variables)
for (i, vi) in enumerate(b.variables)
vs[i] = ModelingToolkit.Operation(vi.op, [independent_variable(b)])
dvs[i] = D(vs[i])
end
expr = b(vs, parameters(b), independent_variable(b))
# Hardcoded in this case, since I know z=0
eqs = dvs[1:2] .~ expr[1:2]
push!(eqs, dvs[3] ~ 0)
unknown_sys = ODESystem(eqs, independent_variable(b), variables(b), parameters(b))
Then, I cannot construct a basis from it:
unknown_eq = ODEFunction(unknown_sys)
julia> b = Basis((u, p, t)->unknown_eq(u, [1.; 1.], t), u)
ERROR: MethodError: no method matching Basis(::Array{Expression,1}, ::Array{Operation,1}; parameters=Operation[], iv=t)
Edit: Same issue comes up if I build completely from scratch:
@parameters t p[1:3]
@variables x(t) y(t) z(t)
@derivatives D'~t
eqs = [D(x) ~ p[1] * y,
D(y) ~ p[2] * y,
D(z) ~ p[3]]
unknown_sys = ODESystem(eqs)
unknown_eq = ODEFunction(unknown_sys)
b = Basis((u, p, t)->unknown_eq(u, [1.; 1.], t), u) # Error
Although if D(z) ~ p[3] * x
(for example), there is no problem.
I'll try to have a look later on today.
This may end up solving itself once updating to MTK 4.0. I think it has something to do with SciML/ModelingToolkit.jl#433, which all the breaking changes in 518 squashed. The differences between an Expression, Operation and Variable are now mostly removed. I think that Constant(0)
is also no longer needed, which turns the set of Operations in the above case into an Expression.
Upon sleeping on this, of course if I want to do this manually I don't need to use a value in the third equation, so
@parameters t p[1:2]
@variables x(t) y(t) z(t)
@derivatives D'~t
eqs = [D(x) ~ p[1] * y,
D(y) ~ p[2] * y]
recovered_sys = ODESystem(eqs)
recovered_eq = ODEFunction(recovered_sys)
function dudt(u, p, t)
X, Y, Z = u
a, b, c, = p_
y = recovered_eq(u, p, t)
#TODO: The result is flipped? Why?
[y[2] - Z,
X + y[1],
b + Z*(X-c)]
end
works just fine. Apart from the oddity that the order of results end up reversed—no idea why.
Hey! Sorry for not being too responsive. I had a lot on my plate.
Soo, starting from your recent example with slight modifications
using ModelingToolkit
@parameters t p[1:2]
@variables x(t) y(t) z(t)
@derivatives D'~t
eqs = [D(x) ~ p[1] * y,
D(y) ~ p[2] * y]
recovered_sys = ODESystem(eqs)
recovered_eq = ODEFunction(recovered_sys)
function dudt(u, p, t)
X, Y, Z = u
a, b, c = p
y = recovered_eq(u, p, t)
#TODO: The result is flipped? Why?
[y[2] - Z,
X + y[1],
b + Z*(X-c)]
end
@parameters x[1:3] p[1:3] t # For human readable tracing...
recovered_eq(x, p, t)
dudt(x, p, t)
Results in
2-element Array{Operation,1}:
p₁ * x₂
p₂ * x₂
3-element Array{Operation,1}:
p₂ * x₂ - x₃
x₁ + p₁ * x₂
p₂ + x₃ * (x₁ - p₃)
Which checks out. I have been using ModelingToolkit v3.21.0
.
Your first example is indeed an issue. I assumed that a Basis
should always be created with Array{Operation}
. Since the output of the equations is an Expression
, simply converting this works.
using DataDrivenDiffEq
@parameters t p[1:3]
@variables x(t) y(t) z(t)
@derivatives D'~t
eqs = [D(x) ~ p[1] * y,
D(y) ~ p[2] * y,
D(z) ~ p[3]]
unknown_sys = ODESystem(eqs)
unknown_eq = ODEFunction(unknown_sys)
@parameters u[1:3]
b = Basis((u, p, t)->Operation.(unknown_eq(u, [1.0; 1.0; 1.0], t)), u) # Error
b.basis
With an output of
2 dimensional basis in ["u₁", "u₂", "u₃"]
2-element Array{Expression,1}:
u₂
ModelingToolkit.Constant(1.0)
Which is due to the duplicate equations p[1]*y = 1.0*y
and `p[2]y = 1.0y which are consequently removed.
Circling back to the original question: I think we should allow something like a recover_basis(result)
or similar to extract partly complete results. Maybe this work? :)
Hey @AlCap23, thanks for getting back to me. I'm swamped myself, so will take a little to absorb this and respond. Certainly a low priority! :)
I'm hitting up against this assertion error quite a lot.
Perhaps it's my miss-understanding of how to use SINDy correctly; but it's happened in more than one way now.
Current example: Playing around with a Rössler attractor using the standard parameter values. Test system looks like
From which I build a UODE case to 'learn' what the
Y
variable does in each equationWhich gives me a learned dataset
Using the
basis
from the SINDy example documentation, I recoverWhich is exactly what I expect. But since there is no result in
z
/f_3
, then creating anODESystem
fails:This comes about so that
eqs = dvs .~ b(vs, parameters(b), independent_variable(b))
can be a broadcasted operation. But is it possible/advisable to construct anf_3 = 0
orf_3 = p₃
case when a variable is not extant?The other case where I come up against this problem can be discussed using the same example. Consider we know that our unknown does not affect the third equation, but don't know which of the three variables of our system cover the right-most term in the first and second equations. I would write something like:
Notice here there are three input variables into the NN chain, but only two outputs. Since we're not even looking for a result for the third equation here, calling
ODESystem
on the result will again hit the assertion error.If these problems don't really make sense, it'd be nice to discuss how to parse a problem like the above in the documentation, to avoid any future questions of this nature.