Ok, so using Memoize.jl it’s not too bad until you get to the clincher… If you’re ok with a finite difference approximation, then it’s doable… let’s see what happens.
Using the hard stuff here, already;)
But, OTOH derivatives are somehow recursive too…
That’s what the finite differences approach leverages to get O(N) ![]()
Yes, it seems to be the way. I’ll will implement it and share the results here.
No need, start with this:
using Memoize
@memoize function wanddw(n,t,p)
if n < 0
error("wanddw: n must be an integer > 0")
end
if n == 0
if t < -1
return (complex(0.0),complex(0.0))
elseif t <= 1
return ((exp(p+p*t) - 1.0)/p, (t+1)*exp(p+p*t)/p - (exp(p+p*t)-1)/p^2)
else
return (2*exp(p*t)*sinh(p)/p, 2*sinh(p)*t*exp(p*t)/p - 2*sinh(p)*exp(p*t)/p^2 +2*cosh(p)*exp(p*t)/p)
end
elseif n == 1
t < -1 && return (complex(0.),complex(0.))
t <= 1 && return (-(1.0+exp(p+p*t)*(p-1)+p*t)/p^2, (-(p-1.0)*(t+1)*exp(p*t+p)-exp(p*t+p)-t)/p^2 - 2*((-(p-1)*exp(p*t+p)-p*t-1)/p^3))
return (-2.0*exp(p*t)*(p*cosh(p)-sinh(p))/p^2, -(2.0*(p*cosh(p)-sinh(p))*t*exp(p*t)/p^2)-2.0*sinh(p)*exp(p*t)/p - 4.0*(p*cosh(p)-sinh(p))*exp(p*t)/p^3)
elseif n >= 2
(wprev,dwprev) = wanddw(n-1,t,p)
(wprev2,dwprev2) = wanddw(n-2,t,p)
(wprev2d, dwprev2d) = wanddw(n-2,t,p+complex(0.0125,.0125))
return (2*t*wprev - 2*dwprev-wprev2, 2*t*dwprev - (dwprev2d-dwprev2)/complex(0.0125,0.0125))
end
end
The finite difference is hard coded, so you’ll want to fix that, but it returns IMMEDIATELY.
I used Maxima to get the symbolic derivatives for the base cases (because I’m lazy).
Thanks! I like the idea of returning both the value and the derivative in the same function for extra memoization efficiency!
I wonder if I can get this to work in terms of accuracy.
Thank you very much again.
It was fun! I think you just add the finite difference step size as a parameter and then use that in the code. Let us know how it works out, show some graphs or something.
I did that, and then tried it like this:
julia> wanddw(4,.1,complex(-.1),.0005)
(0.2959821757039954 - 0.0015914894716217987im, 0.4360000703131307 + 0.002182010051928633im)
julia> wanddw(4,.1,complex(-.1),.00005)
(0.2959821757039954 - 0.0015914894716217987im, 0.43592222276367454 + 0.0021033784685235563im)
julia> wanddw(4,.1,complex(-.1),.000005)
(0.2959821757039954 - 0.0015914894716217987im, 0.43591441315285184 + 0.002095691848207612im)
so at least as the step size gets smaller, it isn’t doing anything much
here’s the code with the dp:
using Memoize
@memoize function wanddw(n,t,p,dp)
if n < 0
error("wanddw: n must be an integer > 0")
end
if n == 0
if t < -1
return (complex(0.0),complex(0.0))
elseif t <= 1
return ((exp(p+p*t) - 1.0)/p, (t+1)*exp(p+p*t)/p - (exp(p+p*t)-1)/p^2)
else
return (2*exp(p*t)*sinh(p)/p, 2*sinh(p)*t*exp(p*t)/p - 2*sinh(p)*exp(p*t)/p^2 +2*cosh(p)*exp(p*t)/p)
end
elseif n == 1
t < -1 && return (complex(0.),complex(0.))
t <= 1 && return (-(1.0+exp(p+p*t)*(p-1)+p*t)/p^2, (-(p-1.0)*(t+1)*exp(p*t+p)-exp(p*t+p)-t)/p^2 - 2*((-(p-1)*exp(p*t+p)-p*t-1)/p^3))
return (-2.0*exp(p*t)*(p*cosh(p)-sinh(p))/p^2, -(2.0*(p*cosh(p)-sinh(p))*t*exp(p*t)/p^2)-2.0*sinh(p)*exp(p*t)/p - 4.0*(p*cosh(p)-sinh(p))*exp(p*t)/p^3)
elseif n >= 2
(wprev,dwprev) = wanddw(n-1,t,p)
(wprev2,dwprev2) = wanddw(n-2,t,p)
(wprev2d, dwprev2d) = wanddw(n-2,t,p+complex(dp,dp))
return (2*t*wprev - 2*dwprev-wprev2, 2*t*dwprev - (dwprev2d-dwprev2)/complex(dp,dp))
end
end
Even order 100 returns immediately, thanks to Memoization.jl !
julia> wanddw(100,.1,complex(-.1),.000005)
(6.329687336395586e86 - 2.2156952437460406e86im, -3.069266938528541e90 + 4.369343530297239e90im)
For what it’s worth, the above code is not numerically stable (and contains an error). I found another way to calculate the convolution analytically and use the recursive function to define the partial derivative with respect to p. Thanks again for the help!