MATLAB style griddedInterpolant usage from Interpolations.jl - matlab

I'm trying to convert a MATLAB program of mine to Julia. A key feature of this program uses the griddedInterpolant function in MATLAB. I have found the Julia replacement (Interpolations.jl) and I have done a simple test in the 2-dimensional case to be sure that I understand how it works. This particular program uses 4-D arrays though, and I can't seem to figure out how to work the Interpolations.jl method beyond 2 dimensions.
A very simplified example of the MATLAB behavior. Note that this is a trivial example because V is flat, but you get the point. What my program would do is be changing the values of V, kp1gv, and kp2gv within a loop.
nK_1 = 50;
nK_2 = 50;
nKP_1 = 10;
nKP_2 = 10;
K1 = linspace(0,100,nK_1);
K2 = linspace(0,100,nK_2);
KP1 = linspace(0,100,nKP_1);
KP2 = linspace(0,100,nKP_1);
nX = 3;
nY = 3;
X = [0.9,1,1.1];
Y = [0.95,1,1.05];
[k1gv,k2gv,xgv,ygv] = ndgrid(K1,K2,X,Y); % creates grid vectors
V = ones(nK_1,nK_2,nX,nY); % value func to be interpolated
Fit = griddedInterpolant(k1gv,k2gv,xgv,ygv,V,'linear'); % fitted val fun
[kp1gv,kp2gv,xpgv,ypgv,kk1,kk2] = ndgrid(KP1,KP2,X,Y,K1,K2);
Fitted = Fit(kp1gv,kp2gv,xpgv,ypgv);
What I am looking for is to duplicate this in Julia, either exactly as it is in MATLAB or by rewriting it to be faster. Here is my attempt now:
nK_1 = 50
nK_2 = 50
nKP_1 = 10
nKP_2 = 10
K1 = linspace(0,100,nK_1)
K2 = linspace(0,100,nK_2)
KP1 = linspace(0,100,nKP_1)
KP2 = linspace(0,100,nKP_1)
nX = 3
nY = 3
X = [0.9,1,1.1]
Y = [0.95,1,1.05]
V = ones(nK_1,nK_2,nX,nY)
# using the more compact notation here, would be similar to
# griddedInterpolant({K1,K2,X,Y},V,'linear') -- no issues with this fit
Fit = interpolate((K1,K2,X,Y),V,Gridded(Linear()))
# this is how I'm getting ndgrid functionality
KP1gv = Float64[i for i in KP1, j in KP2, x in X, y in Y, a in K1, b in K2]
KP2gv = Float64[j for i in KP1, j in KP2, x in X, y in Y, a in K1, b in K2]
Xgv = Float64[x for i in KP1, j in KP2, x in X, y in Y, a in K1, b in K2]
Ygv = Float64[y for i in KP1, j in KP2, x in X, y in Y, a in K1, b in K2]
# what I want to work
Fitted = Fit[KP1gv,KP2gv,Xgv,Ygv]
I've read the documentation on Interpolations.jl, and I know this should be doable, I just can't seem to get it to work.

Rather than construct all those ndgrid coordinate arrays, just do this:
fitted = [Fit[i,j,k,l] for i in KP1, j in KP2, k in X, l in Y]
But a couple of tips:
Anything performance-critical (like the line above) should be placed in a function. This is absolutely crucial. See the performance tips page of the manual.
This seems not to be well-documented, but Gridded is designed for cases where you have a rectangular grid, but where the spacing may not be regular. In cases of regular spacing, you can do better with
itp = interpolate(V, Linear(), OnGrid())
sitp = scale(itp, K1, K2, x, y)
where x and y are also linspace objects.

Related

I cant get the result of y

I have this questions but my result is different with the exact result what is the wrong?
this is what i tried.
x = 2;
z = 3;
y = x^2*x*z+12*x*z+((exp(x)/log(x*z)-log(x*z)*nthroot(x*z,2)))*nthroot(x*z,3);
result of y is 95.5185 not equal to -21.455
MATLAB Logarithm Conventions
Breaking up long equations into separate terms is useful for debugging. Here the case was the logarithmic functions that were used.
log → log10() (base-10 logarithm)
ln → log() (natural logarithm)
x = 2;
z = 3;
Term_1 = (x^2)*x*z;
Term_2 = 12*x*z;
Term_3 = (exp(x))/(log(x*z) - log10(x*z)*sqrt(x*z));
Term_4 = nthroot(x*z,3);
y = Term_1 + Term_2 + Term_3*Term_4;
y
Ran using MATLAB R2019b

Convolution of two dependent distributions in MATLAB

