Different output for equivalent code between Matlab and Julia - matlab

I have a working Matlab algorithm for the evolution of a pulse via solving the 1D wave equation. I know the code works and I can see the pulse traveling to the sides and bouncing off the walls, when plotting; but when I translate the corresponding steps as Julia code, however, the results are completely different for the same set of inputs. I insert a simple pulse that should propagate across a string in both directions but that isn't exactly the result that I'm getting in Julia... and I don't know why.
Could anybody help point out what I'm doing wrong?
Thank you,
This is the corresponding Julia code:
using Plots,OhMyREPL
#-solving a PDE.
#NOTE: DOMAIN
# space
Lx = 20
dx = 0.1
nx = Int(trunc(Lx/dx))
x = range(0,Lx,nx)#0:dx:Lx-1
#NOTE: TIME
T = 10
#NOTE: Field variable.
#-variables.
wn = zeros(nx)#-current value.
wnm1 = wn#zeros(nx)#-w at time n-1.
wnp1 = wn#zeros(nx)#-w at time n+1.
# wnp1[1:end] = wn[1:end]#-w at time n+1.
#NOTE: PARAMETERS.
CFL = 1#-CFL = c.dt/dx
c = 1#-the propagation speed.
dt = CFL * dx/c
# #NOTE: Initial Conditions.
wn[49] = 0.1; wn[50] = 0.2; wn[51] = 0.1
wnp1[1:end] = wn[1:end]#--assumption that they are being equaled.
wnp1 = wn#--assumption that they are being equaled.
run = true
if run == true
#NOTE: Initial Conditions.
wn[49] = 0.1; wn[50] = 0.2; wn[51] = 0.1
wnp1 = wn#--wnp1[1:end] = wn[1:end]#--assumption that they are being equaled.
# NOTE: Time stepping loop.
t = 0
while t<T
#-apply reflecting boundary conditions.
wn[1]=0.0;wn[end]=0.0
#NOTE: solution::
global t = t+dt
global wnm1 = wn; global wn = wnp1#-save CURRENT and PREVIOUS arrays.
for i=2:nx-1
global wnp1[i] = 2*wn[i] - wnm1[i] + CFL^2 * ( wn[i+1] -2*wn[i] + wn[i-1] )
end
end
plot(x,wnp1, yrange=[-0.2,0.2])
end
Only one peak
This is the corresponding Matlab code:
%Domain
%Space.
Lx = 20;
dx = 0.1;
nx = fix(Lx/dx);
x = linspace(0,Lx,nx);
%Time
T = 10;
%Field Variables
wn = zeros(nx,1);
wnm1 = wn;
wnp1 = wn;
%Parameters...
CFL = 1;
c = 1;
dt = CFL*dx/c;
%Initial conditions
wn(49:51) = [0.1 0.2 0.1];
wnp1(:) = wn(:);
run = true;
if run == true
%Initial conditions
wn(49:51) = [0.1 0.2 0.1];
wnp1(:) = wn(:);
%Time stepping loop
t = 0;
while t<T
%Reflecting boundary conditions:
wn([1 end]) = 0.0;
%Solution:
t=t+dt;
wnm1=wn;wn = wnp1;%-save current and previous arrays.
for i=2:nx-1
wnp1(i) = 2*wn(i) - wnm1(i) + CFL^2 * ( wn(i+1) - 2*wn(i) + wn(i-1) );
endfor
end
plot(x,wnp1)
end
Two peaks, as it should be.
If anybody could point me in the right direction, I'd greatly appreciate it.

Julia arrays are designed to be fast. So, when an array is assigned to another array, the default behaviour is make the two arrays the same - same variable address, occupying the same area of memory. Editing one array does the exact same edit on the other array:
x = [1,2,3]
y = x
x[1] = 7
# x is [7,2,3]
# y is [7,2,3] also
To copy data from one array to another an explicit copy is required:
x = [1,2,3]
y = copy(x)
x[1] = 7
# x is [7,2,3]
# y is [1,2,3] still
The same thing happens with Python lists:
x = [1,2,3]
y = x
x[0] = 7 # Julia indexes arrays 1+, Python indexes lists 0+
# x is [7,2,3]
# y is [7,2,3] also
It is done this way to minimise the effort of unnecessary copying, which can be expensive.
The upshot is that if the code is running but gives the wrong result, a possible cause is assigning an array when a copy is intended.
The question author indicated that this was indeed the cause of the code's behaviour.

