I want to use a Genetic Algorithm to predict the thickness value. Currently, my code runs and produces results where it hits upper bound and lower bound.

clear; clc;
%% 1) Experimental Data
time = [7200 14400 21600];
temp = [1123 1173 1223];
thick_exp = [16.83 24.51 31.09;
26.28 37.98 49.91;
41.56 59.12 69.01];
R = 8.314; % Gas constant (J/mol·K)
%% 2) Convert matrix to vector
[temp_grid,time_grid] = ndgrid(temp,time);
time_vector = time_grid(:);
temp_vector = temp_grid(:);
thick_exp_vector = thick_exp(:);
%% 3) Define Kinetic Model
% thickness = k * sqrt(t)
% k = k0 * exp(-Q/RT)
kineticModel = @(params,t,T) ...
sqrt( t .* params(1) .* exp(-params(2).*1000 ./ (8.314 .* T)) );
%% 4) Define RMSE Fitness Function
fitnessFcn = @(params) ...
sqrt(mean((thick_exp_vector - kineticModel(params,time_vector,temp_vector)).^2));
%% 5) Define Bounds [k0 Q]
lb = [1e2 50000];
ub = [1e8 300000];
%% 6) GA Options
options = optimoptions('ga',...
'PopulationSize',100,...
'MaxGenerations',200,...
'Display','iter');
%% 7) Run Genetic Algorithm
[nParams,fval] = ga(fitnessFcn,2,[],[],[],[],lb,ub,[],options);
Single objective optimization: 2 Variables Options: CreationFcn: @gacreationuniform CrossoverFcn: @crossoverscattered SelectionFcn: @selectionstochunif MutationFcn: @mutationadaptfeasible Best Mean Stall Generation Func-count f(x) f(x) Generations 1 200 42.76 42.76 0 2 295 42.76 42.76 1 3 390 42.76 42.76 2 4 485 42.76 42.76 3 5 580 42.76 42.76 4 6 675 42.76 42.76 5 7 770 42.76 42.76 6 8 865 42.76 42.76 7 9 960 42.76 42.76 8 10 1055 42.76 42.76 9 11 1150 42.76 42.76 10 12 1245 42.76 42.76 11 13 1340 42.76 42.76 12 14 1435 42.76 42.76 13 15 1530 42.76 42.76 14 16 1625 42.76 42.76 15 17 1720 42.76 42.76 16 18 1815 42.76 42.76 17 19 1910 42.76 42.76 18 20 2005 42.76 42.76 19 21 2100 42.76 42.76 20 22 2195 42.76 42.76 21 23 2290 42.76 42.76 22 24 2385 42.76 42.76 23 25 2480 42.76 42.76 24 26 2575 42.76 42.76 25 27 2670 42.76 42.76 26 28 2765 42.76 42.76 27 29 2860 42.76 42.76 28 30 2955 42.76 42.76 29 Best Mean Stall Generation Func-count f(x) f(x) Generations 31 3050 42.76 42.76 30 32 3145 42.76 42.76 31 33 3240 42.76 42.76 32 34 3335 42.76 42.76 33 35 3430 42.76 42.76 34 36 3525 42.76 42.76 35 37 3620 42.76 42.76 36 38 3715 42.76 42.76 37 39 3810 42.76 42.76 38 40 3905 42.76 42.76 39 41 4000 42.76 42.76 40 42 4095 42.76 42.76 41 43 4190 42.76 42.76 42 44 4285 42.76 42.76 43 45 4380 42.76 42.76 44 46 4475 42.76 42.76 45 47 4570 42.76 42.76 46 48 4665 42.76 42.76 47 49 4760 42.76 42.76 48 50 4855 42.76 42.76 49 51 4950 42.76 42.76 50 ga stopped because the average change in the fitness value is less than options.FunctionTolerance.
thick_pred = kineticModel(nParams,time_vector,temp_vector);
MAPE = mean(abs((thick_exp_vector - thick_pred) ./ thick_exp_vector)) * 100;
%% 8) Display Results
disp(['Q = ',num2str(nParams(2)),' J/mol'])
Q = 50000 J/mol
disp(['k0 = ',num2str(nParams(1))])
k0 = 100
disp(['RMSE = ',num2str(fval)])
RMSE = 42.7649
disp(['MAPE = ',num2str(MAPE),' %'])
MAPE = 100 %

