Matlab Optimisation - Minimise objective function using genetic algorithm - matlab

I want to set up the generic algorithm for a function that includes roughly 400 lines of script. The script itself is an optimisation process and I want to use the genetic algorithm to find the best input parameters into the optimisation process (M and OPratio). M lies between 0 and 10^7 and OPratio between 0 and 1.
The function of the script is:
NPVtotal = cut_off_optimisation(M,OPratio)
set up for the genetic algorithm:
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options.InitialPopulationMatrix = X0;
[M,OPratio,fval] = ga(cut_off_optimisation(M,OPratio),nvars,[],[],[],[],LB,UB)
I get following error:
Undefined function or variable 'M'.
I am new to optimisation and the genetic algorithm so would appreciate any help, please let me know if more information is necessary.

First of all I am assuming that the objective is to minimize the Objective function cut_off_optimisation.
Now first update your function to look like this
function y = cut_off_optimisation(x)
M=x(1);
OPratio=x(2);
%
% paste body of your currently used function here
%
y=NPVtotal ;
Now use this code to minimize your objective function.
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0);
[x,fval] = ga(#cut_off_optimisation,nvars,[],[],[],[],...
LB,UB,[],options);
M=x(1);
OPratio=x(2);
Update: If you don't want to update your function. Just run this main code. Keep the function NPVtotal = cut_off_optimisation(M,OPratio) in the same folder as that of the main code.
objectiveFunction=#(x)cut_off_optimisation(x(1),x(2));
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0);
[x,fval] = ga(objectiveFunction,nvars,[],[],[],[],...
LB,UB,[],options);
M=x(1);
OPratio=x(2);
fval
M
OPratio
Update: For getting final population members and fitness values. Replace the above ga function call statement to below statement.
[x,fval,exitflag,output,population,score] = ga(objectiveFunction,nvars,[],[],[],[],LB,UB,[],options);
M=x(1);
OPratio=x(2);
In here population will have the members of the final population and score will have fitness values for the final population. Default population size is 20. So you will have 20 rows in both the matrix. Number of columns in population will be equivalent to number of variables in the problem and score will be a column matrix. You can change the population size by adding option PopulationSize to gaoptimset.
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0,'PopulationSize',30);
To know more about the options available for gaoptimset and their expected values and their {default values}. Go to matlab help and search for gaoptimset. There you will find a table with all these details. Here is the link from matlab website http://in.mathworks.com/help/gads/gaoptimset.html .There may be changes according to your matlab version. So its better to use help in matlab.

Related

How to deal with such error when using MATLAB interior point method to handle convex optimization?

