1 ## Copyright (C) 2008, 2010, 2012 Luca Favatella <slackydeb@gmail.com>
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published by
5 ## the Free Software Foundation; either version 2 of the License, or
6 ## (at your option) any later version.
8 ## This program is distributed in the hope that it will be useful,
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 ## GNU General Public License for more details.
13 ## You should have received a copy of the GNU General Public License
14 ## along with this program; If not, see <http://www.gnu.org/licenses/>.
17 ## @deftypefn{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars})
18 ## @deftypefnx{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars}, @var{A}, @var{b})
19 ## @deftypefnx{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars}, @var{A}, @var{b}, @var{Aeq}, @var{beq})
20 ## @deftypefnx{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars}, @var{A}, @var{b}, @var{Aeq}, @var{beq}, @var{LB}, @var{UB})
21 ## @deftypefnx{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars}, @var{A}, @var{b}, @var{Aeq}, @var{beq}, @var{LB}, @var{UB}, @var{nonlcon})
22 ## @deftypefnx{Function File} {@var{x} =} ga (@var{fitnessfcn}, @var{nvars}, @var{A}, @var{b}, @var{Aeq}, @var{beq}, @var{LB}, @var{UB}, @var{nonlcon}, @var{options})
23 ## @deftypefnx{Function File} {@var{x} =} ga (@var{problem})
24 ## @deftypefnx{Function File} {[@var{x}, @var{fval}] =} ga (@dots{})
25 ## @deftypefnx{Function File} {[@var{x}, @var{fval}, @var{exitflag}] =} ga (@dots{})
26 ## @deftypefnx{Function File} {[@var{x}, @var{fval}, @var{exitflag}, @var{output}] =} ga (@dots{})
27 ## @deftypefnx{Function File} {[@var{x}, @var{fval}, @var{exitflag}, @var{output}, @var{population}] =} ga (@dots{})
28 ## @deftypefnx{Function File} {[@var{x}, @var{fval}, @var{exitflag}, @var{output}, @var{population}, @var{scores}] =} ga (@dots{})
29 ## Find minimum of function using genetic algorithm.
34 ## The objective function to minimize. It accepts a vector @var{x} of
35 ## size 1-by-@var{nvars}, and returns a scalar evaluated at @var{x}.
37 ## The dimension (number of design variables) of @var{fitnessfcn}.
39 ## The structure of the optimization parameters; can be created using
40 ## the @code{gaoptimset} function. If not specified, @code{ga} minimizes
41 ## with the default optimization parameters.
43 ## A structure containing the following fields:
45 ## @item @code{fitnessfcn}
53 ## @item @code{nonlcon}
54 ## @item @code{randstate}
55 ## @item @code{randnstate}
56 ## @item @code{solver}
57 ## @item @code{options}
64 ## The local unconstrained found minimum to the objective function,
67 ## The value of the fitness function at @var{x}.
70 ## @seealso{gaoptimset}
73 ## Author: Luca Favatella <slackydeb@gmail.com>
76 function [x fval exitflag output population scores] = \
77 ga (fitnessfcn_or_problem,
83 options = gaoptimset ())
93 ## retrieve the problem structure
95 problem = fitnessfcn_or_problem;
97 problem.fitnessfcn = fitnessfcn_or_problem;
98 problem.nvars = nvars;
105 problem.nonlcon = nonlcon;
106 problem.randstate = rand ("state");
107 problem.randnstate = randn ("state");
108 problem.solver = "ga";
109 problem.options = options;
112 ## call the function that manages the problem structure
113 [x fval exitflag output population scores] = __ga_problem__ (problem);
118 ## number of input arguments
120 %! f = @rastriginsfcn;
124 %!error x = ga (f, nvars, [])
125 %!error x = ga (f, nvars, [], [], [])
126 %!error x = ga (f, nvars, [], [], [], [], [])
127 %!error x = ga (f, nvars, [], [], [], [], [], [], @(x) [[], []], gaoptimset (), [])
129 ## number of output arguments
133 %!function f = ff (nvars)
134 %! f = @(x) sum (x(:, 1:nvars) .** 2, 2);
135 %!error x = ga (ff (3), 2);
137 # TODO: test that each field in the user-specified "problem" structure is checked
140 ## flawless execution with right arguments
142 %! f = @rastriginsfcn;
144 %!function [C, Ceq] = nonlcon (x)
147 %!test x = ga (f, nvars);
148 %!test x = ga (f, nvars, [], []);
149 %!test x = ga (f, nvars, ones (3, nvars), ones (3, 1));
150 %!test x = ga (f, nvars, [], [], [], []);
151 %!test x = ga (f, nvars, [], [], ones (4, nvars), ones (4, 1));
152 %!test x = ga (f, nvars, [], [], [], [], [], []);
153 %!test x = ga (f, nvars, [], [], [], [], - Inf (1, nvars), Inf (1, nvars));
154 %!test x = ga (f, nvars, [], [], [], [], - ones (1, nvars), ones (1, nvars));
155 %!test x = ga (f, nvars, [], [], [], [], [], [], @(x) [[], []]);
156 %!test x = ga (f, nvars, [], [], [], [], [], [], @nonlcon);
157 %!test x = ga (f, nvars, [], [], [], [], [], [], @(x) [[], []], gaoptimset ());
158 %!test # TODO: convert to error after implementing private ga-specific createOptimProblem. All fields in the user-specified structure should be checked
159 %! problem = struct ("fitnessfcn", @rastriginsfcn,
161 %! "options", gaoptimset ());
164 ## flawless execution with any nvars
165 %!function f = ff (nvars)
166 %! f = @(x) sum (x(:, 1:nvars) .** 2, 2);
169 %! x = ga (ff (nvars), nvars);
172 %! x = ga (ff (nvars), nvars);
175 %! x = ga (ff (nvars), nvars);
177 ## flawless execution with any supported optimization parameter
178 ## different from the default value
179 %!shared f, nvars, default_options
180 %! f = @rastriginsfcn;
182 %! default_options = gaoptimset ();
183 %!function [C, Ceq] = nonlcon (x)
187 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, default_options);
188 %!test # TODO: use non-default value
189 %! options = gaoptimset ("CreationFcn", @gacreationuniform);
190 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
191 %!test # TODO: use non-default value
192 %! options = gaoptimset ("CrossoverFcn", @crossoverscattered);
193 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
195 %! options = gaoptimset ("CrossoverFraction", rand);
196 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
198 %! ps = getfield (default_options, "PopulationSize");
199 %! options = gaoptimset ("EliteCount", randi ([0, ps]));
200 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
202 %! options = gaoptimset ("FitnessLimit", 1e-7);
203 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
204 %!test # TODO: use non-default value
205 %! options = gaoptimset ("FitnessScalingFcn", @fitscalingrank);
206 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
208 %! g = getfield (default_options, "Generations");
209 %! options = gaoptimset ("Generations", g + 1);
210 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
212 %! ps = getfield (default_options, "PopulationSize");
213 %! ## Initial population can be partial
214 %! options_w_full_ip = \
215 %! gaoptimset ("InitialPopulation", rand (ps, nvars));
216 %! partial_ip = randi ([0, ps - 1]);
217 %! options_w_partial_ip = \
218 %! gaoptimset ("InitialPopulation", rand (partial_ip, nvars));
219 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options_w_full_ip);
220 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options_w_partial_ip);
222 %! ps = getfield (default_options, "PopulationSize");
223 %! ## Initial scores needs initial population
225 %! options_w_full_ip_full_is = \
226 %! gaoptimset ("InitialPopulation", rand (ps, nvars),
227 %! "InitialScores", rand (ps, 1 ));
228 %! partial_ip = randi ([2, ps - 1]);
229 %! options_w_partial_ip_full_is = \
230 %! gaoptimset ("InitialPopulation", rand (partial_ip, nvars),
231 %! "InitialScores", rand (partial_ip, 1 ));
233 %! ## Initial scores can be partial
234 %! partial_is_when_full_ip = randi ([1, ps - 1]);
235 %! partial_is_when_partial_ip = randi ([1, partial_ip - 1]);
236 %! options_w_full_ip_partial_is = \
237 %! gaoptimset ("InitialPopulation", rand (ps, nvars),
238 %! "InitialScores", rand (partial_is_when_full_ip, 1 ));
239 %! options_w_partial_ip_partial_is = \
240 %! gaoptimset ("InitialPopulation", rand (partial_ip, nvars),
241 %! "InitialScores", rand (partial_is_when_partial_ip, 1 ));
243 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon,
244 %! options_w_full_ip_full_is);
245 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon,
246 %! options_w_partial_ip_full_is);
247 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon,
248 %! options_w_full_ip_partial_is);
249 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon,
250 %! options_w_partial_ip_partial_is);
251 %!test # TODO: use non-default value
252 %! options = gaoptimset ("MutationFcn", {@mutationgaussian, 1, 1});
253 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
255 %! options = gaoptimset ("PopInitRange", [-2; 2]);
256 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
258 %! options = gaoptimset ("PopulationSize", 200);
259 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
260 %!test # TODO: use non-default value
261 %! options = gaoptimset ("SelectionFcn", @selectionstochunif);
262 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
263 %!test # TODO: use non-default value
264 %! options = gaoptimset ("TimeLimit", Inf);
265 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
266 %!error # TODO: this should become test
267 %! options = gaoptimset ("UseParallel", "always");
268 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
270 %! options = gaoptimset ("Vectorized", "on");
271 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
274 ## error with conflicting optimization parameters: population size et al.
276 %! f = @rastriginsfcn;
278 %!function [C, Ceq] = nonlcon (x)
281 %!error # Elite count cannot be greater than the population size
283 %! bad_options = gaoptimset ("PopulationSize", ps,
284 %! "EliteCount", ps + 1);
285 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
286 %!error # The number of individuals in the initial population cannot be greater of the population size
288 %! bad_options = gaoptimset ("PopulationSize", ps,
289 %! "InitialPopulation", zeros (ps + 1, nvars));
290 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
291 %!error # Initial scores cannot be specified without specifying the initial population too
292 %! bad_options = gaoptimset ("InitialScores", zeros (3, 1));
293 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
294 %!error # The number of initial scores specified cannot be greater of the number of individuals in the initial population
296 %! bad_options = gaoptimset ("InitialPopulation", zeros (ip, nvars),
297 %! "InitialScores", zeros (ip + 1, 1));
298 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
300 ## error with vectorized evaluation of objective function. Vectorized
301 ## objective functions are better because can be evaluated both as
302 ## serial and vectorized.
305 %!function [C, Ceq] = nonlcon (x)
308 %!function f = ff (nvars)
309 %! f = @(x) sum (x(:, 1:nvars) .** 2, 2);
310 %!function f_not_vectorized = ff_not_vectorized (nvars)
311 %! f_not_vectorized = @(x) sum (x(1:nvars) .** 2);
312 %!test # A non-vectorized objective function works when no vectorization is required
313 %! f = ff_not_vectorized (nvars);
314 %! options = gaoptimset ("Vectorized", "off");
315 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
316 %!error # A non-vectorized objective function does not work when vectorization is required
317 %! f = ff_not_vectorized (nvars);
318 %! options = gaoptimset ("Vectorized", "on");
319 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
320 %!test # A vectorized objective function works when no vectorization is required
322 %! options = gaoptimset ("Vectorized", "off");
323 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
324 %!test # A vectorized objective function works when vectorization is required
326 %! options = gaoptimset ("Vectorized", "on");
327 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
329 ## error with conflicting optimization parameters: parallel and
330 ## vectorized evaluation of objective function
332 %! f = @rastriginsfcn;
334 %!function [C, Ceq] = nonlcon (x)
338 %! options = gaoptimset ("UseParallel", "never",
339 %! "Vectorized", "off");
340 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
341 %!error # TODO: this should become test
342 %! options = gaoptimset ("UseParallel", "always",
343 %! "Vectorized", "off");
344 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
346 %! bad_options = gaoptimset ("UseParallel", "garbage",
347 %! "Vectorized", "off");
348 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
350 %! options = gaoptimset ("UseParallel", "never",
351 %! "Vectorized", "on");
352 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, options);
354 %! bad_options = gaoptimset ("UseParallel", "always",
355 %! "Vectorized", "on");
356 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
358 %! bad_options = gaoptimset ("UseParallel", "garbage",
359 %! "Vectorized", "on");
360 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
362 %! bad_options = gaoptimset ("UseParallel", "never",
363 %! "Vectorized", "garbage");
364 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
366 %! bad_options = gaoptimset ("UseParallel", "always",
367 %! "Vectorized", "garbage");
368 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);
370 %! bad_options = gaoptimset ("UseParallel", "garbage",
371 %! "Vectorized", "garbage");
372 %! x = ga (f, nvars, [], [], [], [], [], [], @nonlcon, bad_options);