Answers (1)

The reason is that the optimal values for the 'k0' and 'Q' parameter values are far outside the bounds that you set for them. Unconstrained, 'k0' is a bit less than 80, and 'Q' is a bit less than 400.
I am not certain what you are doing, so I cannot comment on the accuracy of your 'kineticModel' function. However that is where I would look in order to understand the reason the optimal estimated parameters are far outside the bounds that you set for them.
Example --
clear; clc;
%% 1) Experimental Data
time = [7200 14400 21600];
temp = [1123 1173 1223];
thick_exp = [16.83 24.51 31.09;
26.28 37.98 49.91;
41.56 59.12 69.01];
R = 8.314; % Gas constant (J/mol·K)
%% 2) Convert matrix to vector
[temp_grid,time_grid] = ndgrid(temp,time);
time_vector = time_grid(:);
temp_vector = temp_grid(:);
thick_exp_vector = thick_exp(:);
%% 3) Define Kinetic Model
% thickness = k * sqrt(t)
% k = k0 * exp(-Q/RT)
kineticModel = @(params,t,T) ...
sqrt( t .* params(1) .* exp(-params(2).*1000 ./ (8.314 .* T)) );
%% 4) Define RMSE Fitness Function
fitnessFcn = @(params) ...
sqrt(mean((thick_exp_vector - kineticModel(params,time_vector,temp_vector)).^2));
%% 5) Define Bounds [k0 Q]
lb = [1e2 50000];
ub = [1e8 300000];
lb = [];
ub = [];
%% 6) GA Options
options = optimoptions('ga',...
'PopulationSize',100,...
'MaxGenerations',200,...
'Display','iter');
%% 7) Run Genetic Algorithm
[nParams,fval] = ga(fitnessFcn,2,[],[],[],[],lb,ub,[],options);
Single objective optimization: 2 Variables Options: CreationFcn: @gacreationuniform CrossoverFcn: @crossoverscattered SelectionFcn: @selectionstochunif MutationFcn: @mutationgaussian Best Mean Stall Generation Func-count f(x) f(x) Generations 1 200 14.07 156.7 0 2 295 10.74 104.4 0 3 390 10.74 94.5 1 4 485 10.74 129 2 5 580 10.52 67.99 0 6 675 9.926 89.29 0 7 770 9.926 96.72 1 8 865 9.908 62.39 0 9 960 9.806 44.84 0 10 1055 9.791 48.85 0 11 1150 9.791 23.5 1 12 1245 9.767 25.91 0 13 1340 9.767 26.63 1 14 1435 9.767 23.21 2 15 1530 9.767 19.18 3 16 1625 9.767 24.26 4 17 1720 9.767 26.56 5 18 1815 9.767 21.31 6 19 1910 9.297 19.13 0 20 2005 9.297 25.05 1 21 2100 9.297 29.21 2 22 2195 9.297 35.17 3 23 2290 9.297 27.73 4 24 2385 9.025 23.29 0 25 2480 9.025 22.72 1 26 2575 9.025 27.08 2 27 2670 9.025 24.73 3 28 2765 9.025 21.41 4 29 2860 9.025 18.7 5 30 2955 8.996 18.01 0 Best Mean Stall Generation Func-count f(x) f(x) Generations 31 3050 8.957 17.91 0 32 3145 8.957 18.16 1 33 3240 8.957 15.15 2 34 3335 8.957 18.7 0 35 3430 8.957 21.13 1 36 3525 8.957 20.42 2 37 3620 8.906 19.32 0 38 3715 8.893 20.28 0 39 3810 8.893 33.31 1 40 3905 8.844 19.38 0 41 4000 8.844 15.79 1 42 4095 8.844 20.79 2 43 4190 8.844 18.77 3 44 4285 8.844 15.44 4 45 4380 8.844 17.58 5 46 4475 8.736 15.17 0 47 4570 8.736 20.08 1 48 4665 8.615 18.29 0 49 4760 8.615 17.65 1 50 4855 8.615 20.69 2 51 4950 8.562 22.22 0 52 5045 8.562 28.51 1 53 5140 8.549 30.08 0 54 5235 8.549 25.28 1 55 5330 8.488 18.09 0 56 5425 8.488 19.24 1 57 5520 8.481 18.87 0 58 5615 8.481 20.75 1 59 5710 8.48 20.06 0 60 5805 8.48 21.11 1 Best Mean Stall Generation Func-count f(x) f(x) Generations 61 5900 8.48 18.86 2 62 5995 8.48 14.19 3 63 6090 8.48 15.17 4 64 6185 8.438 13.97 0 65 6280 8.438 13.29 1 66 6375 8.43 17.63 0 67 6470 8.43 15.74 1 68 6565 8.407 12.39 0 69 6660 8.342 12.8 0 70 6755 8.342 13.5 1 71 6850 8.342 20.65 2 72 6945 8.339 15.66 0 73 7040 8.339 15.84 1 74 7135 8.339 13.03 2 75 7230 8.339 13.98 3 76 7325 8.339 13.04 4 77 7420 8.339 12.38 5 78 7515 8.309 12.33 0 79 7610 8.309 12.53 1 80 7705 8.309 12.71 2 81 7800 8.309 13.2 3 82 7895 8.309 12.56 4 83 7990 8.309 13.5 5 84 8085 8.309 16.09 6 85 8180 8.304 14.83 0 86 8275 8.276 15.31 0 87 8370 8.263 16.64 0 88 8465 8.22 12.14 0 89 8560 8.22 11.84 1 90 8655 8.22 11.24 2 Best Mean Stall Generation Func-count f(x) f(x) Generations 91 8750 8.22 12.43 3 92 8845 8.218 14.84 0 93 8940 8.217 12.62 0 94 9035 8.217 12.22 1 95 9130 8.217 13.43 0 96 9225 8.217 12.16 1 97 9320 8.217 13.79 2 98 9415 8.171 12.65 0 99 9510 8.171 15.65 1 100 9605 8.171 14.59 2 101 9700 8.17 14.44 0 102 9795 8.17 14.52 1 103 9890 8.168 13.44 0 104 9985 8.152 13.17 0 105 10080 8.152 12.61 0 106 10175 8.152 13.73 1 107 10270 8.152 14.79 2 108 10365 8.152 11.2 3 109 10460 8.15 12.29 0 110 10555 8.146 11.9 0 111 10650 8.146 12.07 1 112 10745 8.146 11.85 2 113 10840 8.146 14.05 3 114 10935 8.146 12.07 4 115 11030 8.132 12.88 0 116 11125 8.132 11.72 1 117 11220 8.132 11.54 2 118 11315 8.132 10.87 3 119 11410 8.131 11.82 0 120 11505 8.13 11.27 0 Best Mean Stall Generation Func-count f(x) f(x) Generations 121 11600 8.053 11.72 0 122 11695 8.053 11.11 1 123 11790 8.053 10.79 2 124 11885 8.053 11.13 3 125 11980 8.053 10.66 4 126 12075 8.024 10.24 0 127 12170 8.024 10.34 1 128 12265 8.024 10.41 2 129 12360 8.024 11.35 3 130 12455 8.004 11.15 0 131 12550 7.997 11.7 0 132 12645 7.997 11.73 1 133 12740 7.997 11.51 2 134 12835 7.997 10.36 3 135 12930 7.997 10.38 4 136 13025 7.997 10.15 5 137 13120 7.997 10.46 6 138 13215 7.997 10.81 7 139 13310 7.997 10.72 8 140 13405 7.959 11.1 0 141 13500 7.959 10.59 1 142 13595 7.959 10.51 2 143 13690 7.959 10.36 3 144 13785 7.958 10.49 0 145 13880 7.957 10.8 0 146 13975 7.957 11.04 1 147 14070 7.956 10.4 0 148 14165 7.954 9.872 0 149 14260 7.954 9.718 1 150 14355 7.953 9.984 0 Best Mean Stall Generation Func-count f(x) f(x) Generations 151 14450 7.953 9.994 1 152 14545 7.953 9.339 2 153 14640 7.953 9.656 3 154 14735 7.915 9.325 0 155 14830 7.915 9.813 1 156 14925 7.915 9.717 2 157 15020 7.915 9.765 3 158 15115 7.914 9.305 0 159 15210 7.906 8.667 0 160 15305 7.905 8.862 0 161 15400 7.903 9.023 0 162 15495 7.903 8.826 1 163 15590 7.903 9.219 2 164 15685 7.903 9.257 0 165 15780 7.892 8.922 0 166 15875 7.891 8.8 0 167 15970 7.879 8.847 0 168 16065 7.879 8.906 1 169 16160 7.876 9.178 0 170 16255 7.867 8.323 0 171 16350 7.867 8.495 1 172 16445 7.859 8.496 0 173 16540 7.859 8.397 1 174 16635 7.859 8.264 2 175 16730 7.859 8.557 3 176 16825 7.856 8.358 0 177 16920 7.856 8.288 1 178 17015 7.849 8.275 0 179 17110 7.849 8.292 1 180 17205 7.849 8.447 0 Best Mean Stall Generation Func-count f(x) f(x) Generations 181 17300 7.849 8.236 1 182 17395 7.848 8.225 0 183 17490 7.846 8.2 0 184 17585 7.846 8.172 1 185 17680 7.844 8.161 0 186 17775 7.844 8.16 1 187 17870 7.843 8.069 0 188 17965 7.842 8.056 0 189 18060 7.842 8.081 1 190 18155 7.839 8.013 0 191 18250 7.839 7.942 1 192 18345 7.839 7.942 2 193 18440 7.838 7.936 0 194 18535 7.836 7.9 0 195 18630 7.836 7.896 1 196 18725 7.836 7.886 2 197 18820 7.836 7.88 0 198 18915 7.836 7.87 0 199 19010 7.836 7.864 0 200 19105 7.836 7.841 0 ga stopped because it exceeded options.MaxGenerations.
thick_pred = kineticModel(nParams,time_vector,temp_vector);
MAPE = mean(abs((thick_exp_vector - thick_pred) ./ thick_exp_vector)) * 100;
thick_pred_mtx = reshape(thick_pred,numel(time), numel(temp)).'; % Used in 'surf' Plot
%% 8) Display Results
disp(['Q = ',num2str(nParams(2)),' J/mol'])
Q = 78.3864 J/mol
disp(['k0 = ',num2str(nParams(1))])
k0 = 364.0751
disp(['RMSE = ',num2str(fval)])
RMSE = 7.8356
disp(['MAPE = ',num2str(MAPE),' %'])
MAPE = 21.107 %
figure
surf(time, temp, thick_exp, DisplayName='Data')
hold on
surf(time_grid, temp_grid, thick_pred_mtx, FaceAlpha=0.5, EdgeColor='r', DisplayName='Function Fit')
hold off
xlabel('Time')
ylabel('Temperature (K)')
zlabel('Thickness')
title('Unconstrained Parameter Fit')
legend(Location='best')
view(40,30)
.
EDIT -- (13 Feb 2026 at 18:25)
The transposed 'thick_pred_mtx' seems to more closely approximate the data than if it is not transposed.
.

Asked:

on 13 Feb 2026

Edited:

on 13 Feb 2026

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!