Correct usage of Interpolations.jl outside the domain - julia

I'm porting a Matlab code into julia and so far i'm having amazing results:
A code that in Matlab runs in more than 5 hours, julia does it in a little more than 8 minutes! however i have a problem...
In matlab i have:
for xx=1:xlong
for yy = 1:ylong
U_alturas(xx,yy,:) = interp1(squeeze(NivelAltura_int(xx,yy,:)),squeeze(U(xx,yy,:)), interpolar_a);
V_alturas(xx,yy,:) = interp1(squeeze(NivelAltura_int(xx,yy,:)),squeeze(V(xx,yy,:)), interpolar_a);
end
end
that produces NaNs whenever a point in interpolar_a is outside the range in NivelAltura_int.
In Julia i'm trying to do the same with:
for xx in 1:xlong
for yy in 1:ylong
AltInterp = interpolate((Znw_r,),A_s_c_r,Gridded(Linear()));
NivelAltura_int[xx,yy,1:end] = AltInterp[Znu[1:end]]
Uinterp = interpolate((squeeze(NivelAltura_int[xx,yy,1:end],(1,2)),),squeeze(U[xx,yy,1:end],(1,2)),Gridded(Linear()));
Vinterp = interpolate((squeeze(NivelAltura_int[xx,yy,1:end],(1,2)),),squeeze(V[xx,yy,1:end],(1,2)),Gridded(Linear()));
U_alturas[xx,yy,1:end] = Uinterp[Alturas[1:end]];
V_alturas[xx,yy,1:end] = Vinterp[Alturas[1:end]];
end
end
using the package Interpolations.jl. Whenever the point is outside the domain, this package extrapolates, which is incorrect for my purposes.
I can add a few lines of code that check and substitutes the values outside the domain with NaNs, but i believe it would add some time to the computation and is not very elegant.
In the documentation of the package, it mentions a kind of object like this:
Uextrap = extrapolate(Uinterp,NaN)
To control the behavior outside the domain, but i haven't find how to use it, i've tried adding it under Uinterp, i've tried evaluating it but it, naturally, won't work that way.
Could you help me on this one?
Thanks!

It looks like you may be running into two issues here. First, there's been some recent work on gridded extrapolations (#101) that may not be in the tagged version yet. If you're willing to live on the edge, you can Pkg.checkout("Interpolations") to use the development version (Pkg.free("Interpolations") will put you back on the stable version again).
Secondly, it looks like there's a still a missing method for vector-valued gridded extrapolations (issue #24):
julia> using Interpolations
itp = interpolate((collect(1:10),), collect(.1:.1:1.), Gridded(Linear()))
etp = extrapolate(itp, NaN);
julia> etp[.5:1:10.5]
ERROR: BoundsError: # ...
in throw_boundserror at abstractarray.jl:156
in getindex at abstractarray.jl:488
As you can see, it's trying to use the generic definitions for all abstract arrays, which will of course throw bounds errors. Interpolations just needs to add its own definition.
In the mean time, you can use a comprehension with scalar indexing:
julia> [etp[x] for x=.5:1:10.5]
11-element Array{Any,1}:
NaN
0.15
0.25
0.35
0.45
0.55
0.65
0.75
0.85
0.95
NaN

The following sample (refer) shows how extrapolate works:
Preparation:
using Interpolations
f(x) = sin((x-3)*2pi/9 - 1)
xmax = 10
A = Float64[f(x) for x in 1:xmax] # domain .EQ. 1:10
itpg = interpolate(A, BSpline(Linear()), OnGrid())
The itpg object extrapolates outside points conforming its interpolation type:
itpg[2] # inside => -0.99190379965505
itpg[-2] # outside => 0.2628561875219271
Now we use extrapolat object to control extrapolation behavior:
etpg = extrapolate(itpg, NaN);
etpg[2]==itpg[2] # same result when point is inside => true
isnan(etpg[-2]) # NaN when the point is outside => true
So an extrapolate object does interpolation conforming its parent while extrapolates in a custom manner.

Related

how to correctly pass a value to an included function?

