BIMK / PlatEMO

Evolutionary multi-objective optimization platform
1.58k stars 462 forks source link

Individuals out of range #71

Open cnelmortimer opened 3 years ago

cnelmortimer commented 3 years ago

Dear PlatEMO team,

First of all, thank you for providing such a powerful tool for free. PlatEMO seems a very useful piece of software.

I am new to MO in general and to this framework in particular. I am facing a problem of bounds with MOEAD and my custom problem. Namely, PlatEMO tries to evaluate an individual that is definitely out of range and makes my cost function to fail. This is the command that I run:

main('-algorithm', @MOEAD, '-problem', @NC_MO_GRC_BL, '-N', 300, '-evaluation', 50000, '-save', 1);

The referred problem defines its own bounds as follows in its constructor:

bounds = zeros(10, 2);
bounds(1,:) = [-0.000000001 0.000000001]; % a
bounds(2,:) = [-0.02 0.02]; % V_Peak (espike)
bounds(3,:) = [-0.06 -0.02]; % V_T (eth)
bounds(4,:) = [-0.000000001 0.000000001]; % b
bounds(5,:) = [0.0000000000001 0.000000000005]; % C_m (cm)
bounds(6,:) = [-0.08 -0.04]; % E_L (erest)
bounds(7,:) = [0.000000000001 0.00000001]; % g_L (grest)
bounds(8,:) = [0.001, 1]; % Delta_T (delta_t)
bounds(9,:) = [0.001, 1]; % T_w (tw)
bounds(10,:) = [-0.08 -0.04]; % V_r (vreset)

obj.Global.lower = bounds(:,1)'; % From column to row vector
obj.Global.upper = bounds(:,2)';

And everything starts running fine, but at certain point, it seems that PlatEMO's MOEAD generates and tries to evaluate some individuals that are out of range. I added this code to the problem file (CalObj method) to catch the error:

try
    v = NC_MO_ObjFunc(Pop(i, :), obj.pyContext); % My external cost function
catch ME
    disp('Bad individual:');
    format hex;
    disp(Pop(i, :));
    format short;
    rethrow(ME);
end
PopObj(i,:) = [v(1), v(2)];

And the individual is:

Bad individual:
  Columns 1 through 9

   be0ea6fe14510cc1   3f8f19ea69bed28d   bfae165eab635fb0   3e059d71343b3773   3d92bceaf3850f88   bfad29916ac09a9c   3dfde2294d57b250   3f8f6ea8673f4ae0   3fec637b3018db68

  Column 10

   bfb3086046e6e2e8

After converting it to decimal we have:

>> BadBoy = [ hex2dec('be0ea6fe14510cc1'), ...
    hex2dec('3f8f19ea69bed28d'), ...
    hex2dec('bfae165eab635fb0'), ...
    hex2dec('3e059d71343b3773'), ...
    hex2dec('3d92bceaf3850f88'), ...
    hex2dec('bfad29916ac09a9c'), ...
    hex2dec('3dfde2294d57b250'), ...
    hex2dec('3f8f6ea8673f4ae0'), ...
    hex2dec('3fec637b3018db68'), ...
    hex2dec('bfb3086046e6e2e8')];
>> BadBoy

BadBoy =

   1.0e+19 *

    1.3695    0.4580    1.3812    0.4469    0.4437    1.3812    0.4467    0.4580    0.4606    1.3813

As you can see, the decision vector is out of bounds (upper), and it causes my cost function to fail.

Am I missing anything? According to the PlatEMO manual on page 16:

(...) Firstly, for continuous MOPs, it may be greater than the upper bound GLOBAL.upper or less than the lower bound GLOBAL.lower, in this case it will be set to the boundary value by INDIVIDUAL class, while MOP classes need not handle this case. (...)

So my MOP class, i.e., NC_MO_GRC_BL, does not handle the out of bounds case. In fact, I have manually tried to build an individual from the previous out of bounds case, and , as expected, the INDIVIDUAL's constructor seems to filter properly:

>> my_ind = INDIVIDUAL(BadBoy);
63                  obj(1,size(Decs,1)) = INDIVIDUAL;
K>> isequal(Decs, Global.upper)

ans =

  logical

   1

Thus, I suppose that decision variables are moved far away at a different point within MOEAD.

Do you have any suggestion? Am I doing anything wrong?

Thank you very much for your help in advance

linjunhe commented 3 years ago

Have you tried to enlarge the range of decision variables before offspring generation?

For example,

properties(Access = private)
    range;
