-
-
Notifications
You must be signed in to change notification settings - Fork 231
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
real and complex noises? #145
Comments
This error is because https://github.com/JuliaDiffEq/DiffEqBase.jl/blob/master/src/problems/sde_problems.jl#L19 That you can pass in a noise process, but https://github.com/JuliaDiffEq/DiffEqBase.jl/blob/master/src/noise_process.jl#L9 it's just a function. It should be a function It will even go a little further. One way is that it will allow many forms of temporal noise. One way these are implemented is by generating them from an SDE. This will allow one to use a system of equations, and make say the first equation have white-noise via I am thinking of actually expanding it all the way to For point 2, I will make the default always type matching. However, one the noise type is more flexible like this, a user who wants real noise with a complex-valued independent variable will be able to easily define a new
Thanks, I'll use this.
I think it would be easiest to just allow the direct construction. It's not difficult to write the function, but it would heavily clutter the API to have a lot of "complex-variable specific" parts around. Again, I think it should be supported for users to tweak these parts, but the "simple path" needs to define where it ends, otherwise it will end up not so simple! |
Yes, I think the simplest situation is: trigger the noise based on integrated type, and allow direct construction for those cases not covered by the default. This means that
with with dwj standard Gaussian Wiener processes having mean(dW^*(t)dW(t))=dt and mean(dW(t)^2) = mean([dW(t)^*]^2)=0.
In this context the case of full generality should then, for each complex variable passed, allow access to two independent real noises, one for each real valued degree of freedom. Presumably you could allow an arbitrary number to be called, and this would cover the common case where less noises are needed than variables. The main reason for this: complex variables allow much simpler "pseudo-code" input. At the moment I would need to reduce my problem to a much more complicated set of equations for each of the real degrees of freedom. For a few variables, no problem, but for multimode fields, this quickly becomes very unwieldy. What you are proposing could be used to construct stochastic field theory problems quite directly, I believe. The real advantage of doing this in Julia: complete freedom of setup of the integrated type. One should not be restricted to certain discretizations of the fields, and can build arbitrary setup and analysis code around the integrator. Super cool! |
Expand on this a little bit more? You mean like allow multiple dW processes? That can be done by making the problem take in a tuple, and have |
So if I understand: I could pass a tuple of coefficients that forms a linear transformation acting on two real noises? Then for each complex variable
and default would be dW[j] is complex Wiener as above (two real noises for each complex variable), but in general I could pass a tuple of coefficients (a,b) allowing arbitrary linear combinations The general underlying problem is of the form |
Yes. The proposal is to allow you to specify
which is something that was recently lost with the inplace noise process update (though admittedly, the previous way was archaic and limited). Again, each default noise process would default to type-matching, so if I realized that we will need a toggle in |
(sorry for some repetition here) I am not so sure that this level of generality is necessary. The general multimode stochastic problem can be reduced to the form
where
which seems a bit more specific that what you have written above, that would generate a column vector of noises for each So I think if you replace the product Maybe I am missing something: is If that was the case, there could be a nice reason to have the more general constructor you propose by allowing more noise vectors if it means more scope for colored noise, fractional Brownian Motion, etc. To answer your question:
it would be the same, provided |
I see what you're saying. Instead of having The slightly difficult thing is that we allow more than just equations on vectors. The reason is that using matrices or tensors for
Let's clarify:
In the current setup the A necessary input into the Example: u = Vector{Float64}(10)
SDEProblem(f,g,u0,tspan) Makes SDEProblem(f,g,u0,tspan,ito_dims = 5) Makes u = Matrix{Float64}(10,8)
SDEProblem(f,g,u0,tspan,ito_dims = 5) Makes u = Matrix{Complex128}(10,8)
SDEProblem(f,g,u0,tspan,ito_dims = 5) would be the same sizing as before, but now u = Matrix{Complex128}(10,8)
SDEProblem(f,g,u0,tspan,ito_dims = 5,noise=my_noise_process) where we change the noise process by defining noise_func(t,u,W,rand_vec) = randn!(rand_vec,Float64) which will fill it with only real-valued numbers, which would convert to I think this is what we're looking for? |
Some caveats to address: Are element-wise operations on a What do we do about If these are worked out, then the above has a good implementation. |
I think this is likely a good way to go. Most problems would be handled by What if I have
but now instead of
I want
For a common problem I look at it, the construction is:
So getting This may sound a bit perverse, but it is the general form of the optimised algorithm. There are other stochastic field theory problems that can be cast in this form. So from a solver point of view, The problem comes down to having the freedom to a work in a convenient spatial basis, rather than being forced onto a regular spatial grid. For open quantum systems problems this becomes essential... |
No, this is a random differential equation (RDE), not an SDE. There is no way to guarantee that the user will treat it as an SDE.
No, it needs to not be able to do that. There are many methods for which more control over the application of the noise process to the function http://epubs.siam.org/doi/pdf/10.1137/09076636X Anything involving iterated stochastic integrals wouldn't be possible. Only Euler methods will work with this (or of course, RDE methods).
Are you sure this construction isn't solving a related RDE? Do you have a paper showing the convergence of this? I am curious to see this all written out to see if I can support it properly. Note that I am interested in the RDE case, and will get something going when Kloden releases his next book: |
Ok, it may be true that formally it is an RDE once you go to an arbitrary basis, but the underlying object is definitely an SDE. Papers are http://journals.aps.org/pre/abstract/10.1103/PhysRevE.89.013302 (full numerical method, fairly lengthy) and for shorter overview http://journals.aps.org/pra/abstract/10.1103/PhysRevA.86.053634 Even just Euler would be fine though, if that is the only algorithm that is formally valid. Is it possible to let the user do it, and only allow Euler as an integrator? In fact, this is what we have always used as we know it is robust. Semi-implicit Euler for SDE in Stratonovich form. We have used RK methods quite a bit for weak additive noise in this context, but the full Stochastic GPE needs a lower order method due to multiplicative noise. I guess the point is this: it is only obviously an SDE in a particular representation, due to the fact that is coming from a projected field theory. |
I see. Thanks for the reference. Yeah, even though it's an SDE in some representation, the way it's written is not an SDE unless you can make that That said, it does follow the general form form for an RDE, so those methods definitely apply. And because the Euler methods still apply to RDEs, IIRC Euler-Maruyama -> Ito and Euler-Heun -> Stratonovich in this form. But there are methods which will do well for these. And adaptivity should carry over using the RODE-SDE pair approach, though a proof of convergence would technically be needed. Reference: http://publikationen.ub.uni-frankfurt.de/frontdoor/index/index/docId/40146 I think Ito/Stratonovich-driven RODEs should get their own type, since they likely will be important enough. The can plug into the StochsticDiffEq.jl stuff though to get the adaptivity, interpolations, and event handling with a few modifications. But I am not going to commit to tackling this right now: this'll have to wait a bit. |
Ok, very interesting! I look forward to seeing how this develops. Putting aside the RDE issue (which would be awesome to solve), I think your proposal will expand the scope in very worthwhile ways. It would be great if Euler was enabled for RODEs in the meantime, and you allowed that level of construction with noise. That would mean we could start writing code to your interface, and then hope to use more interesting algorithms in future as they get implemented. Is there scope for doing that? |
Yeah. That's not difficult. I think it would be best to build solvers for Wiener process driven RODEs into StochasticDiffEq.jl. The roadmap would be:
That should be all that's needed? Event handling may be broken with this though, but most of the stuff should work. I need to finish up a paper right now though. |
+1 for this, and for your paper getting finished |
I made a bit of progress here. The complex numbers stuff is implemented, and so is the RODEs, but not the non-diagonal noise things. I have a lot of plans there, and will likely do it today. Note that none of this is "currently available": it's part of a 7 repository API change so please just wait for the release to try it. Side Note: RODEs and The Projective MethodBut I want to point out that what you have isn't easily seen as an RODE either. Those fall into the form: u' = f(t,u,W) that is, they use the solution of some random process. Also, in your project formulas, you need the dt too, for example dψ|M = ≡ P((-i/hbar)*V(r,t)*ψ*dt + i*ψ*dW*M(r,t)) there, using u' = f(t,u,dt,dW) Again, I looked that up and am coming up blank. But, there is a convoluted way of writing that as an RODE... and I can use Lipschitz arguments to show certain forms of convergence. So the Euler type method u_{n+1} = u_n + f(t,u,dt,dW) where the directly uses the increments in-place actually makes sense from an algorithm perspective (it should converge), but this is a bad idea. No only is there no way to ensure that the "user does this correctly", but also you can't put any algorithms on this. For example, when I say it converges, the simple way of putting in the values for True AnswerIn the algorithm you want to do, you just need to project after doing the update. Because we know that this converges (under appropriate assumptions), I think it's fine to explicitly do that projection to the SDE. I think the best way to implement this algorithm as an SDE would be in two steps. The first step is to define condition = (t,u,integrator) -> true Then do your affect: function affect!(integrator)
# do something to change integrator.u
# i.e. do your projection here,
end Then make this via Then you would use sol = solve(prob,EM(),callback=cb) That will make it use the Euler-Maruyama method, and project after each update (before each save, see the sol = solve(prob,EulerHeun(),callback=cb) and I'll implement the |
First thoughts: this looks great! There are a bunch of advantages to casting the whole algorithm+formulation in this way. Projected functional calculus tries to achieve this same goal: set up a calculus that is basically as clean and useable as functional calculus (i.e. work as if there are no projectors), but with projectors at appropriate points to achieve a consistent representation of the field theory on a finite basis set. Nagging worry: this is a shift of perspective so that Let's say I want to implement a three-body loss term, where my nonlinear term in the RHS that I want to project is now of the form Should I go back to position again to get back my field Put another way, it looks like you are saying "just project at the end of the step" which is cool, but: Some kind of answer |
I'm not sure I understand everything you're saying, but:
Callbacks are for injecting arbitrary code to act on the |
OK! I think this could be a totally kick ass solution. One further thought: do the callbacks give enough freedom that the noise triggering can be set by them? If I take the simplest case of additive noise (simple-growth Stochastic Projected Gross-Pitaevskiie equation), the noise is additive in the coefficient space of the representation
Now if I need to pass the spatial field For additive noise this could be just fine (one would need to work out the coefficients of the diffusion matrix for the given quadrature grid). For the multiplicative noise terms, I would need to dig a bit deeper to see if it would allow noise construction. Put another way - do I have enough access to the individual noises to create spatially coloured noise? So I want to be able to make the following (a true SDE in projected functional calculus):
on a grid
For my specific case there is an easy way to construct this noise in momentum space, if I have access to Gaussian random noises triggered by the coefficients In practice this means that writing the SDE diffusion term as
|
You could... but you shouldn't do it this way. It will screw with the saving and all sorts of other things. Just create a I implemented everything discussed here except the expanded API for a After this huge tagging set goes through, I'll do
Note that for Stratonovich, we only have an Euler-Heun Strong Order 0.5 method. I tried to put a derivative-free Milstein method in the same release, but had some issues: I didn't want that blocking the release, so I ignored it for now. But that should be pretty accessible to anyone who wants to help out since it's at the debugging stage. Let me get some docs up. |
Nevermind, I went ahead and did the |
While the tags are waiting to go through, let me give you a brief overview of what this is all looking like. There are some changes to my original plan, but generally the same idea. Non-Diagonal NoiseThis is specified by a keyword argument f = (t,u,du) -> du.=1.01u
g = function (t,u,du)
du[1,1] = 0.3u[1]
du[1,2] = 0.6u[1]
du[1,3] = 0.9u[1]
du[1,4] = 0.12u[2]
du[2,1] = 1.2u[1]
du[2,2] = 0.2u[2]
du[2,3] = 0.3u[2]
du[2,4] = 1.8u[2]
end
prob = SDEProblem(f,g,ones(2),(0.0,1.0),noise_rate_prototype=zeros(2,4)) and then g = function (t,u,du)
du[1,1] = 0.3u[1]
du[1,2] = 0.6u[1]
du[1,3] = 0.9u[1]
du[1,4] = 0.12u[2]
du[2,1] = 1.2u[1]
du[2,2] = 0.2u[2]
du[2,3] = 0.3u[2]
du[2,4] = 1.8u[2]
end
prob = SDEProblem(f,g,ones(2),(0.0,1.0),noise_rate_prototype=sprand(2,4,1.0)) a trivial sparse matrix. Right now only the Noise ProcessesIn the WHITE_NOISE = NoiseProcess{:White,true,typeof(white_noise_func_wrapper!)}(white_noise_func_wrapper!) and @inline function white_noise_func_wrapper!(rand_vec,integrator)
wiener_randn!(rand_vec)
end which is essentially just There is one helper function that exists. construct_correlated_noisefunc(Γ::AbstractArray) This constructs a CallbacksSo okay, you have non-diagonal noise being solved, and it's generated by a special using StochasticDiffEq, DiffEqBase
f = (t,u,du) -> du.=1.01u
g = (t,u,du) -> du.=1.01u
u0 = ones(4)
tspan = (0.0,1.0)
prob = SDEProblem(f,g,u0,tspan) First we have the condition. The callback is called after every step that this is true. Since we want to project after each step, we will just make it: function condition(t,u,integrator)
true
end Now we have our effect. What we want to do is round every value, so: function affect!(integrator)
integrator.u .= round.(integrator.u)
end Now we make the callback: cb = DiscreteCallback(condition,affect!,save_positions=(false,true)) Notice that we set it to save only after the projection. Now we can look at the cool nonsense that comes out: sol = solve(prob,EM(),callback=cb,dt=1/10)
using Plots; plot(sol) ConclusionThose tools together should be able to solve your problem. If not, I'll need some details and we can try again. The docs should have a lot more details, but feel free to open issues on DiffEqDocs.jl and mention wherever you think the docs are lacking. |
Docs are updated but won't build until the tags go through. |
I notice the docs seem to have updated. The callback stuff works (! super cool !) and I can generate a similar plot. f = (t,u,du) -> du.=1.01u
g = function (t,u,du)
du[1,1] = 0.3u[1]
du[1,2] = 0.6u[1]
du[1,3] = 0.9u[1]
du[1,4] = 0.12u[2]
du[2,1] = 1.2u[1]
du[2,2] = 0.2u[2]
du[2,3] = 0.3u[2]
du[2,4] = 1.8u[2]
end
prob = SDEProblem(f,g,ones(2),(0.0,1.0),noise_rate_prototype=zeros(2,4)) I get julia> prob = SDEProblem(f,g,ones(2),(0.0,1.0),noise_rate_prototype=zeros(2,4))
ERROR: MethodError: no method matching DiffEqBase.SDEProblem{uType,tType,isinplace,isinplaceNoise,NoiseClass,F,F2,F3}(::##5#6, ::##7#8, ::Array{Float64,1}, ::Tuple{Float64,Float64}; noise_rate_prototype=[0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0])
Closest candidates are:
DiffEqBase.SDEProblem{uType,tType,isinplace,isinplaceNoise,NoiseClass,F,F2,F3}(::Any, ::Any, ::Any, ::Any; iip, noise) at /Users/abradley/.julia/v0.5/DiffEqBase/src/problems/sde_problems.jl:20 got unsupported keyword argument "noise_rate_prototype"
DiffEqBase.SDEProblem{uType,tType,isinplace,isinplaceNoise,NoiseClass,F,F2,F3}{T}(::Any) at sysimg.jl:53 got unsupported keyword argument "noise_rate_prototype"
in (::Core.#kw#Type)(::Array{Any,1}, ::Type{DiffEqBase.SDEProblem}, ::Function, ::Function, ::Array{Float64,1}, ::Tuple{Float64,Float64}) at ./<missing>:0 Should I be on master here (currently just on latest)? Another question: I see in the compatibility chart there is no complex number support for ODE. Do you plan to expand complex number support further? It would be great to be able to set up complex variable ODEs and SDEs using ParameterizedFunctions. Can I do this for SDEs yet? |
It hasn't been released yet. Need to wait on a few things. I'll post when it's released.
Yes, but currently there is no way to calculate Jacobians with complex numbers. So non-stiff methods work, stiff methods won't. Issues for this: #110 It would be an easy contribution to those libraries though to set this up, but I haven't had the time to do this.
Yes, just define |
All that was proposed here is now implemented. Please see the updated docs. To keep things compartmentalized, open a new issue for whatever comes up. Thanks for the discussion and the ideas! |
You should be able to give it all a try now. The new docs have built. Let me know if you have any questions! |
great! thanks so much for this, and for great discussions. I am working on getting some minimal examples running within the new API. More soon! |
I am working on setting up some quantum phase space simulations. The issue is that I would like to be able to pass f and g to
SDEProblem
. However, when I pass f and go toSDEProblem
, although complex variables are supported, the noise is called with the integrated type. Whenprob
is passed tosolve
, it triggers the errorMethodError: no method matching randn(::MersenneTwister, ::Type{Complex{Float64}})
Closest candidates are:
randn{T}(::AbstractRNG, ::Type{T}, ::Tuple{Vararg{Int64,N}}) at random.jl:1216
randn{T}(::AbstractRNG, ::Type{T}, ::Integer, ::Integer...) at random.jl:12
It seems that this is triggered by array problems: if I try to solve the single variable Kubo problem (which requires a real noise), everything goes well, problem solved.
So there are two issues
can noises be more directly called and manipulated to construct a new noise in general? I would like to be able to, say, take real Gaussian noises, construct an object in momentum space, then Fourier transform (or matrix transform depending on the basis) to position space, and use the new spatially coloured noise in a position space SDE. This would allow for a broader class of SDEs as, for example, the spatial basis would presumably be free for the user to construct.
is there future scope to have the option of passing a complex variable problem to solve, but having access to real or complex noises, rather than triggering noise from the integrated type? Access to the real noises would be great, allowing construction of complex noises if desired:
from the standard Wiener process dw1, dw2, a unit-normalised complex noise is constructed as
dW = (dw1 + i*dw2)/sqrt(2)
It would be great to be able to define a vector drift and factorized diffusion matrix for complex variables using
@ode_def_bare
, and pass this toSDEProblem
, with a some choice as to whether complex or real noises are assumed in the productB*dW
, or whether a more direct noise construction is needed.The text was updated successfully, but these errors were encountered: