"Interpolation requires at least two sample points" Genetic Algorithm to solve ODE
Show older comments
I am attempting to solve a 13 parameter ODE using a genetic algorithm. Please bear with me, I know it's a lot of code, but I've been working for several months trying to fix this on my own. It would probably be easier to skip down to the full heirarchy, or to my section called *to the crux of the matter* and then use the above code for reference.
Most of this code was obtained from a tutorial I am working with: http://matlabgeeks.com/tips-tutorials/ode-tips-tutorials/modeling-with-odes-in-matlab-part-4b/
Here is the code for the GA:
function [P,best] = ga(pop_size,chrom_len,pm,pc,max_gen)
% /---------------------------------------------------------------------\
% | Copyright (C) 2009 George Bezerra |
% | |
% | This program is free software: you can redistribute it and/or modify |
% | it under the terms of the GNU General Public License as published by |
% | the Free Software Foundation, either version 3 of the License, or |
% | (at your option) any later version. |
% | |
% | This program is distributed in the hope that it will be useful, |
% | but WITHOUT ANY WARRANTY; without even the implied warranty of |
% | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
% | GNU General Public License for more details. |
% | |
% | You should have received a copy of the GNU General Public License |
% | along with this program. If not, see <http://www.gnu.org/licenses/>.|
% \ ---------------------------------------------------------------------/
%
% Inputs:
% pop_size => population size
% chrom_len => chromosome length
% pm => probability of mutation
% pc => probability of crossover
% max_gen => maximum number of generations
%
% Outputs:
% P => population
% best => best individual of the population
%
% suggested run: [P,best] = ga(100,100,0.01,0.5,200);
% INITIALIZE POPULATION
P = initialize(pop_size,chrom_len);
% EVALUATION
fit = maxones_fitness(P);
gen = 1;
while gen<=max_gen & max(fit)<chrom_len
% SELECTION
P = tournament_selection(P,fit,2);
% CROSSOVER
P = two_point_crossover(P,pc);
% MUTATION
P = point_mutation(P,pm);
% EVALUATION
fit = fitness(P);
% record data
max_fit(gen) = max(fit);
mean_fit(gen) = mean(fit);
% display information
disp_info(gen,max_fit(gen));
gen = gen+1;
end
disp(sprintf('Generation: %d',gen));
disp(sprintf('Best fitness: %d\n',max_fit(end)));
% plot evolution curve
plot(1:length(max_fit), max_fit,'b');
hold on;
plot(1:length(mean_fit), mean_fit,'g');
hold off;
xlabel('Generations');
ylabel('Fitness');
legend('Best fitness','Average fitness','Location','SouthEast');
% output best individual
[m,ind] = max(fit);
best = P(ind,:);
function [P] = initialize(pop_size,chrom_length)
P = round(rand(pop_size,chrom_length));
function [fit] = maxones_fitness(P)
for i=1:size(P,1)
fit(i) = length(find(P(i,:)));
end
function [P_new] = tournament_selection(P,fit,tourn_size)
for i=1:size(P,1)
t = ceil(rand(1,tourn_size)*size(P,1));
[max_fit,winner] = max(fit(t));
P_new(i,:) = P(t(winner),:);
end
function [P_new] = two_point_crossover(P,pc)
mating_list = randperm(size(P,1));
P_new = [];
while ~isempty(mating_list)
pair = mating_list(1:2);
mating_list(1:2) = [];
if rand<pc
crossover_points = ceil(rand(1,2)*(size(P,2)));
point1 = min(crossover_points);
point2 = max(crossover_points);
individual1 = P(pair(1),:);
individual2 = P(pair(2),:);
individual1(point1:point2) = P(pair(2),point1:point2);
individual2(point1:point2) = P(pair(1),point1:point2);
P_new = [P_new;individual1;individual2];
else
P_new = [P_new;P(pair,:)];
end
end
function [P_new] = point_mutation(P,pm)
r = rand(size(P));
mutation_list = find(r<pm);
P_new = P;
P_new(mutation_list(find(P(mutation_list)==1))) = 0;
P_new(mutation_list(find(P(mutation_list)==0))) = 1;
function [] = disp_info(gen,fit)
if mod(gen,10)==0
disp(sprintf('Generation: %d',gen));
disp(sprintf('Best fitness: %d\n',fit));
end
function [P_new] = roulette_selection(P,fit)
fit = (fit - min(fit)).^2;
fit = cumsum(fit);
fit = fit/max(fit);
P_new = [];
for i=1:size(P,1)
f = find(fit>rand);
P_new = [P_new;P(f(1),:)];
end
I am confident this code itself is ok, as I have used it for a much smaller (two equations and four parameters)
In the "hierarchy" of errors the first error indicates that the problem is in line 52 where it calls the fitness function. Here is the fitness function:
% fitness(P)
% Returns a vector with the fitness scores for each member of the
% population, P
%
% Inputs:
% P - A 2D array of the population genomes
% gene_len - number of binary digits per number (1/2 chrom_len)
% Output:
% fit = A 1D array of fitness scores
function fit = fitness(P)
% Use globals so we can send the values into our tnf.m funciton
global alpha beta gamma zeta delta eta_h eta_c theta epsilon omega psi phi Ko;
% I like to initialize any arrays I use
fit = zeros(size(P,1), 1);
% Loop through each individual in the population
for i=1:size(P,1)
% Convert the current chromosome to real values. I chose to interperet
% the chromosome as two binary representations of numbers. You may
% also want to consider a Grey Code representation (or others).
[alpha, beta, gamma, zeta, delta, eta_h, eta_c, theta, epsilon, omega, psi, phi, Ko] = gene_to_values(P(i,:));
% Get the model output for the given values of beta and delta
% Changed 50000 to 132
[t, y] = ode15s('TNF', [0 20], [132, 0, 0, 400]);
% The fitness is related to the squared error between the model and
% the data, so we use our squared_error.m function. Remember we are
% interested in fitting the predicted infected populatoin, y(:,2), to
% the predicted empricial data, sick*100.
%
%NOTE removed the 100* before second list of data points (actually
%changed to 1)
error = squared_error([0 1 2 3 6 8 20] , [132 183 358 448 563 526 0], t,...
y(:,2));
% The GA maximizes values, so we return the inverse of the error to
% trick the GA into minimzing the error.
fit(i) = error^-1;
end
Line 39 is calling the next bit of code. I will rewrite here for clarity:
error = squared_error([0 1 2 3 6 8 20] , [132 183 358 448 563 526 0], t,...
y(:,2));
[0 1 2 3 6 8 20] are my time points and [132 183 358 448 563 526 0] are my data points that I am trying to solve for.
The ODE being solved with ODE15s is TNF.m and is given here:
% TNF.m
%
function dx = TNF(t,x)
global alpha beta gamma zeta delta eta_h eta_c theta epsilon omega psi phi Ko;
dx = zeros(4,1);
dx(1) = alpha*x(1)*(1-power(x(1)/x(4),1))-beta*x(1)-gamma*x(3)*x(1);
dx(2) = beta*x(1)+zeta*gamma*x(3)*x(1)-delta*x(2);
dx(3) = theta*x(2)+epsilon-omega*x(3) - phi*(power(x(4)/Ko,1));
dx(4) = eta_c*x(3)*x(1)+eta_h*x(3)-psi*x(4)*power(x(4)/Ko,4);
And the last bit of code not native to Matlab is the squared error function:
% squared_error(data_time, data_points, fn_time, fn_points)
% Calculates the squared error between a set of data points and a
% series of points representing a function output.
%
% Inputs:
% data_time - A 1D array containing the time points of the data
% data_points - A 1D array of the data values
% fit_time - A 1D array of the time points for the function output
% fit_points - A 1D array of the function values
% Output:
% error = the sum of the squared residuals of the data vs. the function
function error = squared_error(data_time, data_points, fn_time, fn_points)
% Intepolate the fit to match the time points of the data
fn_values = interp1(fn_time, fn_points, data_time);
% Square the error values and sum them up
error = sum((fn_values - data_points).^2);
Here are the errors:
*Warning: Matrix is singular, close to singular or badly scaled. Results may be inaccurate. RCOND = NaN. > In ode15s at 590 In fitness at 29 In ga at 52 Warning: Failure at t=0.000000e+00. Unable to meet integration tolerances without reducing the step size below the smallest value allowed (7.905050e-323) at time t. > In ode15s at 669 In fitness at 29 In ga at 52 Error using griddedInterpolant Interpolation requires at least two sample points in each dimension.
Error in interp1>Interp1D (line 346) F = griddedInterpolant(Xext,V,method);
Error in interp1 (line 241) Vq = Interp1D(X,V,Xq,method);
Error in squared_error (line 16) fn_values = interp1(fn_time, fn_points, data_time);
Error in fitness (line 39) error = squared_error([0 1 2 3 6 8 20] , [132 183 358 448 563 526 0], t,...
Error in ga (line 52) fit = fitness(P);*
The warnings may not be important. My question (finally!) is on the errors. I can trace the problem as follows:
ga.m calls fitness.m, which calls squared_error.m as follows:
error = squared_error([0 1 2 3 6 8 20] , [132 183 358 448 563 526 0], t,y(:,2));
The first line of squared_error.m provides the syntax:
% squared_error(data_time, data_points, fn_time, fn_points)
Note that fn_time in this place is just t. This is an autonomous ODE that does not depend on t.
So let's look at squared error (the troublesome line 16):
fn_values = interp1(fn_time, fn_points, data_time);
squared_error is calling interp1 with these parameters, basically like this:
interp1(t, [0 1 2 3 6 8 20] , [132 183 358 448 563 526 0])
interp1.m has a syntax as follows (from the comments)
Vq = INTERP1(X,V,Xq) interpolates to find Vq, the values of the
% underlying function V=F(X) at the query points Xq. X must
% be a vector of length N.
so X=t V=[0 1 2 3 6 8 20] Xq = [132 183 358 448 563 526 0]
Now interp1.m is calling interp1d on line 241. (here's where I start to get confused)
Vq = Interp1D(X,V,Xq,method);
Now, I know: X=t V=[0 1 2 3 6 8 20] Xq = [132 183 358 448 563 526 0]
I don't know what the method is. Interp1D doesn't seem to have an m file. Though we're not at the error yet. interp1.m also calls griddedInterpolant.m on like 346. This is where we are getting an error.
F = griddedInterpolant(Xext,V,method);
Now, Xext = X which is still t V is still [0 1 2 3 6 8 20]
*TO THE CRUX OF THE MATTER**
Matlab is complaining that griddedinterpolant.m "requires two sample points in each dimension". I think it is complaining about t. But my ODEs are autonomous and do not depend upon t. I'm clearly not understanding something, and I'm over my head in much of this code.
I think the problem is most likely in the fitness function, and in my lack of understanding in how all of this fits together. The tutorial above will give you an idea what I'm trying to do. I took a basic example of an ODE with two equations and two parameters and tried to adopt it to one with 4 equations and 13 parameters. (gulp!). The code works fine for the simpler case.
I will be eternally grateful for help on this.
-Dave K
5 Comments
Geoff Hayes
on 11 Aug 2014
David - that is a lot of code! For future reference, it may be better if you attach each function to your question (using the paperclip button) rather than pasting the lot of it into the body of your question.
A couple of things - what are the parameters that you use to start the GA? By that I mean, what inputs do you pass to
ga(pop_size,chrom_len,pm,pc,max_gen)
Without knowing this, it is difficult to re-run your code and try to debug what is happening. As well, at least the function gene_to_values is missing from your above code.
----------------------
You state that
ga.m calls fitness.m, which calls squared_error.m as follows:
error = squared_error([0 1 2 3 6 8 20] , [132 183 358 448 563 526 0], t,y(:,2));
and that since
fn_values = interp1(fn_time, fn_points, data_time);
squared_error is calling interp1 with these parameters, basically like this:
interp1(t, [0 1 2 3 6 8 20] , [132 183 358 448 563 526 0])
This is not quite correct. The signature for squared_error is
function error = squared_error(data_time, data_points, fn_time, fn_points)
so interp1 makes use of t, y(:,2), and [0 1 2 3 6 8 20] in that order.
-----------------------
I looked at the link, and saw the simplified version of gene_to_values, so you may want to post that piece of code and have it reviewed since your version must return 13 parameters whereas the posted one is just returning 2. It seems here that it would be easy to make a mistake in the conversion from a binary representation of a number to its floating-point equivalent.
Note that if I just set all of these 13 variables to zero, then from the output of ode15s, t becomes a 1x1 scalar and y is a 1x4 vector. And when squared_error is called, I observe the same error message as you. Note that the documentation for squared_error suggests that all inputs are 1D arrays. So if t is just a scalar, then that goes against the assumptions of the code.
David
on 12 Aug 2014
Geoff Hayes
on 12 Aug 2014
Hi David - you didn't attach genes_to_values.m. Could you? It does seem kind of tricky especially as it is within this code that you define the mapping of the ten bits (for each variable) to the interval assigned to that variable.
Note that the first input to ga(132,130,0.01,0.5,200) is 132 and that corresponds to the population size i.e. the number of "organisms" in the population that will be randomly assigned data for each of the 13 variables for the fitness function. I am not sure what you mean by the first data point...
David
on 12 Aug 2014
Geoff Hayes
on 12 Aug 2014
You realize that the population size will not change over time? It will remain fixed at 132 members (unless the code has been modified to , where each member eventually evolves to the optimal (or sub-optimal) solution.
Accepted Answer
More Answers (0)
Categories
Find more on Mathematics in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!