@ChrisRackauckas: concerning (3), you could make it a
RKIntegrator and then just overload the
onstep! etc functions to use the unrolled stuff. For the user it would then be just like the other RK-solvers, just faster.
@pwl: I love the docs! Tnx!
Also, looking through both your and my PR (#19 & #17), I have the feeling we should make
tstop part of
Problem. It features in the options everywhere, so it should be in the most central place. Then
tsteps would be part of the options for fixed-step solvers (with
@assert tsteps[end]==tstop), adaptive solvers wouldn't need any time, and dense output has
IVP? On one hand, there are common options showing up in each solver/integrator, on the other hand keeping all options, without exceptions, in the solver/integrator makes our types isolated and keeps the communication between them to minimum.
tdirmight be more painful to use and implement but I would like to see it as a part of an internal implementation of a solver. Not everyone will want to use
tdirin exactly the same form, some solvers might even choose not implement the reverse time integration at all.
Solvertypes in there and a single int
IVPis immutable and it could get in conflict with
tdiraccording to keyword arguments
Regarding #17 :
I think having
solve(prob) would be putting too much stuff into our interface, which exacerbates the problem with naming that we already have. I was imagining something like this (using the current names for clarity):
ode for users who "just came here to solve an equation".
solve as an iterator constructor and the recommended way to deal with differential equations.
We already have a function that "solves" the
Problem, and it's called
collect. It is clear what it does and it is consistent with how Julia deals with iterators. I don't think there is enough reason to introduce a special name for what you suggest as
solve(prob), which is essentially a transpose of
collect (tuple of arrays instead of array of tuples coming from
collect), we could call it
collect_transpose or something but calling it
solve has several issues: it would be a waste of a good name (we are short on these), it would suggest that it does more then it actually does (it is
collect in a disguise) and it would cause confusion in the long run (isn't iterating the same as solving? which one is the recommended way? What's the difference between
And, although I agree that
ode is not the best name I wouldn't rename it to
solve either. It would suggest that it is more general then it is, in practice it can only solve explicit ODEs. If we want to call it
solve we should think about generalizing it to other problems too, which is, in my opinion, beyond the scope of this PR.
I think that, independently of the name we choose for
solve, we have to make it very clear in the docs that
solve returns an iterable object and it can be, and should be, dealt with just as any other iterable object in Julia. This is way more important then a name we pick (let's face it, there is no perfect choice here). And if you don't know how iterators work, feel free to use
ode, which is still powerful enough for most use cases. Later on we could add functions like
implicit to solve other types of equations but that's a different story.
I wouldn't mind renaming
problem that much (my only gripe with it is the
problem(initial value problem) thing), but I would mind having
collect and similar utility functions. That would be bad because people would no longer think in terms of iterators and that would be just one too many different ways to do the same thing, which is bad because we would have to maintain all of them at the same time.
I think it is fine and should be encouraged to not use the iterators if not needed. The mostly empty loops in our examples are testimony that usually iterators are not needed. Calling the function which solves an ode
ode is fine, but
solve would be good too.
The problem with
collect is that its return is essentially unusable: a vector of tuples.
collect_transpose's return is usable, but still suffers from
y being a vector and the old
hcat(y...) problem. Also
collect_transpose is really obscure.
So, having something like
ode, returning something useful, namely a vector for
t and an array for
y if applicable, will be important to make ODE.jl easily usable.
ybeing a vector? In contrast to what?
collect_transpose, or whatever you call it, is essentially an extremely simple wrapper around an iterator. It's still far from something that a name
collect_transposeas to how to accumulate the output
collect_transposeis the right name for the function we need but it's closer to what it actually does then
as for the output, let's say I have a custom type to hold points:
type Point<:AbstractVector x;y end getindex(::Point)=...
there is no way you can push it into a
Matrix without loosing its interpretation as a point.
Moreover, how do you imagine handling the matrix if you don't know it's size a priori? The only way you will know the size is to run the iteration, but then you still have to store the output at the same time. But if you are already storing the output (as a vector) then you would only loose on performance if you want to convert it in afterward (e.g. to a matrix).
f=solve(ode)giving an interpolable object with call overloaded so that
dy(t). And with
getindexdefined so that
Yes, you're right, changing the return type depending on a keyword argument is bad.
Concerning your example, yes, there can be corner cases. The beauty of building on iterators, is that one can always drop down to them and do what one wants. Alternatively, we could only return a matrix when
isa(y0, Vector), that should cover 95% of the use-cases.
When the number of output points is not known, then make a vector, push to that and reshape in the end. (Note that the reshape does not copy but just re-interprets the memory as matrix).
I still don't want to call something a solution which is not a solution at all but can only be used to, maybe, generate a solution.