Assume that I have two discrete random variables X and Y.
X = {1,3,3,5,7,7,7,9,9,9,9,9}
and
Y = {5,5,9,9,10,12,13}
Where their empirical CDFs are given as:
F_x(1) = 0.0833, F_x(3) = 0.25, F_x(5) = 0.33, F_x(7) = 0.5833 and F_x(9) = 1
and
F_y(5) = 0.2857, F_y(9) = 0.5714, F_y(10) = 0.7143, F_y(12) = 0.8571 and F_y(13) = 1
Assuming their joint distribution is
H(x,y) = F_x(x) * F_y(y)
which is actually the "assumption" of X and Y are independent.
How can i calculate the Z = X + Y and F(z) in MATLAB ?
Note: I gave the H(x,y) as a simple product function for the simplicity, but it can be anything in reality which actually models the dependency between X and Y.
Given continuous probability density functions FX and FY, with a joint probability density function FX,Y, we can compute FX+Y as the integral of FX,Y over the line z=x+y. If the probability density functions are discrete, the integral above should be written as the derivative of the integral over the part of the plane given by z<=x+y.
This is fairly simple to do in MATLAB. Let's start with OP's data:
F_x = [0.0833,0.0833,0.25,0.25,0.33,0.33,0.5833,0.5833,1]; % CDF
F_x = diff([0,F_x]); % PDF
F_y = [0,0,0,0,0.2857,0.2857,0.2857,0.2857,0.5714,0.7143,0.7143,0.8571,1]; % CDF
F_y = diff([0,F_y]); % PDF
H = F_x.' .* F_y; % example joint PDF
Now we sum F_cum(z) = sum(H(x,y)) for all values z<=x+y, and then take the derivative F = diff([0,F_cum]):
[m,n] = size(H);
F_cum = zeros(1,m+n-1);
for z = 1:numel(F_cum)
s = 0;
for x = 1:numel(F_x)
y = z-x+1;
y = max(min(y,n),1); % avoid out of bounds indexing
s = s + sum(H(x,1:y));
end
F_cum(z) = s;
end
F = diff([0,F_cum]);
Note that we defined y=z-x+1, meaning z=y+x-1. Thus F(1) corresponds to z=2. This is the lowest possible value that can come out of the sum of the two distributions, which we defined to start at 1.
The above can be simplified by padding H with zeros and shifting each row by one additional element. This lines up the line z=x+y on a column of the matrix, allowing us to use a trivial sum projection:
H = [H,zeros(m)];
for ii=2:m
H(ii,:) = circshift(H(ii,:),ii-1);
end
F_cum = cumsum(sum(H,1));
F_cum = F_cum(1:end-1); % last element we don't need
F2 = diff([0,F_cum]);
But because diff([0,cumsum(F)]) == F (up to numerical precision), we can skip those two operations:
F3 = sum(H,1);
F3 = F3(1:end-1); % last element we don't need
(all(abs(F-F2)<1e-15) and all(abs(F-F3)<1e-16))

MATLAB: efficient generating of block matrices using a block vector