When I implement such MATLAB codes to do convex optimization (the following example is similar to MATLAB official docs from here):
function [xsol,fval,history,searchdir] = runfmincon
clear;clc;
% Set up shared variables with outfun
history.x = [];
history.fval = [];
searchdir = [];
% Call optimization
x0 = [0.1 0.1];
options = optimoptions(#fmincon,'OutputFcn',#outfun,...
'Algorithm','interior-point','Display','iter');
[xsol,fval] = fmincon(#objfun,x0,[],[],[],[],[],[],#confun,options);
function stop = outfun(x,optimValues,state)
stop = false;
switch state
case 'init'
hold on
case 'iter'
% Concatenate current point and objective function
% value with history. x must be a row vector.
history.fval = [history.fval; optimValues.fval];
history.x = [history.x; x];
% Concatenate current search direction with
% searchdir.
searchdir = [searchdir;...
optimValues.searchdirection'];
plot(x(1),x(2),'o');
% Label points with iteration number and add title.
% Add .15 to x(1) to separate label from plotted 'o'.
text(x(1)+.15,x(2),...
num2str(optimValues.iteration));
title('Sequence of Points Computed by fmincon');
case 'done'
hold off
otherwise
end
end
function f = objfun(x)
f = -x(1)*x(2);
end
function [c, ceq] = confun(x)
% Nonlinear inequality constraints
c = [x(1)^2 + x(2)^2 -1];
% Nonlinear equality constraints
ceq = [];
end
end
When I run the code above, an error occurs:
出错 barrier
出错 fmincon (line 834)
[X,FVAL,EXITFLAG,OUTPUT,LAMBDA,GRAD,HESSIAN] = barrier(funfcn,X,A,B,Aeq,Beq,l,u,confcn,options.HessFcn, ...
出错 runfmincon (line 15)
[xsol,fval] = fmincon(#objfun,x0,[],[],[],[],[],[],#confun,options);
Plus, other methods, like sqp and active-set, work fine, ONLY interior-point leads to an error.
I have checked the initial point, I have NOT found any problems.
How to solve that? Please help me!!! Thanks!!!
Oh! I solved it!!! When implementing the interior-point method, just delete the parameter searchdir, and the codes corresponding to it—while for the sqp or active-set methods, keeping this parameter does not pose any problem.

Error lsqnonlin reaching values using nested functions

I want to fit two parameters using lsqnonlin. I have set up my system of ODE and want to solve them with my new parameters that are found after I performed the lsqnonlin.
function [dcdt] = CSTRSinSeries(t,c,par)
for i=1:par.n
if i==1
dcdt(i) = 1/par.tau_per_tank * (par.c0-c(i))+par.kf*(par.c0_meth-c(i))- ...
par.kb*c(i).^2;
else
dcdt(i) = 1/par.tau_per_tank * (c(i-1)-c(i))+par.k*(par.c0_meth-c(i))- ...
par.kb*c(i).^2;
end
end
dcdt = dcdt';
end
% fitcrit function
function error = fitcrit(curve_fit_parameters,time_exp,conc_exp, par, init)
[time_model_fit,conc_model_fit] = ode45(#(t,c) CSTRSinSeries(t,c,par),time_exp,init,[]);
error = (conc_exp-conc_model_fit);
end
I think the problem has to do with the fact that my parameters are in parameter struct par and that I don't want to fit on all of these parameters, but just on basis of two of those.
This is my main script for performing the curve fitting:
% initial guesses for model parameters, no. of indeces is number of fitted % parameters
k0 = [0.028 0.002];
% lower and upper bounds for model parameters, this can be altered
LB = [0.00 0.00];
UB = [Inf Inf];
% Set up fitting options
options = optimset('TolX',1.0E-6,'MaxFunEvals',1000);
% Perform nonlinear least squares fit (note that we store much more
% statistics than just the final fitted parameters)
[curve_fit_parameters,RESNORM,RESIDUAL,EXITFLAG,OUTPUT,LAMBDA,JACOBIAN] = ...
lsqnonlin(#(k) fitcrit(k,time_exp, conc_exp, par, init),k0,LB,UB,options);
The code now does not give an error, however the output of my curve_fit_parameters is now the same as my initial values (also when I change my initial value, it stays the same).
The error is:
>> PackedBed
Initial point is a local minimum.
Optimization completed because the size of the gradient at the initial point
is less than the default value of the optimality tolerance.
<stopping criteria details>
The stopping criteria gives a relative first-order optimality 0.00+00, so I think that the parameters lsqnonlin changes have no influence on my error.
I think that the mistake is the lsqnonlin function, where I refer to 'k' instead of 'par.kb and par.kf'. However, I don't know how to refer to these as these are nested functions. Replacing 'k' by 'par.kb, par.kf' gives me the error: Unexpected matlab operator.
Could anyone help me with my problem?
As suggested by Adriaan in the comments, you can easily make a copy of the par struct in your fitcrit function, and assign the parameters to optimize to this par_tmp struct. The fitcrit function then becomes:
function error = fitcrit(curve_fit_parameters,time_exp,conc_exp, par, init)
par_tmp = par;
par_tmp.kf = curve_fit_parameters(1); % change to parameters you need to fit
par_tmp.kb = curve_fit_parameters(2);
[time_model_fit,conc_model_fit] = ode45(#(t,c) odefun(t,c,par_tmp),time_exp,init,[]); % pass par_tmp to ODE solver
error = (conc_exp-conc_model_fit);
end
In the script calling lsqnonlin, you will have to change the par struct to include the converged solution of the parameter subset to optimize:
% initial guesses for model parameters, no. of indeces is number of fitted % parameters
k0 = [0.028 0.002];
% lower and upper bounds for model parameters, this can be altered
LB = [0.00 0.00];
UB = [Inf Inf];
% Set up fitting options
options = optimset('TolX',1.0E-6,'MaxFunEvals',1000);
% Perform nonlinear least squares fit (note that we store much more
% statistics than just the final fitted parameters)
[curve_fit_parameters,RESNORM,RESIDUAL,EXITFLAG,OUTPUT,LAMBDA,JACOBIAN] = ...
lsqnonlin(#(k) fitcrit(k,time_exp, conc_exp, par, init),k0,LB,UB,options);
% make par_final
par_final = par;
par_final.kf = curve_fit_parameters(1);
par_final.kb = curve_fit_parameters(2);
```

Determining convergence rate of fsolve - Matlab

Suppose I'm solving a system of nonlinear equations. A simple example would be:
function example
x0 = [15; -2];
options = optimoptions('fsolve','Display','iter','TolFun',eps,'TolX',eps);
[x,fval,exitflag,output] = fsolve(#P1a,x0,options);
end
function f1 = P1a(x)
f1 = [x(1)+x(2)*(x(2)*(5-x(2))-2)- 13; x(1)+x(2)*(x(2)*(1+x(2))-14)-29];
end
How do I determine the rate of convergence? 'Display','iter' shows me the norm at each step, but I can't find a way to extract these values. (For this particular example, I believe fsolve does not converge to the right solution, but rather to a local minimum. That is not the issue, however. I just want to find a way to estimate the convergence rate.)
You can get plenty out of fsolve. However, you'll need to do some work. Read up on the 'OutputFcn' option and writing output functions for Matlab's Optimization methods. This is vary similar to the option of the same name used by Matlab's ODE solvers. Here's an example that replicates the 'Display','iter' option-value for fsolve (for the default 'trust-region-dogleg' algorithm specifically):
function stop = outfun(x,optimValues,state)
% See private/trustnleqn
stop = false;
switch state
case 'init'
header = sprintf(['\n Norm of First-order Trust-region\n',...
' Iteration Func-count f(x) step optimality radius']);
disp(header);
case 'iter'
iter = optimValues.iteration; % Iteration
numFevals = optimValues.funccount; % Func-count
F = optimValues.fval; % f(x)
normd = optimValues.stepsize; % Norm of step
normgradinf = optimValues.firstorderopt; % First-order optimality
Delta = optimValues.trustregionradius; % Trust-region radius
if iter > 0
formatstr = ' %5.0f %5.0f %13.6g %13.6g %12.3g %12.3g';
iterOutput = sprintf(formatstr,iter,numFevals,F'*F,normd,normgradinf,Delta);
else
formatstr0 = ' %5.0f %5.0f %13.6g %12.3g %12.3g';
iterOutput = sprintf(formatstr0,iter,numFevals,F'*F,normgradinf,Delta);
end
disp(iterOutput);
case 'done'
otherwise
end
You can then call this via:
function example
P1a=#(x)[x(1)+x(2)*(x(2)*(5-x(2))-2)- 13; x(1)+x(2)*(x(2)*(1+x(2))-14)-29];
x0 = [15; -2];
opts = optimoptions('fsolve','Display','off','OutputFcn',#outfun,'TolFun',eps,'TolX',eps);
[x,fval,exitflag,output] = fsolve(P1a,x0,opts);
This still just prints to the Command Window. From here it's a matter of creating an output function that can write data to an array, file, or other data structure. Here's how you might do that with a global variable (in general, not a good idea):
function stop = outfun2(x,optimValues,state)
stop = false;
global out; % Global variable, define in main function too
switch state
case 'init'
out = [];
case 'iter'
iter = optimValues.iteration; % Iteration
numFevals = optimValues.funccount; % Func-count
F = optimValues.fval; % f(x)
normd = optimValues.stepsize; % Norm of step
normgradinf = optimValues.firstorderopt; % First-order optimality
Delta = optimValues.trustregionradius; % Trust-region radius
out = [out;iter numFevals F'*F normd normgradinf Delta];
case 'done'
otherwise
end
Then just declare global out; in your main function before calling fsolve. You could also accomplish this by making your output function a nested function, in which case the out array would be shared with the outer main function.
The second output function example performs dynamic memory allocation instead of reallocating the entire out array. There's no way around this because both we and the algorithm don't know how many iterations it will take to converge. However, for a few hundred iterations, dynamic memory allocation will be plenty fast.
I'll leave "determining the rate of convergence" to you now that you have the tools in hand...

Matlab ode solvers: changing state and specified time

I am solving a set of ODEs (dy/dt) at t=0, all initial conditions t=0 y_0=(0,0,0). Can I add some number to the y values at different times (e.g., at t=10, y1 should be added to that number; at t=20, y2 should be added to that number, etc.) and solve the equations?
Inserting large discontinuities in your ODE in the way you suggest (and the way illustrated by #macduff) can lead to less precision and longer computation times (especially with ode45 - ode15s might be a better option or at least make sure that your absolute and relative tolerances are suitable). You've effectively produced a very stiff system. If you want to add some number to the ODEs starting at a specific time keep in mind that the solver only evaluates these equations at specific points in time of its own choosing. (Don't be mislead by the fact that you can obtain fixed step-size outputs by specifying tspan as more than two elements – all of Matlab's solvers are variable step-size solvers and choose their true steps based on error criteria.)
A better option is to integrate the system piecewise and append the resultant outputs from each run together:
% t = 0 to t = 10, pass parameter a = 0 to add to ODEs
a = 0;
tspan = [0 10];
[T,Y] = ode45(#(t,y)myfun(t,y,a),tspan,y_0);
% t = 10 to t = 20, pass parameter a = 10 to add to ODEs
a = 10;
[t,y] = ode45(#(t,y)myfun(t,y,a),tspan+T(end),Y(end,:));
T = [T;t(2:end)];
Y = [Y;y(2:end,:)];
% t = 20 to t = 30, pass parameter a = 20 to add to ODEs
a = 20;
[t,y] = ode45(#(t,y)myfun(t,y,a),tspan+T(end),Y(end,:));
T = [T;t(2:end)];
Y = [Y;y(2:end,:)];
The Matlab editor may complain about the array T and Y not being preallocated and/or growing, but it's fine in this case as they're growing in large chunks only a few times. Alternatively, if you want fixed output step-sizes, you can do this:
dt = 0.01;
T = 0:dt:30;
Y = zeros(length(T),length(y_0));
% t = 0 to t = 10, pass parameter a = 0 to add to ODEs
a = 0;
[~,Y(1:10/dt+1,:)] = ode45(#(t,y)myfun(t,y,a),T(1:10/dt+1),y_0);
% t = 10 to t = 20, pass parameter a = 10 to add to ODEs
a = 10;
[~,Y(10/dt+1:20/dt+1,:)] = ode45(#(t,y)myfun(t,y,a),T(10/dt+1:20/dt+1),Y(10/dt+1,:));
% t = 20 to t = 30, pass parameter a = 20 to add to ODEs
a = 20;
[~,Y(20/dt+1:end,:)] = ode45(#(t,y)myfun(t,y,a),T(20/dt+1:end),Y(20/dt+1,:));
One could easily convert both of the above blocks of code to more compact for loops if desired.
In both cases your ODE function myfun incorporates the parameter a this way:
function ydot = myfun(t,y,a)
y(1) = ... % add a however you like
...
Ok, like Simon McKenzie says we really need more info on your urgent issue, but I think I can help. From what you've given us, I'll assume you have a function myfun that you pass to something like ode45
y_0 = [0,0,0];
% here Tfinal is the time in seconds that you want to simulate to
% and specify the tspan so that you will get a solution at each whole
% number of seconds, I believe
[t,y] = ode45(#myfun,[0:0.1:Tfinal],y_0);
Somewhere you have defined your function, here I'll call it myfun
function dy = myfun(t,y)
% here let's check to see if it's time to add your offsets
% still, you may want to put a little fudge factor for the time, but
% you'll have to experiment, I'll set it up though
EPS = 1e-12;
if( t < 10 + EPS || t > 10 - EPS )
y(1) = y(1) + 10;
.
.
.
.
% this only makes sense if you then use y(1) in the compuatation
Otherwise, just add the offset to the returned solution vector, i.e.
idx10 = find( t == 10 ); % should be there since it's in the tspan
y(idx10:end) = y(idx10:end) + 10; % I guess you add it from that point on?

Optimization by perturbing variable

My main script contains following code:
%# Grid and model parameters
nModel=50;
nModel_want=1;
nI_grid1=5;
Nth=1;
nRow.Scale1=5;
nCol.Scale1=5;
nRow.Scale2=5^2;
nCol.Scale2=5^2;
theta = 90; % degrees
a_minor = 2; % range along minor direction
a_major = 5; % range along major direction
sill = var(reshape(Deff_matrix_NthModel,nCell.Scale1,1)); % variance of the coarse data matrix of size nRow.Scale1 X nCol.Scale1
%# Covariance computation
% Scale 1
for ihRow = 1:nRow.Scale1
for ihCol = 1:nCol.Scale1
[cov.Scale1(ihRow,ihCol),heff.Scale1(ihRow,ihCol)] = general_CovModel(theta, ihCol, ihRow, a_minor, a_major, sill, 'Exp');
end
end
% Scale 2
for ihRow = 1:nRow.Scale2
for ihCol = 1:nCol.Scale2
[cov.Scale2(ihRow,ihCol),heff.Scale2(ihRow,ihCol)] = general_CovModel(theta, ihCol/(nCol.Scale2/nCol.Scale1), ihRow/(nRow.Scale2/nRow.Scale1), a_minor, a_major, sill/(nRow.Scale2*nCol.Scale2), 'Exp');
end
end
%# Scale-up of fine scale values by averaging
[covAvg.Scale2,var_covAvg.Scale2,varNorm_covAvg.Scale2] = general_AverageProperty(nRow.Scale2/nRow.Scale1,nCol.Scale2/nCol.Scale1,1,nRow.Scale1,nCol.Scale1,1,cov.Scale2,1);
I am using two functions, general_CovModel() and general_AverageProperty(), in my main script which are given as following:
function [cov,h_eff] = general_CovModel(theta, hx, hy, a_minor, a_major, sill, mod_type)
% mod_type should be in strings
angle_rad = theta*(pi/180); % theta in degrees, angle_rad in radians
R_theta = [sin(angle_rad) cos(angle_rad); -cos(angle_rad) sin(angle_rad)];
h = [hx; hy];
lambda = a_minor/a_major;
D_lambda = [lambda 0; 0 1];
h_2prime = D_lambda*R_theta*h;
h_eff = sqrt((h_2prime(1)^2)+(h_2prime(2)^2));
if strcmp(mod_type,'Sph')==1 || strcmp(mod_type,'sph') ==1
if h_eff<=a
cov = sill - sill.*(1.5*(h_eff/a_minor)-0.5*((h_eff/a_minor)^3));
else
cov = sill;
end
elseif strcmp(mod_type,'Exp')==1 || strcmp(mod_type,'exp') ==1
cov = sill-(sill.*(1-exp(-(3*h_eff)/a_minor)));
elseif strcmp(mod_type,'Gauss')==1 || strcmp(mod_type,'gauss') ==1
cov = sill-(sill.*(1-exp(-((3*h_eff)^2/(a_minor^2)))));
end
and
function [PropertyAvg,variance_PropertyAvg,NormVariance_PropertyAvg]=...
general_AverageProperty(blocksize_row,blocksize_col,blocksize_t,...
nUpscaledRow,nUpscaledCol,nUpscaledT,PropertyArray,omega)
% This function computes average of a property and variance of that averaged
% property using power averaging
PropertyAvg=zeros(nUpscaledRow,nUpscaledCol,nUpscaledT);
%# Average of property
for k=1:nUpscaledT,
for j=1:nUpscaledCol,
for i=1:nUpscaledRow,
sum=0;
for a=1:blocksize_row,
for b=1:blocksize_col,
for c=1:blocksize_t,
sum=sum+(PropertyArray((i-1)*blocksize_row+a,(j-1)*blocksize_col+b,(k-1)*blocksize_t+c).^omega); % add all the property values in 'blocksize_x','blocksize_y','blocksize_t' to one variable
end
end
end
PropertyAvg(i,j,k)=(sum/(blocksize_row*blocksize_col*blocksize_t)).^(1/omega); % take average of the summed property
end
end
end
%# Variance of averageed property
variance_PropertyAvg=var(reshape(PropertyAvg,...
nUpscaledRow*nUpscaledCol*nUpscaledT,1),1,1);
%# Normalized variance of averageed property
NormVariance_PropertyAvg=variance_PropertyAvg./(var(reshape(...
PropertyArray,numel(PropertyArray),1),1,1));
Question: Using Matlab, I would like to optimize covAvg.Scale2 such that it matches closely with cov.Scale1 by perturbing/varying any (or all) of the following variables
1) a_minor
2) a_major
3) theta
I am aware I can use fminsearch, however, how I am not able to perturb the variables I want to while using this fminsearch.
I won't pretend to understand everything that you are doing. But it sounds like a typical minimization problem. What you want to do is to come up with a single function that takes a_minor, a_major and theta as arguments, and returns the square of the difference between covAvg.Scale2 and cov.Scale1. Something like this:
function diff = minimize_me(a_minor, a_major, theta)
... your script goes here
diff = (covAvg.Scale2 - cov.Scale1)^2;
end
Then you need matlab to minimize this function. There's more than one option here. Since you only have three variables to minimize over, fminsearch is a good place to start. You would call it something like this:
opts = optimset('display', 'iter');
x = fminsearch( #(x) minimize_me(x(1), x(2), x(3)), [a_minor_start a_major_start theta_start], opts)
The first argument to fminsearch is the function you want to optimize. It must take a single argument: a vector of the variables that will be perturbed in order to find the minimum value. Here I use an anonymous function to extract the values from this vector and pass them into minimize_me. The second argument to fminsearch is a vector containing the values to start searching at. The third argument are options that affect the search; it's a good idea to set display to iter when you first start optimizing, so that you can get an idea of well the optimizer is converging.
If your parameters have restricted domains (e.g. they must all be positive) take a look at fminsearchbnd on the file exchange.
If I have misunderstood your problem, and this doesn't help at all, try posting code that we can run to reproduce the problem ourselves.