Matlab Genetic Algorithm for a non-genetic case - matlab

I've never used optimization tools, but I think I have to use now, so I'm a bit lost. After using the answer given by #A. Donda, I have noticed that maybe that is not the best solution because every time I run the function it gives a different matrix 'pares' and in the majority of times it says that I need more evaluations. So I was thinking that maybe the answer to my problem are Genetic Algorithms optimization, but once again I do not know how to work with them.
My first problem is described below and the answer by #A. Donda is in the only post of a answer. I really need this optimization done and I don't know how to proceed for this case with GA tools.
Thank you so much in advance again, and thank you #A. Donda for your answer.
As asked, I tried to put here the code that I was trying to explain, I hope it will result:
function opt_pares
clear all; clc; close all;
h = randi(24,8760,1);
nd = randi(365,8760,1);
veic = randi(333,8760,1);
max_veic = max(veic);
veicN = veic./max_veic;
Gh = randi(500,8760,1);
Dh = randi(500,8760,1);
Ih = Gh-Dh;
A = randi([300 800], 27,1);
max_Gh = max(Gh);
max_Dh = max(Dh);
max_Ih = max(Ih);
lat = 70;
HRA =15.*(h-12);
decl = 23.27*sind(360*(284+nd)/365);
Ii = zeros(8760,27);
Di = zeros(8760,27);
Gi = zeros(8760,27);
pares = randi([0,90],27,2);
inclin = pares(:,1);
azim = pares(:,2);
% for MATRIZC
for n=1:27
Ii(:,n) = Ih.*(sind(decl).*sind(lat).*cosd(inclin(n))-sind(decl).*cosd(lat).*sind(inclin(n)).*cosd(azim(n))+cosd(decl).*cosd(lat).*cosd(inclin(n)).*cosd(HRA)+cosd(decl).*sind(lat).*sind(inclin(n)).*cosd(azim(n)).*cosd(HRA)+cosd(decl).*sind(inclin(n)).*sind(azim(n)).*sind(HRA));
Di(:,n) = 0.5*Dh.*(1+cosd(inclin(n)));
Gi(:,n) = (Ii(:,n)+Di(:,n))*A(n,1);
end
Gparque = sum(Gi,2);
max_Gparque = max(Gparque);
GparqueN = Gparque./max_Gparque;
RMSE = sqrt(mean((GparqueN-veicN).^2));
% end
end
I don't know if it is possible, maybe this time I can be more assertive.
My main goal is to achieve the best 'RMSE' possible, to do so I have to create a matrix ('pares') where each line contains a pair of values (one value from each column).
These values have to be within a certain range(0-90). With each of this 27 pairs I have to calculate 'Ii'/'Gi'/'Di', giving me a matrix with a size like 8760*27.
Then I make a sum of 'Gi' to have 'Gparque'(vector 8760*1) and finally I I calculate 'RMSE'. When I have RMSE calculated, I have to modify the matrix 'pares' to other values that can result in a better RMSE. Once there are many combinations of 27 values that can be within the 0-90 range, I have to get a solution that can optimize this search for the minimum RMSE.
The parts that are in comments in the code (a for loop with 'pares') is the thing that I have no idea how to do, because I have to change the values of 'pares' but with some optimization criteria that can approximate the minimum of RMSE.
I hope this time I have explain this doubt better.
Thank you very much!