Suppose
x = [x1;x2; ...; xn]
where each xi is a column vector with length l(i). We can set L = sum(l), the total length of x. I would like to generate 2 matrices based on x:
Let's call them A and B. For example, when x only as 2 blocks x1 and x2 then:
A = [x1*x1' zeros(l(1),l(2)); zeros(l(2),l(1)), x2*x2'];
B = [x1 zeros(l(1),1);
zeros(l(2),1), x2];
In the notation of the problem, A is always L by L and B is L by n. I can generate A and B given x using loops but it is tedious. Is there a clever (loop-free) way to generate A and B. I am using MATLAB 2018b but you can assume earlier version of MATLAB if necessary.
I think it is both short and fast:
B = x .* (repelem((1:numel(l)).',l)==(1:numel(l)));
A = B * B.';
If you have large data It is better to use sparse matrix:
B = sparse(1:numel(x), repelem(1:numel(l), l), x);
A = B * B.';
The following should work. In this case I do an inefficient conversion to cell arrays so there may be a more efficient implementation possible.
cuml = [0; cumsum(l(:))];
get_x = #(idx) x((1:l(idx))+cuml(idx));
x_cell = arrayfun(get_x, 1:numel(l), 'UniformOutput', false);
B = blkdiag(x_cell{:});
A = B*B';
Edit
After running some benchmarks I found a direct loop based implementation to be about twice as fast as the cell based approach above.
A = zeros(sum(l));
B = zeros(sum(l), numel(l));
prev = 0;
for idx = 1:numel(l)
xidx = (1:l(idx))+prev;
A(xidx, xidx) = x(xidx,1) * x(xidx,1)';
B(xidx, idx) = x(idx,1);
prev = prev + l(idx);
end
Here's an alternative approach:
s = repelem(1:numel(l), l).';
t = accumarray(s, x, [], #(x){x*x'});
A = blkdiag(t{:});
t = accumarray(s, x, [], #(x){x});
B = blkdiag(t{:});

Interpolation using polyfit (Matlab)

My script is supposed to run Runge-Kutta and then interpolate around the tops using polyfit to calculate the max values of the tops. I seem to get the x-values of the max points correct but the y-values are off for some reason. Have sat with it for 3 days now. The problem should be In the last for-loop when I calculate py?
Function:
function funk = FU(t,u)
L0 = 1;
C = 1*10^-6;
funk = [u(2); 2.*u(1).*u(2).^2./(1+u(1).^2) - u(1).*(1+u(1).^2)./(L0.*C)];
Program:
%Runge kutta
clear all
close all
clc
clf
%Given values
U0 = [240 1200 2400];
L0 = 1;
C = 1*10^-6;
T = 0.003;
h = 0.000001;
W = [];
% Runge-Kutta 4
for i = 1:3
u0 = [0;U0(i)];
u = u0;
U = u;
tt = 0:h:T;
for t=tt(1:end-1)
k1 = FU(t,u);
k2 = FU(t+0.5*h,u+0.5*h*k1);
k3 = FU((t+0.5*h),(u+0.5*h*k2));
k4 = FU((t+h),(u+k3*h));
u = u + (1/6)*(k1+2*k2+2*k3+k4)*h;
U = [U u];
end
W = [W;U];
end
I1 = W(1,:); I2 = W(3,:); I3 = W(5,:);
dI1 = W(2,:); dI2 = W(4,:); dI3 = W(6,:);
I = [I1; I2; I3];
dI = [dI1; dI2; dI3];
%Plot of the currents
figure (1)
plot(tt,I1,'r',tt,I2,'b',tt,I3,'g')
hold on
legend('U0 = 240','U0 = 1200','U0 = 2400')
BB = [];
d = 2;
px = [];
py = [];
format short
for l = 1:3
[H,Index(l)]=max(I(l,:));
Area=[(Index(l)-2:Index(l)+2)*h];
p = polyfit(Area,I(Index(l)-2:Index(l)+2),4);
rotp(1,:) = roots([4*p(1),3*p(2),2*p(3),p(4)]);
B = rotp(1,2);
BB = [BB B];
Imax(l,:)=p(1).*B.^4+p(2).*B.^3+p(3).*B.^2+p(4).*B+p(5);
Tsv(i)=4*rotp(1,l);
%px1 = linspace(h*(Index(l)-d-1),h*(Index(l)+d-2));
px1 = BB;
py1 = polyval(p,px1(1,l));
px = [px px1];
py = [py py1];
end
% Plots the max points
figure(1)
plot(px1(1),py(1),'b*-',px1(2),py(2),'b*-',px1(3),py(3),'b*-')
hold on
disp(Imax)
Your polyfit line should read:
p = polyfit(Area,I(l, Index(l)-2:Index(l)+2),4);
More interestingly, take note of the warnings you get about poor conditioning of that polynomial (I presume you're seeing these). Why? Partly because of numerical precision (your numbers are very small, scaled around 10^-6) and partly because you're asking for a 4th-order fit to five points (which is singular). To do this "better", use more input points (more than 5), or a lower-order polynomial fit (quadratic is usually plenty), and (probably) rescale before you use the polyfit tool.
Having said that, in practice this problem is often solved using three points and a quadratic fit, because it's computationally cheap and gives very nearly the same answers as more complex approaches, but you didn't get that from me (with noiseless data like this, it doesn't much matter anyway).

Vectorizing in matlab

I am searching for a way to get rid of the following loop (over theta):
for i=1:1:length(theta)
V2_ = kV2*cos(theta(i));
X = X0+V2_;
Y = Y0-V2_*(k1-k2);
Z = sqrt(X.^2-Z0-4*V2_.*(k.^2*D1+k1));
pktheta(:,i)=exp(-t/2*V2_).*(cosh(t/2*Z)+...
Y./((k1+k2)*Z).*sinh(t/2*Z));
end
where X0,Y0,Z0 and kV2 are dependent on the vector k (same size). t, D1, k1 and k2 are numbers. Since I have to go through this loop several times, how can I speed it up?
Thanks
Try this -
N = numel(theta);
V2_ = kV2*cos(theta(1:N));
X0 = repmat(X0,[1 N]);
Y0 = repmat(Y0,[1 N]);
Z0 = repmat(Z0,[1 N]);
X = X0 + V2_;
Y = Y0-V2_*(k1-k2);
Z = sqrt(X.^2-Z0-4.*V2_ .* repmat(((1:N).^2)*D1 + k1.*ones(1,N),[size(X0,1) 1]));
pktheta = exp(-t/2*V2_).*(cosh(t/2*Z) + Y./((k1+k2)*Z).*sinh(t/2*Z));
Definitely BSXFUN must be faster, if someone could post with it.