Related

Incrementing array within parfor?

I am calculating sort of a histogram based on the distance between a pair of points in 3d space:
numBins = 20;
binWidth = 2.5;
pop = zeros(1,numBins);
parfor j=1:particles
r1 = coords(j,:);
for k=j+1:particles
r2 = coords(k,:);
d = norm(r1-r2);
ind = ceil(d/binWidth);
pop(ind) = pop(ind) + 1;
end
end
This, expectedly, results in
Error: The variable pop in a parfor cannot be classified.
I understand the problem, but I am confused as to how can I solve it.
In principle, what could be done is to have n copies of pop = zeroes(1,numBins) be sent to each of n workers, and joined by adding each copy together at the end of computation. How can I do this here? Or is there another, more standard way of solving the problem?
There is two things that don't work in your code:
1) for k = j+1:particles
In a parfor a nested loop should have fixed bound.
2) pop(ind)
Matlab is afraid that the for-loop order matters and display an error message. Even if, in your specific case, the order doesn't matters (But matlab is not smart enough to know that).
The solution, Linearization:
%Dummy data
numBins = 20;
binWidth = 2.5;
particles = 10;
coords = rand(10,2)*40;
%Initialization
pop = zeros(1,numBins);
parfor j=1:particles
r1 = coords(j,:)
r2 = coords((j+1):end,:)
d = sqrt(sum([r1-r2].^2,2)) % compute each norm at the same time !
pop = pop + histcounts(ceil(d/binWidth),0:numBins)
end
You can create a function that computes the inner loop and use a handle to it in the parfor (I didn't tested it but I think it should work according to the documentation):
function pop = hist_comp(pop,j,particles,coords,binWidth)
r1 = coords(j,:);
for k=j+1:particles
r2 = coords(k,:);
d = norm(r1-r2);
ind = ceil(d/binWidth);
pop(ind) = pop(ind) + 1;
end
end
numBins = 20;
binWidth = 2.5;
particles = 10;
coords = rand(10,2)*5;
pop = zeros(1,numBins);
f = #(pop,j) hist_comp(pop,j,particles,coords,binWidth);
parfor j=1:particles
pop = f(pop,j);
end

Code unable to reconcile vector assignment

I am interested in modifying the code below to find the parameters I_1, I_2, I_3 and I_4, to be used in another code. Every time I run the code, it throws up
In an assignment A(:) = B, the number of elements in A and B must be the same
on this line " mult(mult == 0) = B;".
I have spent eternity figuring out what the problem could be. Here is the code:
%%% Some Parameters %%
delta = 0.6; % Blanked subframe ratio
B = [0 0.2 0.4 0.6 0.8 1]; %Power splitting factor
k = 2.3; %Macro BS density
f = k*5; %Small cell density
j = 300; %users density
P_m = 46; %Macro BS transmission power
P_s = 23; %SC transmit power
Zm = -15;
Zs = -15;
iter = 30; %Iteration run
h = 500; %Simulation area
hu = 0.8*h; %users simulation area
Vm = round(k*h); %Macro BS average no in h
Vs = round(f*h); %SC average no in h
Vu = round(j*hu); %%users average no in hu
Pm = 10^(P_m/10)/1000*10^(Zm/10);
Ps = 10^(P_s/10)/1000*10^(Zs/10);
for i = iter;
%% XY coodinates for Macrocell, small cells and users.
Xm = sqrt(h)*(rand(Vm,1)-0.5);
Ym = sqrt(h)*(rand(Vm,1)-0.5);
Xs = sqrt(h)*(rand(Vs,1)-0.5);
Ys = sqrt(h)*(rand(Vs,1)-0.5);
Xu = sqrt(hu)*(rand(Vu,1)-0.5);
Yu = sqrt(hu)*(rand(Vu,1)-0.5);
%Total coordinates for MBS and small cells
Total_Coord = [Xs Ys ones(size(Xs)) Xm Ym 2*ones(size(Xm))];
%Distance between BSs and users
[Xsm_mat, Xu_mat] = meshgrid(Total_Coord(:,1),Xu);
[Ysm_mat, Yu_mat] = meshgrid(Total_Coord(:,2),Yu);
Distance = sqrt((Xsm_mat-Xu_mat).^2 + (Ysm_mat-Yu_mat).^2);
%% To determine serving BS for each user
[D_m,idx_m] = min(Distance(:,(length(Xs)+1):end),[],2);
idx_m = idx_m + length(Xs);
[D_s,idx_s] = min(Distance(:,1:length(Xs)),[],2);
%% Power received by users from each BS
Psm_mat = [Ps*ones(length(Xu),length(Xs))
Pm*ones(length(Xu),length(Xm))]; % Transmit power of MBS and small cells
Pr_n = Psm_mat.*exprnd(1,size(Psm_mat))./(Distance*1e3).^4;
mult = binornd(1,delta,1,length(Xm)); % Full transmission power of each
interfering MBS for delta
mult(mult == 0) = B; % Reduced transmission power for (1-delta)
Pr = Pr_n.*[ones(length(Xu),length(Xs)) repmat(mult,length(Xu),1)];%
Interference from each BS
%% Power received by each user from serving BSs
Prm = Pr(sub2ind(size(Pr),(1:length(idx_m))',idx_m));
Prs = Pr(sub2ind(size(Pr),(1:length(idx_s))',idx_s));
P_m_n = Pr_n(sub2ind(size(Pr_n),(1:length(idx_m))',idx_m));
%% Total interference for each UE
I_T = sum(Pr,2) - Prm - Prs;
I_1 = P_m_n./(Prs + I_T);
I_2 = Prs./(P_m_n + I_T);
I_3 = B*I_1;
I_4 = Prs./(B*P_m_n + I_T);
end
The error appeared on this line "mult(mult == 0) = B;".
I know it to be assignment problem which requires equality in both the left and right dimensions. Suggestions for correction will be appreciated.
Your vector mult has length Vm (number of macro BS?). Assigning to mult(mult==0) will assign to a subset of this vector (those that have a value equal to zero). What you are assigning is your variable B which you define as B = [0 0.2 0.4 0.6 0.8 1], i.e., it is a length-6 vector. The assignment fails unless mult has exactly 6 zeros.
I highly doubt that this is what you want. It looks like you are trying to assign the same value ("Reduced transmission power"). Then your B should be scalar though.
Since we have no idea what you are trying to do (and your code is not exactly an MCVE), we can only guess.

Calculating numerical integral using integral or quadgk

I am using MATLAB to calculate the numerical integral of a complex function including natural exponent.
I get a warning:
Infinite or Not-a-Number value encountered
if I use the function integral, while another error is thrown:
Output of the function must be the same size as the input
if I use the function quadgk.
I think the reason could be that the integrand is infinite when the variable ep is near zero.
Code shown below. Hope you guys can help me figure it out.
close all
clear
clc
%%
N = 10^5;
edot = 10^8;
yita = N/edot;
kB = 8.6173324*10^(-5);
T = 300;
gamainf = 0.115;
dTol = 3;
K0 = 180;
K = K0/160.21766208;
nu = 3*10^12;
i = 1;
data = [];
%% lambda = ec/ef < 1
for ef = 0.01:0.01:0.1
for lambda = 0.01:0.01:0.08
ec = lambda*ef;
f = #(ep) exp(-((32/3)*pi*gamainf^3*(0.5+0.5*sqrt(1+2*dTol*K*(ep-ec)/gamainf)-dTol*K*(ep-ec)/gamainf).^3/(K*(ep-ec)).^2-16*pi*gamainf^3*(0.5+0.5*sqrt(1+2*dTol*K*(ep-ec)/gamainf)-dTol*K*(ep-ec)/gamainf).^2/((1+dTol*K*(ep-ec)/(gamainf*(0.5+0.5*sqrt(1+2*dTol*K*(ep-ec)/gamainf)-dTol*K*(ep-ec)/gamainf)))*(K*(ep-ec)).^2))/(kB*T));
q = integral(f,0,ef,'ArrayValued',true);
% q = quadgk(f,0,ef);
prob = 1-exp(-yita*nu*q);
data(i,1) = ef;
data(i,2) = lambda;
data(i,3) = q;
i = i+1;
end
end
I've rewritten your equations so that a human can actually understand it:
function integration
N = 1e5;
edot = 1e8;
yita = N/edot;
kB = 8.6173324e-5;
T = 300;
gamainf = 0.115;
dTol = 3;
K0 = 180;
K = K0/160.21766208;
nu = 3e12;
i = 1;
data = [];
%% lambda = ec/ef < 1
for ef = 0.01:0.01:0.1
for lambda = 0.01:0.01:0.08
ec = lambda*ef;
q = integral(#f,0,ef,'ArrayValued',true);
% q = quadgk(f,0,ef);
prob = 1 - exp(-yita*nu*q);
data(i,:) = [ef lambda q];
i = i+1;
end
end
function y = f(ep)
G = K*(ep - ec);
r = dTol*G/gamainf;
S = sqrt(1 + 2*r);
x = (1 + S)/2 - r;
Z = 16*pi*gamainf^3;
y = exp( -Z*x.^2.*( 2*x/(3*G.^2) - 1/(G.^2*(1 + r/x))) ) /...
(kB*T));
end
end
Now, for the first iteration, ep = 0.01, the value of the argument of the exp() function inside f is huge. In fact, if I rework the function to return the argument to the exponent (not the value):
function y = f(ep)
% ... all of the above
% NOTE: removed the exp() to return the argument
y = -Z*x.^2.*( 2*x/(3*G.^2) - 1/(G.^2*(1 + r/x))) ) /...
(kB*T);
end
and print its value at some example nodes like so:
for ef = 0.01 : 0.01 : 0.1
for lambda = 0.01 : 0.01 : 0.08
ec = lambda*ef;
zzz(i,:) = [f(0) f(ef/4) f(ef)];
i = i+1;
end
end
zzz
I get this:
% f(0) f(ef/4) f(ef)
zzz =
7.878426438111721e+07 1.093627454284284e+05 3.091140080273912e+03
1.986962280947140e+07 1.201698288371587e+05 3.187767404903769e+03
8.908646053687230e+06 1.325435523124976e+05 3.288027743119838e+03
5.055141696747510e+06 1.467952125661714e+05 3.392088351112798e+03
...
3.601790797707676e+04 2.897200140791236e+02 2.577170427480841e+01
2.869829209254144e+04 3.673888685004256e+02 2.404148067956737e+01
2.381082059148755e+04 4.671147785149462e+02 2.238181495716831e+01
So, integral() has to deal with things like exp(10^7). This may not be a problem per se if the argument would fall off quickly enough, but as shown above, it doesn't.
So basically you're asking for the integral of a function that ranges in value between exp(~10^7) and exp(~10^3). Needless to say, The d(ef) in the order of 0.01 isn't going to compensate for that, and it'll be non-finite in floating point arithmetic.
I suspect you have a scaling problem. Judging from the names of your variables as well as the equations, I would think that this has something to do with thermodynamics; a reworked form of Planck's law? In that case, I'd check if you're working in nanometers; a few factors of 10^(-9) will creep in, rescaling your integrand to the compfortably computable range.
In any case, it'll be wise to check all your units, because it's something like that that's messing up the numbers.
NB: the maximum exp() you can compute is around exp(709.7827128933840)

Solving PDE with Matlab

`sol = pdepe(m,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,x,t);
% Extract the first solution component as u.
u = sol(:,:,:);
function [c,f,s] = ParticleDiffusionpde(x,t,u,DuDx)
global Ds
c = 1/Ds;
f = DuDx;
s = 0;
function u0 = ParticleDiffusionic(x)
global qo
u0 = qo;
function [pl,ql,pr,qr] = ParticleDiffusionbc(xl,ul,xr,ur,t,x)
global Ds K n
global Amo Gc kf rhop
global uavg
global dr R nr
sum = 0;
for i = 1:1:nr-1
r1 = (i-1)*dr; % radius at i
r2 = i * dr; % radius at i+1
r1 = double(r1); % convert to double precision
r2 = double(r2);
sum = sum + (dr / 2 * (r1*ul+ r2*ur));
end;
uavg = 3/R^3 * sum;
ql = 1;
pl = 0;
qr = 1;
pr = -((kf/(Ds.*rhop)).*(Amo - Gc.*uavg - ((double(ur/K)).^2).^(n/2) ));`
dq(r,t)/dt = Ds( d2q(r,t)/dr2 + (2/r)*dq(r,t)/dr )
q(r, t=0) = 0
dq(r=0, t)/dr = 0
dq(r=dp/2, t)/dr = (kf/Ds*rhop) [C(t) - Cp(at r = dp/2)]
q = solid phase concentration of trace compound in a particle with radius dp/2
C = bulk liquid concentration of trace compound
Cp = trace compound concentration at particle surface
I want to solve the above pde with initial and boundary conditions given. Tried Matlab's pdepe, but does not work satisfactorily. Maybe the boundary conditions is creating problem for me. I also used this isotherm equation for equilibrium: q = K*Cp^(1/n). This is convection-diffusion equation but i could not find any write ups that addresses solving this type of equation properly.
There are two problems with the current implementation.
Incorrect Source Term
The PDE you are attempting to solve has the form
which has the equivalent form
where the last term arises due to the factor of 2 in the original PDE.
The last term needs to be incorporated into pdepe via a source term.
Calculation of q average
The current implementation attempts to calculate the average value of q using the left and right values of q passed to the boundary condition function.
This is incorrect.
The average value of q needs to be calculated from a vector of up-to-date values of the quantity.
However, we have the complication that the only function to receive all mesh values is ParticleDiffusionpde; however, the mesh values passed to that function are not guaranteed to be from the mesh we provided.
Solution: use events (as described in the pdepe documentation).
This is a hack since the event function is meant to detect zero-crossings, but it has the advantage that the function is given all values of q on the mesh we provide.
So, the working example below (you'll notice I set all of the parameters to 1 since I didn't know better) uses the events function to update a variable qStore that can be accessed by the boundary condition function (see here for an explanation), and the boundary condition function performs a vectorized trapezoidal integration for the average calculation.
Working Example
function [] = ParticleDiffusion()
% Parameters
Ds = 1;
q0 = 0;
K = 1;
n = 1;
Amo = 1;
Gc = 1;
kf = 1;
rhop = 1;
% Space
rMesh = linspace(0,1,10);
rMesh = rMesh(:) ;
dr = rMesh(2) - rMesh(1) ;
% Time
tSpan = linspace(0,1,10);
% Vector to store current u-value
qStore = zeros(size(rMesh));
options.Events = #(m,t,x,y) events(m,t,x,y);
% Solve
[sol,~,~,~,~] = pdepe(1,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,rMesh,tSpan,options);
% Use the events function to update qStore
function [value,isterminal,direction] = events(m,~,~,y)
qStore = y; % Value of q on rMesh
value = m; % Since m is constant, it will never be zero (no event detection)
isterminal = 0; % Continue integration
direction = 0; % Detect all zero crossings (not important)
end
function [c,f,s] = ParticleDiffusionpde(r,~,~,DqDr)
% Define the capacity, flux, and source
c = 1/Ds;
f = DqDr;
s = DqDr./r;
end
function u0 = ParticleDiffusionic(~)
u0 = q0;
end
function [pl,ql,pr,qr] = ParticleDiffusionbc(~,~,R,ur,~)
% Calculate average value of current solution
qL = qStore(1:end-1);
qR = qStore(2: end );
total = sum((qL.*rMesh(1:end-1) + qR.*rMesh(2:end))) * dr/2;
qavg = 3/R^3 * total;
% Left boundary
pl = 0;
ql = 1;
% Right boundary
qr = 1;
pr = -(kf/(Ds.*rhop)).*(Amo - Gc.*qavg - (ur/K).^n);
end
end

How to stop MATLAB from rounding extremely small values to 0?

I have a code in MATLAB which works with very small numbers, for example, I have values that are on the order of 10^{-25}, however when MATLAB does the calculations, the values themselves are rounded to 0. Note, I am not referring to format to display these extra decimals, but rather the number itself is changed to 0. I think the reason is because MATLAB, by default, uses up to 15 digits after the decimal point for its calculations. How can I change this so that numbers that are very very small are retained as they are in the calculations?
EDIT:
My code is the following:
clc;
clear;
format long;
% Import data
P = xlsread('Data.xlsx', 'P');
d = xlsread('Data.xlsx', 'd');
CM = xlsread('Data.xlsx', 'Cov');
Original_PD = P; %Store original PD
LM_rows = size(P,1)+1; %Expected LM rows
LM_columns = size(P,2); %Expected LM columns
LM_FINAL = zeros(LM_rows,LM_columns); %Dimensions of LM_FINAL
for ii = 1:size(P,2)
P = Original_PD(:,ii);
% c1, c2, ..., cn, c0, f
interval = cell(size(P,1)+2,1);
for i = 1:size(P,1)
interval{i,1} = NaN(size(P,1),2);
interval{i,1}(:,1) = -Inf;
interval{i,1}(:,2) = d;
interval{i,1}(i,1) = d(i,1);
interval{i,1}(i,2) = Inf;
end
interval{i+1,1} = [-Inf*ones(size(P,1),1) d];
interval{i+2,1} = [d Inf*ones(size(P,1),1)];
c = NaN(size(interval,1),1);
for i = 1:size(c,1)
c(i,1) = mvncdf(interval{i,1}(:,1),interval{i,1}(:,2),0,CM);
end
c0 = c(size(P,1)+1,1);
f = c(size(P,1)+2,1);
c = c(1:size(P,1),:);
b0 = exp(1);
b = exp(1)*P;
syms x;
eqn = f*x;
for i = 1:size(P,1)
eqn = eqn*(c0/c(i,1)*x + (b(i,1)-b0)/c(i,1));
end
eqn = c0*x^(size(P,1)+1) + eqn - b0*x^size(P,1);
x0 = solve(eqn);
x0 = double(x0);
for i = 1:size(x0)
id(i,1) = isreal(x0(i,1));
end
x0 = x0(id,:);
x0 = x0(x0 > 0,:);
clear x;
for i = 1:size(P,1)
x(i,:) = (b(i,1) - b0)./(c(i,1)*x0) + c0/c(i,1);
end
% x = [x0 x1 ... xn]
x = [x0'; x];
x = x(:,sum(x <= 0,1) == 0);
% lamda
lamda = -log(x);
LM_FINAL(:,ii) = lamda;
end
The problem is in this step:
for i = 1:size(P,1)
x(i,:) = (b(i,1) - b0)./(c(i,1)*x0) + c0/c(i,1);
end
where the "difference" gets very close to 0. How can I stop this rounding from occurring at this step?
For example, when i = 10, I have the following values:
b_10 = 0.006639735483297
b_0 = 2.71828182845904
c_10 = 0.000190641848119641
c_0 = 0.356210110252579
x_0 = 7.61247930625269
After doing the calculations we get: -1868.47805854794 + 1868.47805854794 which yields a difference of -2.27373675443232E-12, that gets rounded to 0 by MATLAB.
EDIT 2:
Here is my data file which is used for the code. After you run the code (should take about a minute and half to finish running), row 11 in the variable x shows 0 (even after double clicking to check it's real value), when it shouldn't.
The problem you're having is because the IEEE standard for floating points can't distinguish your numbers from zero because they don't utilize sufficient bits.
Have a look at John D'Errico's Big Decimal Class and Variable Precision Integer Arithmetic. Another option would be to use the Big Integer Class from Java but that might be more challenging if you are unfamiliar with using Java and othe rexternal libraries in MATLAB.
Can you give an example of the calculations in which you are using 1e-25 and getting zero? Here's what I get for a floating point called small_num and one of John's high-precision-floats called small_hpf when assigning them and multiplying by pi.
>> small_num = 1e-25
small_num =
1.0000e-25
>> small_hpf = hpf(1e-25)
small_hpf =
1.000000000000000038494869749191839081371989361591338301396127644e-25
>> small_num * pi
ans =
3.1416e-25
>> small_hpf * pi
ans =
3.141592653589793236933163473501228686498684350685747717239459106e-25