I have 2 julia files, alpha.jl and beta.jl.
in alpha.jl, there are 2 functions:
der that returns a derivative using Zygote,
derPlot that plots the function as well as the derivative:
function der(f, x)
y = f'(x)
return y
end
function derPlt(der,z)
plot(f, aspect_ratio=:equal, label="f(x)")
g(f,x₀) = (x -> f(x₀) + f'(x₀)*(x-x₀))
plot!(g(f,x), label="dy",color="magenta")
xlims!(-z,z)
ylims!(-z,z)
end
everything comes out fine when i call these 2 functions in beta.jl, after including the files:
include("alpha.jl")
f(x)=-x^2+2
x = -1.3
derPlt(der(f, x), 6)
However, if i directly enter in a value for the function, the plotted derivative line doesnt update; i.e, if i enter 2.0 instead of passing in some variable named x,
derPlt(der(f, 2.0), 6)
no change is reflected on the plot. New to Julia, trying to understand and fix it.
I think it's because in your derPlt function, you call
plot!(g(f,x),...)
on x instead of the z argument. The problem is then that you define a x = -1.3, the value of which is used inside of derPlt, regardless of what z argument you feed it.
Maybe replace that line with
plot!(g(f,z),...)
and you should be fine.
Seeing as this is a follow up to a question I answered previously I thought I'd have to respond: Benoit is broadly speaking correct, you are running into a scoping issue here, but a few more changes are in order.
Note that your function signature is derPlot(der, z) but then:
You never actually use the der argument in your function body; and
You construct your tangent line as g(f,x₀) = (x -> f(x₀) + f'(x₀)*(x-x₀)) - note that there's no z in there, only an x
Now where does that x come from? In the absence of any x argument being passed to your function, Julia will look for it in the global scope (outside your function). Here, you define x = -1.3, and when you then call derPlt, that's the x that will be used to construct your tangent, irrespective of the z argument you're passing.
Here's a cleaned up and working version:
using Plots, Zygote
function derPlt(f,z)
plot(f, label="f(x)", aspect_ratio = :equal,
xlims = (-5,5), ylims = (-5,5))
g(f,x₀) = (z -> f(x₀) + f'(x₀)*(z-x₀))
plot!(i -> g(f, z)(i), label="dy",color="magenta")
end
f(x)=-x^2+2
derPlt(f, -1.5)
I would encourage you to read the relevant manual section on Scope of Variables to ensure you get an understanding of what's happening in your code - good luck!

1D integration with multivariable function input

To demonstrate, let's start with a simple multi-variable function f(x,y) = xy^2.
I am trying to find a command that would allow me to numerically integrate f(2, y) = 2y^2 from y = 0 to y = 2. (i.e. the original function is multi-variable, but only one variable remains when actually doing the integration)
I needed to define the function that way as I need to obtain the results using different values of x. (probably going to involve for-loop, but that is another story)
I have tried to walk through Cubature's user guide but apparently did not find anything useful. Maybe I have missed it
Can anyone help?
In such case it is simplest to use an anonymous function wrapper:
using QuadGK
f(x,y) = x*y^2
intf(x) = quadgk(y -> f(x, y), 0, 2)
if the anonymous function would be longer you could write:
intf(x) = quadgk(0, 2) do y
f(x, y)
end
This is an exact equivalent of the latter but do syntax allows you to write longer bodies of an anonymous function.
Now you can write e.g.:
julia> intf(1)
(2.6666666666666665, 4.440892098500626e-16)
julia> intf(2)
(5.333333333333333, 8.881784197001252e-16)
julia> intf(3)
(8.0, 0.0)

Plot of function, DomainError. Exponentiation yielding a complex result requires a complex argument

Background
I read here that newton method fails on function x^(1/3) when it's inital step is 1. I am tring to test it in julia jupyter notebook.
I want to print a plot of function x^(1/3)
then I want to run code
f = x->x^(1/3)
D(f) = x->ForwardDiff.derivative(f, float(x))
x = find_zero((f, D(f)),1, Roots.Newton(),verbose=true)
Problem:
How to print chart of function x^(1/3) in range eg.(-1,1)
I tried
f = x->x^(1/3)
plot(f,-1,1)
I got
I changed code to
f = x->(x+0im)^(1/3)
plot(f,-1,1)
I got
I want my plot to look like a plot of x^(1/3) in google
However I can not print more than a half of it
That's because x^(1/3) does not always return a real (as in numbers) result or the real cube root of x. For negative numbers, the exponentiation function with some powers like (1/3 or 1.254 and I suppose all non-integers) will return a Complex. For type-stability requirements in Julia, this operation applied to a negative Real gives a DomainError. This behavior is also noted in Frequently Asked Questions section of Julia manual.
julia> (-1)^(1/3)
ERROR: DomainError with -1.0:
Exponentiation yielding a complex result requires a complex argument.
Replace x^y with (x+0im)^y, Complex(x)^y, or similar.
julia> Complex(-1)^(1/3)
0.5 + 0.8660254037844386im
Note that The behavior of returning a complex number for exponentiation of negative values is not really different than, say, MATLAB's behavior
>>> (-1)^(1/3)
ans =
0.5000 + 0.8660i
What you want, however, is to plot the real cube root.
You can go with
plot(x -> x < 0 ? -(-x)^(1//3) : x^(1//3), -1, 1)
to enforce real cube root or use the built-in cbrt function for that instead.
plot(cbrt, -1, 1)
It also has an alias ∛.
plot(∛, -1, 1)
F(x) is an odd function, you just use [0 1] as input variable.
The plot on [-1 0] is deducted as follow
The code is below
import numpy as np
import matplotlib.pyplot as plt
# Function f
f = lambda x: x**(1/3)
fig, ax = plt.subplots()
x1 = np.linspace(0, 1, num = 100)
x2 = np.linspace(-1, 0, num = 100)
ax.plot(x1, f(x1))
ax.plot(x2, -f(x1[::-1]))
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
plt.show()
Plot
That Google plot makes no sense to me. For x > 0 it's ok, but for negative values of x the correct result is complex, and the Google plot appears to be showing the negative of the absolute value, which is strange.
Below you can see the output from Matlab, which is less fussy about types than Julia. As you can see it does not agree with your plot.
From the plot you can see that positive x values give a real-valued answer, while negative x give a complex-valued answer. The reason Julia errors for negative inputs, is that they are very concerned with type stability. Having the output type of a function depend on the input value would cause a type instability, which harms performance. This is less of a concern for Matlab or Python, etc.
If you want a plot similar the above in Julia, you can define your function like this:
f = x -> sign(x) * abs(complex(x)^(1/3))
Edit: Actually, a better and faster version is
f = x -> sign(x) * abs(x)^(1/3)
Yeah, it looks awkward, but that's because you want a really strange plot, which imho makes no sense for the function x^(1/3).

How do I write a piecewise Differential Equation in Julia?

I am new to Julia, I would like to solve this system:
where k1 and k2 are constant parameters. However, I=0 when y,0 or Ky otherwise, where k is a constant value.
I followed the tutorial about ODE. The question is, how to solve this piecewise differential equation in DifferentialEquations.jl?
Answered on the OP's cross post on Julia Discourse; copied here for completeness.
Here is a (mildly) interesting example $x''+x'+x=\pm p_1$ where the sign of $p_1$ changes when a switching manifold is encountered at $x=p_2$. To make things more interesting, consider hysteresis in the switching manifold such that $p_2\mapsto -p_2$ whenever the switching manifold is crossed.
The code is relatively straightforward; the StaticArrays/SVector/MVector can be ignored, they are only for speed.
using OrdinaryDiffEq
using StaticArrays
f(x, p, t) = SVector(x[2], -x[2]-x[1]+p[1]) # x'' + x' + x = ±p₁
h(u, t, integrator) = u[1]-integrator.p[2] # switching surface x = ±p₂;
g(integrator) = (integrator.p .= -integrator.p) # impact map (p₁, p₂) = -(p₁, p₂)
prob = ODEProblem(f, # RHS
SVector(0.0, 1.0), # initial value
(0.0, 100.0), # time interval
MVector(1.0, 1.0)) # parameters
cb = ContinuousCallback(h, g)
sol = solve(prob, Vern6(), callback=cb, dtmax=0.1)
Then plot sol[2,:] against sol[1,:] to see the phase plane - a nice non-smooth limit cycle in this case.
Note that if you try to use interpolation of the resulting solution (i.e., sol(t)) you need to be very careful around the points that have a discontinuous derivative as the interpolant goes a little awry. That's why I've used dtmax=0.1 to get a smoother solution output in this case. (I'm probably not using the most appropriate integrator either but it's the one that I was using in a previous piece of code that I copied-and-pasted.)

Simulate a bouncing ball?

Is it possible to create a simple model of a bouncing ball, using Julia's equation solvers?
I started with this:
using ODE
function bb(t, f)
(y, v) = f
dy_dt = v
dv_dt = -9.81
[dy_dt, dv_dt]
end
const y0 = 50.0 # height
const v0 = 0.0 # velocity
const startpos = [y0; v0]
ts = 0.0:0.25:10 # time span
t, res = ode45(bb, startpos, ts)
which produces useful-looking numbers:
julia> t
44-element Array{Float64,1}:
0.0
0.0551392
0.25
0.5
0.75
1.0
⋮
8.75
9.0
9.25
9.5
9.75
10.0
julia> res
44-element Array{Array{Float64,1},1}:
[50.0,0.0]
[49.9851,-0.540915]
[49.6934,-2.4525]
[48.7738,-4.905]
[47.2409,-7.3575]
⋮
[-392.676,-93.195]
[-416.282,-95.6475]
[-440.5,-98.1]
But somehow it needs to intervene when the height is 0, and reverse the velocity. Or am I on the wrong track?
DifferentialEquations.jl offers sophisticated callbacks and event handling. Since the DifferentialEquations.jl algorithms are about 10x faster while offering a higher order interpolation, these algorithms are clearly the better choose here anyways.
The first link is the documentation which shows how to do the event handling. The easy interface uses the macros. I start by defining the function.
f = #ode_def BallBounce begin
dy = v
dv = -g
end g=9.81
Here I am showing ParameterizedFunctions.jl to make the syntax nicer, but you can define the function directly as an in-place update f(t,u,du) (like Sundials.jl). Next you define the function which determines when an event takes place. It can be any function which is positive and hits zero at the event time. Here, we are checking for when the ball hits the ground, or for when y=0, so:
function event_f(t,u) # Event when event_f(t,u,k) == 0
u[1]
end
Next you say what to do when the event occurs. Here we want to reverse the sign of the velocity:
function apply_event!(u,cache)
u[2] = -u[2]
end
You put these functions together to build the callback using the macros:
callback = #ode_callback begin
#ode_event event_f apply_event!
end
Now you solve as usual. You define the ODEProblem using f and the initial condition, and you call solve on a timespan. The only thing extra is you pass the callback along with the solver:
u0 = [50.0,0.0]
prob = ODEProblem(f,u0)
tspan = [0;15]
sol = solve(prob,tspan,callback=callback)
Then we can use the plot recipe to automatically plot the solution:
plot(sol)
The result is this:
A few things to notice here:
DifferentialEquations.jl will automatically use an interpolation to more safely check for the event. For example, if the event happened within a timestep but not at the ends, DifferentialEquations.jl will still find it. More or less interpolations points can be included as options to the #ode_event macro.
DifferentialEquations.jl used a rootfinding method to hone in on the moment of the event. Even though the adaptive solver steps past the event, by using rootfinding on the interpolation it finds the exact time of the event, and thus gets the discontinuity right. You can see that in the graph since the ball never goes negative.
There is a whole lot more this can do. Check out the docs. You can do pretty much anything with this. For example, have your ODE changing size over the run to model a population of cells with birth and deaths. This is something other solver packages can't do.
Even with all of these features, speed is not compromised.
Let me know if you need any extra functionality added to the "ease of use" interface macros.
Somewhat hacky:
function bb(t, f)
(y, v) = f
dy_dt = v
dv_dt = -9.81*sign(y)
[dy_dt, dv_dt]
end
where you just follow a convention where y and -y refer to the same heights. You can then plot the trajectory of the bouncing ball by just plotting abs(y).

Resources