OK, so here is an attempt at a question. I'm not sure how useful the results will be in the end, because I don't understand the underlying problem and I don't have real data to test it with.
You were right that you need an optimization algorithm, your problem appears to be more complex than simple linear algebra. For optimization I use the function fminsearch from the Optmization Toolbox.
First the function whose value is to be optimized (the objective function) needs to be defined. Based on your code, this is
function RMSE = fun(pares)
inclin = pares(:,1);
azim = pares(:,2);
Ii = zeros(8760,27);
Di = zeros(8760,27);
Gi = zeros(8760,27);
for n=1:27
Ii(:,n) = Ih.*(sind(decl).*sind(lat).*cosd(inclin(n))-sind(decl).*cosd(lat).*sind(inclin(n)).*cosd(azim(n))+cosd(decl).*cosd(lat).*cosd(inclin(n)).*cosd(HRA)+cosd(decl).*sind(lat).*sind(inclin(n)).*cosd(azim(n)).*cosd(HRA)+cosd(decl).*sind(inclin(n)).*sind(azim(n)).*sind(HRA));
Di(:,n) = 0.5*Dh.*(1+cosd(inclin(n)));
Gi(:,n) = (Ii(:,n)+Di(:,n))*A(n,1);
end
Gparque = sum(Gi,2);
max_Gparque = max(Gparque);
GparqueN = Gparque./max_Gparque;
RMSE = sqrt(mean((GparqueN-veicN).^2));
end
Now we can call
pares_opt = fminsearch(#fun, randi([0,90],27,2))
using random initialization. The optimization takes quite a while because the objective function is not very efficiently implemented. Here's a vectorized version that does the same:
% precompute
cHRA = cosd(HRA);
sHRA = sind(HRA);
sdecl = sind(decl);
cdecl = cosd(decl);
slat = sind(lat);
clat = cosd(lat);
function RMSE = fun(pares)
% precompute
cinclin = cosd(pares(:,1))';
sinclin = sind(pares(:,1))';
cazim = cosd(pares(:,2))';
sazim = sind(pares(:,2))';
Ii = bsxfun(#times, Ih, ...
sdecl * (slat * cinclin - clat * sinclin .* cazim) ...
+ (cdecl .* cHRA) * (clat * cinclin + slat * sinclin .* cazim) ...
+ (cdecl .* sHRA) * (sinclin .* sazim));
Di = 0.5 * Dh * (1 + cinclin);
Gi = (Ii + Di) * diag(A);
Gparque = sum(Gi,2);
max_Gparque = max(Gparque);
GparqueN = Gparque./max_Gparque;
RMSE = sqrt(mean((GparqueN-veicN).^2));
end
We have not yet implemented the constraint for pares to lie within [0, 90]. A crude way to do this is to insert these lines:
if any(pares(:) < 0) || any(pares(:) > 90)
RMSE = inf;
return
end
at the beginning of the objective function.
Putting it all together:
function Raquel
h = randi(24,8760,1);
nd = randi(365,8760,1);
veic = randi(333,8760,1);
max_veic = max(veic);
veicN = veic./max_veic;
Gh = randi(500,8760,1);
Dh = randi(500,8760,1);
Ih = Gh-Dh;
A = randi([300 800], 27,1);
lat = 70;
HRA =15.*(h-12);
decl = 23.27*sind(360*(284+nd)/365);
% precompute
cHRA = cosd(HRA);
sHRA = sind(HRA);
sdecl = sind(decl);
cdecl = cosd(decl);
slat = sind(lat);
clat = cosd(lat);
pares_opt = fminsearch(#fun, randi([0,90],27,2))
function RMSE = fun(pares)
if any(pares(:) < 0) || any(pares(:) > 90)
RMSE = inf;
return
end
% precompute
cinclin = cosd(pares(:,1))';
sinclin = sind(pares(:,1))';
cazim = cosd(pares(:,2))';
sazim = sind(pares(:,2))';
Ii = bsxfun(#times, Ih, ...
sdecl * (slat * cinclin - clat * sinclin .* cazim) ...
+ (cdecl .* cHRA) * (clat * cinclin + slat * sinclin .* cazim) ...
+ (cdecl .* sHRA) * (sinclin .* sazim));
Di = 0.5 * Dh * (1 + cinclin);
Gi = (Ii + Di) * diag(A);
Gparque = sum(Gi,2);
max_Gparque = max(Gparque);
GparqueN = Gparque./max_Gparque;
RMSE = sqrt(mean((GparqueN-veicN).^2));
end
end
With simulated data, if I run the optimization twice on the same randomized data but different initial values I get different solutions. This is an indication that there is more than one local minimum of the objective function. Hopefully, this will not be the case with real data.

Related

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)

How to reduce the time consumed by the for loop?

I am trying to implement a simple pixel level center-surround image enhancement. Center-surround technique makes use of statistics between the center pixel of the window and the surrounding neighborhood as a means to decide what enhancement needs to be done. In the code given below I have compared the center pixel with average of the surrounding information and based on that I switch between two cases to enhance the contrast. The code that I have written is as follows:
im = normalize8(im,1); %to set the range of pixel from 0-255
s1 = floor(K1/2); %K1 is the size of the window for surround
M = 1000; %is a constant value
out1 = padarray(im,[s1,s1],'symmetric');
out1 = CE(out1,s1,M);
out = (out1(s1+1:end-s1,s1+1:end-s1));
out = normalize8(out,0); %to set the range of pixel from 0-1
function [out] = CE(out,s,M)
B = 255;
out1 = out;
for i = s+1 : size(out,1) - s
for j = s+1 : size(out,2) - s
temp = out(i-s:i+s,j-s:j+s);
Yij = out1(i,j);
Sij = (1/(2*s+1)^2)*sum(sum(temp));
if (Yij>=Sij)
Aij = A(Yij-Sij,M);
out1(i,j) = ((B + Aij)*Yij)/(Aij+Yij);
else
Aij = A(Sij-Yij,M);
out1(i,j) = (Aij*Yij)/(Aij+B-Yij);
end
end
end
out = out1;
function [Ax] = A(x,M)
if x == 0
Ax = M;
else
Ax = M/x;
end
The code does the following things:
1) Normalize the image to 0-255 range and pad it with additional elements to perform windowing operation.
2) Calls the function CE.
3) In the function CE obtain the windowed image(temp).
4) Find the average of the window (Sij).
5) Compare the center of the window (Yij) with the average value (Sij).
6) Based on the result of comparison perform one of the two enhancement operation.
7) Finally set the range back to 0-1.
I have to run this for multiple window size (K1,K2,K3, etc.) and the images are of size 1728*2034. When the window size is selected as 100, the time consumed is very high.
Can I use vectorization at some stage to reduce the time for loops?
The profiler result (for window size 21) is as follows:
The profiler result (for window size 100) is as follows:
I have changed the code of my function and have written it without the sub-function. The code is as follows:
function [out] = CE(out,s,M)
B = 255;
Aij = zeros(1,2);
out1 = out;
n_factor = (1/(2*s+1)^2);
for i = s+1 : size(out,1) - s
for j = s+1 : size(out,2) - s
temp = out(i-s:i+s,j-s:j+s);
Yij = out1(i,j);
Sij = n_factor*sum(sum(temp));
if Yij-Sij == 0
Aij(1) = M;
Aij(2) = M;
else
Aij(1) = M/(Yij-Sij);
Aij(2) = M/(Sij-Yij);
end
if (Yij>=Sij)
out1(i,j) = ((B + Aij(1))*Yij)/(Aij(1)+Yij);
else
out1(i,j) = (Aij(2)*Yij)/(Aij(2)+B-Yij);
end
end
end
out = out1;
There is a slight improvement in the speed from 93 sec to 88 sec. Suggestions for any other improvements to my code are welcomed.
I have tried to incorporate the suggestions given to replace sliding window with convolution and then vectorize the rest of it. The code below is my implementation and I'm not getting the result expected.
function [out_im] = CE_conv(im,s,M)
B = 255;
temp = ones(2*s,2*s);
temp = temp ./ numel(temp);
out1 = conv2(im,temp,'same');
out_im = im;
Aij = im-out1; %same as Yij-Sij
Aij1 = out1-im; %same as Sij-Yij
Mij = Aij;
Mij(Aij>0) = M./Aij(Aij>0); % if Yij>Sij Mij = M/Yij-Sij;
Mij(Aij<0) = M./Aij1(Aij<0); % if Yij<Sij Mij = M/Sij-Yij;
Mij(Aij==0) = M; % if Yij-Sij == 0 Mij = M;
out_im(Aij>=0) = ((B + Mij(Aij>=0)).*im(Aij>=0))./(Mij(Aij>=0)+im(Aij>=0));
out_im(Aij<0) = (Mij(Aij<0).*im(Aij<0))./ (Mij(Aij<0)+B-im(Aij<0));
I am not able to figure out where I'm going wrong.
A detailed explanation of what I'm trying to implement is given in the following paper:
Vonikakis, Vassilios, and Ioannis Andreadis. "Multi-scale image contrast enhancement." In Control, Automation, Robotics and Vision, 2008. ICARCV 2008. 10th International Conference on, pp. 856-861. IEEE, 2008.
I've tried to see if I could get those times down by processing with colfiltand nlfilter, since both are usually much faster than for-loops for sliding window image processing.
Both worked fine for relatively small windows. For an image of 2048x2048 pixels and a window of 10x10, the solution with colfilt takes about 5 seconds (on my personal computer). With a window of 21x21 the time jumped to 27 seconds, but that is still a relative improvement on the times displayed on the question. Unfortunately I don't have enough memory to colfilt using windows of 100x100, but the solution with nlfilter works, though taking about 120 seconds.
Here the code
Solution with colfilt:
function outval = enhancematrix(inputmatrix,M,B)
%Inputmatrix is a 2D matrix or column vector, outval is a 1D row vector.
% If inputmatrix is made of integers...
inputmatrix = double(inputmatrix);
%1. Compute S and Y
normFactor = 1 / (size(inputmatrix,1) + 1).^2; %Size of column.
S = normFactor*sum(inputmatrix,1); % Sum over the columns.
Y = inputmatrix(ceil(size(inputmatrix,1)/2),:); % Center row.
% So far we have all S and Y, one value per column.
%2. Compute A(abs(Y-S))
A = Afunc(abs(S-Y),M);
% And all A: one value per column.
%3. The tricky part. If Y(i)-S(i) > 0 do something.
doPositive = (Y > S);
doNegative = ~doPositive;
outval = zeros(1,size(inputmatrix,2));
outval(doPositive) = (B + A(doPositive) .* Y(doPositive)) ./ (A(doPositive) + Y(doPositive));
outval(doNegative) = (A(doNegative) .* Y(doNegative)) ./ (A(doNegative) + B - Y(doNegative));
end
function out = Afunc(x,M)
% Input x is a row vector. Output is another row vector.
out = x;
out(x == 0) = M;
out(x ~= 0) = M./x(x ~= 0);
end
And to call it, simply do:
M = 1000; B = 255; enhancenow = #(x) enhancematrix(x,M,B);
w = 21 % windowsize
result = colfilt(inputImage,[w w],'sliding',enhancenow);
Solution with nlfilter:
function outval = enhanceimagecontrast(neighbourhood,M,B)
%1. Compute S and Y
normFactor = 1 / (length(neighbourhood) + 1).^2;
S = normFactor*sum(neighbourhood(:));
Y = neighbourhood(ceil(size(neighbourhood,1)/2),ceil(size(neighbourhood,2)/2));
%2. Compute A(abs(Y-S))
test = (Y>=S);
A = Afunc(abs(Y-S),M);
%3. Return outval
if test
outval = ((B + A) * Y) / (A + Y);
else
outval = (A * Y) / (A + B - Y);
end
function aval = Afunc(x,M)
if (x == 0)
aval = M;
else
aval = M/x;
end
And to call it, simply do:
M = 1000; B = 255; enhancenow = #(x) enhanceimagecontrast(x,M,B);
w = 21 % windowsize
result = nlfilter(inputImage,[w w], enhancenow);
I didn't spend much time checking that everything is 100% correct, but I did see some nice contrast enhancement (hair looks particularly nice).
This answer is the implementation that was suggested by Peter. I debugged the implementation and presenting the final working version of the fast implementation.
function [out_im] = CE_conv(im,s,M)
B = 255;
im = ( im - min(im(:)) ) ./ ( max(im(:)) - min(im(:)) )*255;
h = ones(s,s)./(s*s);
out1 = imfilter(im,h,'conv');
out_im = im;
Aij = im-out1; %same as Yij-Sij
Aij1 = out1-im; %same as Sij-Yij
Mij = Aij;
Mij(Aij>0) = M./Aij(Aij>0); % if Yij>Sij Mij = M/(Yij-Sij);
Mij(Aij<0) = M./Aij1(Aij<0); % if Yij<Sij Mij = M/(Sij-Yij);
Mij(Aij==0) = M; % if Yij-Sij == 0 Mij = M;
out_im(Aij>=0) = ((B + Mij(Aij>=0)).*im(Aij>=0))./(Mij(Aij>=0)+im(Aij>=0));
out_im(Aij<0) = (Mij(Aij<0).*im(Aij<0))./ (Mij(Aij<0)+B-im(Aij<0));
out_im = ( out_im - min(out_im(:)) ) ./ ( max(out_im(:)) - min(out_im(:)) );
To call this use the following code
I = imread('pout.tif');
w_size = 51;
M = 4000;
output = CE_conv(I(:,:,1),w_size,M);
The output for the 'pout.tif' image is given below
The execution time for Bigger image and with 100*100 block size is around 5 secs with this implementation.