end
methods
  function obj = NC_MO_GRC_BL()
    ...
    range = [1e9,1e2,1e2,1e9,1e12,1e2,1e8,1,1,1e2]';
    bounds = zeros(10, 2);
    bounds(1,:) = [-0.000000001 0.000000001]; % a
    bounds(2,:) = [-0.02 0.02]; % V_Peak (espike)
    bounds(3,:) = [-0.06 -0.02]; % V_T (eth)
    bounds(4,:) = [-0.000000001 0.000000001]; % b
    bounds(5,:) = [0.0000000000001 0.000000000005]; % C_m (cm)
    bounds(6,:) = [-0.08 -0.04]; % E_L (erest)
    bounds(7,:) = [0.000000000001 0.00000001]; % g_L (grest)
    bounds(8,:) = [0.001, 1]; % Delta_T (delta_t)
    bounds(9,:) = [0.001, 1]; % T_w (tw)
    bounds(10,:) = [-0.08 -0.04]; % V_r (vreset)

    bounds = bounds.*range;
    obj.range = range;
    obj.Global.lower = bounds(:,1)'; % From column to row vector
    obj.Global.upper = bounds(:,2)';
    obj.Global.encoding = 'real';
  end

  function PopObj = CalObj(obj,PopDec)
    PopDec = PopDec./obj.range';
    ...
  end
...
end
cnelmortimer commented 3 years ago

Dear @linjunhe,

Thank you very much for your interest. Answering to your question: No, I have not tried to modify the ranges. Is it required? In fact, I could try to scale my decision variables to be in [0-1] too. It seems to be more or less an equivalent strategy.

However, I fear that this unexpected behavior might be warning about some kind of bug. I mean, as far as I know, decision ranges have no special requirements in terms of value (do they?), and individuals are expected to respect them (that is the reason why the manual says that MOP classes do not have to handle this situation). Thus, I do not know at what part of the procedure the values of variables soar that much.

I will try to further study the process tomorrow, but have you ever faced this kind of situation or your variables are usually in less different ranges? Could it be a bug?

Thank you very much again and kind regards!

linjunhe commented 3 years ago

Dear @linjunhe,

Thank you very much for your interest. Answering to your question: No, I have not tried to modify the ranges. Is it required? In fact, I could try to scale my decision variables to be in [0-1] too. It seems to be more or less an equivalent strategy.

However, I fear that this unexpected behavior might be warning about some kind of bug. I mean, as far as I know, decision ranges have no special requirements in terms of value (do they?), and individuals are expected to respect them (that is the reason why the manual says that MOP classes do not have to handle this situation). Thus, I do not know at what part of the procedure the values of variables soar that much.

I will try to further study the process tomorrow, but have you ever faced this kind of situation or your variables are usually in less different ranges? Could it be a bug?

Thank you very much again and kind regards!

My first thought is that the values of decision variables might be too small (e.g., 1e-12), which can cause floating point issues.

In any case, I think you can address this issue by setting out-of-bound values back to bounds.

For example, for polynomial mutation in pymoo and jmetal, back-to-bound operation is applied after both crossover and mutation.

Similarly, you can add one line in GA.m and GAhalf.m as follows.

switch Global.encoding
  case 'binary'
    ...
  case 'permutation'
    ...
  otherwise
    ...
    Offspring = min(max(Offspring,Lower),Upper);
end

``

cnelmortimer commented 3 years ago

Dear @linjunhe,

I am still studying the problems with ranges... One of the executions that I have done finished successfully even with my ranges after 50 000 function evaluations, and all the individuals in the final population were inside the bounds, so I am looking at these aspects carefully.

By the way, are PlatEMO algorithms expected to minimize or to maximize the objectives? Or does it depend on each implementation? I cannot find explicit information about this point (as Matlab's "fmincon" is expected to "minimize" for instance).

Kind regards!

cnelmortimer commented 3 years ago

Dear @linjunhe,

It seems that the problem is in my cost function. The "BadBoy" individual that I included in my first comment is within the desired range. I thought that it wasn't because I used "hex2dec" instead of "hex2num", so the conversion was wrong and extremely out of range. I apologize for the confusion. Shall I close this issue?

In any case, I still have a question as a beginner with the PlatEMO framework: Are the methods/algorithms for minimization or for maximization by default? Or are there no requirements for the people that implement them? I cannot see any explicit explanation about that in the manual, and the simplest way to externally check it would imply generating a Pareto front with every optimizer.

Thank you in advance and sorry again for the confusion with the conversion.

linjunhe commented 3 years ago

Dear @linjunhe,

It seems that the problem is in my cost function. The "BadBoy" individual that I included in my first comment is within the desired range. I thought that it wasn't because I used "hex2dec" instead of "hex2num", so the conversion was wrong and extremely out of range. I apologize for the confusion. Shall I close this issue?

In any case, I still have a question as a beginner with the PlatEMO framework: Are the methods/algorithms for minimization or for maximization by default? Or are there no requirements for the people that implement them? I cannot see any explicit explanation about that in the manual, and the simplest way to externally check it would imply generating a Pareto front with every optimizer.

Thank you in advance and sorry again for the confusion with the conversion.

Sorry I did not check github regularly. I am glad to hear that the issue has been addressed. In my opinion, the PlatEMO assumes all the objectives to be minimized.

cnelmortimer commented 3 years ago

Dear @linjunhe ,

Thank you very much for the information!

Kind regards