How to vectorize sum of vector functions

I have a for loop in MATLAB which calculates sum of sine functions as follows:
% preliminary constants, etc.
tTot = 2;
fS = 10000;dt = 1/fS; % total time, sampling rate
Npts = tTot * fS; %number of points
t = dt:dt:tTot;
c1 = 2*pi/tTot;
c2 = pi/fS;
s = zeros(1,Npts)
% loop to optimize:
for(k=1:Npts/2)
s = s + sin(c1*k*t - c2*k*(k-1))
end
Basically, a one-liner for loop that becomes really slow as Npts becomes large. The difficulty comes in the fact that I am summing vectors which are defined by a parameter k, over k.
Is there a way to make this more efficient by vectorizing? One approach I've taken so far is defining a matrix and summing out the result, but this gives me an out of memory error for larger vectors:
[K,T] = meshgrid(1:1:Npts,t);
s = sum(sin(c1*K.*T - c2*K.*(K-1)),2);
Approach #1
Using sine of difference formula: sin(A-B) = sin A cos B - cos A sin B that enables us to leverage fast matrix multiplication -
K = 1:Npts/2;
p1 = bsxfun(#times,c1*K(:),t(:).');
p2 = c2*K(:).*(K(:)-1);
s = cos(p2).'*sin(p1) - sin(p2).'*cos(p1);
Approach #2
With bsxfun -
K = 1:Npts/2;
p1 = bsxfun(#times,c1*K(:),t(:).');
p2 = c2*K(:).*(K(:)-1);
s = sum(sin(bsxfun(#minus, p1,p2)),1);
Approach #1 can be modified to bring in a smaller sized loop to accommodate for problems that have large data arrays as shown next -
num_blks = 100;%// Edit this based on how much RAM can handle workspace data
intv_len = Npts/(2*num_blks); %// Interval length based on number of blocks
KP = 1:Npts/2;
P2 = c2*KP(:).*(KP(:)-1);
sin_P2 = sin(P2);
cos_P2 = cos(P2);
s = zeros(1,Npts);
for iter = 1:intv_len:Npts/2
K = iter:iter+intv_len-1;
p1 = bsxfun(#times,c1*K(:),t(:).');
s = s + (cos_P2(K).'*sin(p1) - sin_P2(K).'*cos(p1));
end

Fourth-order Runge–Kutta method (RK4) collapses after a few iterations

I'm trying to solve:
x' = 60*x - 0.2*x*y;
y' = 0.01*x*y - 100* y;
using the fourth-order Runge-Kutta algorithm.
Starting points: x(0) = 8000, y(0) = 300 range: [0,15]
Here's the complete function:
function [xx yy time r] = rk4_m(x,y,step)
A = 0;
B = 15;
h = step;
iteration=0;
t = tic;
xh2 = x;
yh2 = y;
rr = zeros(floor(15/step)-1,1);
xx = zeros(floor(15/step)-1,1);
yy = zeros(floor(15/step)-1,1);
AA = zeros(1, floor(15/step)-1);
while( A < B)
A = A+h;
iteration = iteration + 1;
xx(iteration) = x;
yy(iteration) = y;
AA(iteration) = A;
[x y] = rkstep(x,y,h);
for h2=0:1
[xh2 yh2] = rkstep(xh2,yh2,h/2);
end
r(iteration)=abs(y-yh2);
end
time = toc(t);
xlabel('Range');
ylabel('Value');
hold on
plot(AA,xx,'b');
plot(AA,yy,'g');
plot(AA,r,'r');
fprintf('Solution:\n');
fprintf('x: %f\n', x);
fprintf('y: %f\n', y);
fprintf('A: %f\n', A);
fprintf('Time: %f\n', time);
end
function [xnext, ynext] = rkstep(xcur, ycur, h)
kx1 = f_prim_x(xcur,ycur);
ky1 = f_prim_y(xcur,ycur);
kx2 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx1);
kx3 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx2);
kx4 = f_prim_x(xcur+h,ycur+h*kx3);
ky2 = f_prim_y(xcur+0.5*h*ky1,ycur+0.5*h);
ky3 = f_prim_y(xcur+0.5*h*ky2,ycur+0.5*h);
ky4 = f_prim_y(xcur+h*ky2,ycur+h);
xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end
function [fx] = f_prim_x(x,y)
fx = 60*x - 0.2*x*y;
end
function [fy] = f_prim_y(x,y)
fy = 0.01*x*y - 100*y;
end
And I'm running it by executing: [xx yy time] = rk4_m(8000,300,10)
The problem is that everything collapses after 2-3 iterations returning useless results. What am I doing wrong? Or is just this method not appropriate for this kind equation?
The semicolons are intentionally omitted.
Looks like I didn't pay attention to actual h size. It works now! Thanks!
Looks like some form of the Lotka-Volterra equation?
I'm not sure if if your initial condition is [300;8000] or [8000;300] (you specify it both ways above), but regardless, you have an oscillatory system that you're trying to integrate with a large fixed time step that is (much) greater than the period of oscillation. This is why your error explodes. If you try increasing n (say, 1e6), you'll find that eventually you'll get a stable solution (assuming that your Runge-Kutta implementation is otherwise correct).
Is there a reason why you're not using Matlab's builtin ODE solvers, e.g. ode45 or ode15s?
function ode45demo
[t,y]=odeode45(#f,[0 15],[300;8000]);
figure;
plot(t,y);
function ydot=f(t,y)
ydot(1,1) = 60*y(1) - 0.2*y(1)*y(2);
ydot(2,1) = 0.01*y(1)*y(2) - 100*y(2);
You'll find that adaptive step size solvers are much more efficient for these types of oscillatory problems. Because your system has such a high frequency and seems rather stiff, I suggest that you also look at what ode15s gives and/or adjust the 'AbsTol' and 'RelTol' options with odeset.
The immediate problem is that the RK4 code was not completely evolved from the scalar case to the case of two coupled equations. Note that there is no time parameter in the derivative funtions. x and y are both dependent variables and thus get the slope update defined by the derivative functions in every step. Then xcur gets the kx updates and ycur gets the ky updates.
function [xnext, ynext] = rkstep(xcur, ycur, h)
kx1 = f_prim_x(xcur,ycur);
ky1 = f_prim_y(xcur,ycur);
kx2 = f_prim_x(xcur+0.5*h*kx1,ycur+0.5*h*ky1);
ky2 = f_prim_y(xcur+0.5*h*kx1,ycur+0.5*h*ky1);
kx3 = f_prim_x(xcur+0.5*h*kx2,ycur+0.5*h*ky2);
ky3 = f_prim_y(xcur+0.5*h*kx2,ycur+0.5*h*ky2);
kx4 = f_prim_x(xcur+h*kx3,ycur+h*ky3);
ky4 = f_prim_y(xcur+h*kx3,ycur+h*ky3);
xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end

Computing object statistics from the second central moments

I'm currently working on writing a version of the MATLAB RegionProps function for GNU Octave. I have most of it implemented, but I'm still struggling with the implementation of a few parts. I had previously asked about the second central moments of a region.
This was helpful theoretically, but I'm having trouble actually implementing the suggestions. I get results wildly different from MATLAB's (or common sense for that matter) and really don't understand why.
Consider this test image:
We can see it slants at 45 degrees from the X axis, with minor and major axes of 30 and 100 respectively.
Running it through MATLAB's RegionProps function confirms this:
MajorAxisLength: 101.3362
MinorAxisLength: 32.2961
Eccentricity: 0.9479
Orientation: -44.9480
Meanwhile, I don't even get the axes right. I'm trying to use these formulas from Wikipedia.
My code so far is:
raw_moments.m:
function outmom = raw_moments(im,i,j)
total = 0;
total = int32(total);
im = int32(im);
[height,width] = size(im);
for x = 1:width;
for y = 1:height;
amount = (x ** i) * (y ** j) * im(y,x);
total = total + amount;
end;
end;
outmom = total;
central_moments.m:
function cmom = central_moments(im,p,q);
total = 0;
total = double(total);
im = int32(im);
rawm00 = raw_moments(im,0,0);
xbar = double(raw_moments(im,1,0)) / double(rawm00);
ybar = double(raw_moments(im,0,1)) / double(rawm00);
[height,width] = size(im);
for x = 1:width;
for y = 1:height;
amount = ((x - xbar) ** p) * ((y - ybar) ** q) * double(im(y,x));
total = total + double(amount);
end;
end;
cmom = double(total);
And here's my code attempting to use these. I include comments for the values I get
at each step:
inim = logical(imread('135deg100by30ell.png'));
cm00 = central_moments(inim,0,0); % 2567
up20 = central_moments(inim,2,0) / cm00; % 353.94
up02 = central_moments(inim,0,2) / cm00; % 352.89
up11 = central_moments(inim,1,1) / cm00; % 288.31
covmat = [up20, up11; up11, up02];
%[ 353.94 288.31
% 288.31 352.89 ]
eigvals = eig(covmat); % [65.106 641.730]
minoraxislength = eigvals(1); % 65.106
majoraxislength = eigvals(2); % 641.730
I'm not sure what I'm doing wrong. I seem to be following those formulas correctly, but my results are nonsense. I haven't found any obvious errors in my moment functions, although honestly my understanding of moments isn't the greatest to begin with.
Can anyone see where I'm going astray? Thank you very much.
EDIT:
According to Wikipedia:
the eignevalues [...] are proportional
to the squared length of the eigenvector axes.
which is explained by:
axisLength = 4 * sqrt(eigenValue)
Shown below is my version of the code (I vectorized the moments functions):
my_regionprops.m
function props = my_regionprops(im)
cm00 = central_moments(im, 0, 0);
up20 = central_moments(im, 2, 0) / cm00;
up02 = central_moments(im, 0, 2) / cm00;
up11 = central_moments(im, 1, 1) / cm00;
covMat = [up20 up11 ; up11 up02];
[V,D] = eig( covMat );
[D,order] = sort(diag(D), 'descend'); %# sort cols high to low
V = V(:,order);
%# D(1) = (up20+up02)/2 + sqrt(4*up11^2 + (up20-up02)^2)/2;
%# D(2) = (up20+up02)/2 - sqrt(4*up11^2 + (up20-up02)^2)/2;
props = struct();
props.MajorAxisLength = 4*sqrt(D(1));
props.MinorAxisLength = 4*sqrt(D(2));
props.Eccentricity = sqrt(1 - D(2)/D(1));
%# props.Orientation = -atan(V(2,1)/V(1,1)) * (180/pi); %# sign?
props.Orientation = -atan(2*up11/(up20-up02))/2 * (180/pi);
end
function cmom = central_moments(im,i,j)
rawm00 = raw_moments(im,0,0);
centroids = [raw_moments(im,1,0)/rawm00 , raw_moments(im,0,1)/rawm00];
cmom = sum(sum( (([1:size(im,1)]-centroids(2))'.^j * ...
([1:size(im,2)]-centroids(1)).^i) .* im ));
end
function outmom = raw_moments(im,i,j)
outmom = sum(sum( ((1:size(im,1))'.^j * (1:size(im,2)).^i) .* im ));
end
... and the code to test it:
test.m
I = imread('135deg100by30ell.png');
I = logical(I);
>> p = regionprops(I, {'Eccentricity' 'MajorAxisLength' 'MinorAxisLength' 'Orientation'})
p =
MajorAxisLength: 101.34
MinorAxisLength: 32.296
Eccentricity: 0.94785
Orientation: -44.948
>> props = my_regionprops(I)
props =
MajorAxisLength: 101.33
MinorAxisLength: 32.275
Eccentricity: 0.94792
Orientation: -44.948
%# these values are by hand only ;)
subplot(121), imshow(I), imdistline(gca, [17 88],[9 82]);
subplot(122), imshow(I), imdistline(gca, [43 67],[59 37]);
Are you sure about the core of your raw_moments function? You might try
amount = ((x-1) ** i) * ((y-1) ** j) * im(y,x);
This doesn't seem like enough to cause the problems you're seeing, but it might be at least a part.