Why do I get Empty Plots during Optimization?
    3 views (last 30 days)
  
       Show older comments
    
Inspired by custom plotting given here, for one-dimensional design variable case, I would like to generalize it to n-dimensional case. I wrote the following code to achieve that:
function state = gaPlotRangeND(options, state, flag)
    %   gaPlotRangeND Plots the mean and the range of the population for n-dimensions.
    %   STATE = gaPlotRangeND(OPTIONS, STATE, FLAG) plots the mean and the range
    %   (highest and the lowest) of individuals for each variable.  
    generation = state.Generation;
    population = state.Population;
    numVars = size(population, 2);
    M = mean(population);
    L = M - min(population);
    U = max(population) - M;
    switch flag
        case 'init'
            for i = 1:numVars
                subplot(numVars, 1, i);
                set(gca, 'xlim', [1, options.MaxGenerations + 1]);
                plotRange = errorbar(generation, M(:, i), L(:, i), U(:, i));
                set(plotRange, 'Tag', ['Var_' num2str(i)]);
                title(['Range of Population, Mean for Variable ' num2str(i)], 'interp', 'none')
                xlabel('Generation', 'interp', 'none')
            end
        case 'iter'
            for i = 1:numVars
                subplot(numVars, 1, i);
                plotRange = findobj(get(gca, 'Children'), 'Tag', ['Var_' num2str(i)]);
                newX = [get(plotRange, 'Xdata'), generation];
                newY = [get(plotRange, 'Ydata'), M(:, i)];
                newL = [get(plotRange, 'Ldata'), L(:, i)];
                newU = [get(plotRange, 'Udata'), U(:, i)];       
                set(plotRange, 'Xdata', newX, 'Ydata', newY, 'Ldata', newL, 'Udata', newU);
            end
    end
end
When I run it for a simple two-dimensional test problem defined below, it does not work. It just outputs two empty subplots on top of each other during execution of genetic algorithm, and ends with a single empty plot. It is supposed to plot mean and range of population at each iteration for each variable, which is two in this case.
function y = booth_func(x)
    y = (x(1) + 2 * x(2) - 7) ^ 2 + (2 * x(1) + x(2) - 5) ^ 2;
end
options = optimoptions('ga', 'PlotFcn', @gaPlotRangeND);
[x, fval] = ga(@booth_func, 2, [], [], [], [], [], [], [], options);
How can I solve this issue? What do I miss here?
3 Comments
  dpb
      
      
 on 11 Aug 2024
				
      Edited: dpb
      
      
 on 11 Aug 2024
  
			This is probably going to be complicated, but...in
case 'init' 
  for i = 1:numVars
    subplot(numVars, 1, i);
    set(gca, 'xlim', [1, options.MaxGenerations + 1]);
    ...
You are creating multiple subplot axes and you have not saved the handle to any of them to later be able to address which of them you want...and, using gca simply will return whatever happens to be the current axes at the time it is called which will always be the last one referenced (which might be any one if user clicks on one during code execution).
At a barest minimum you'll need an array of axes handles to the various suplot axes objects and have to address each one in turn inside the iteration case code.
Not having the toolbox, no way can try anything specific here, but there's at least a starting point to try to work out the issues...
Accepted Answer
  Voss
      
      
 on 14 Aug 2024
        
      Moved: Walter Roberson
      
      
 on 14 Aug 2024
  
      Specifying the OutputFcn rather than the PlotFcn seems to provide something like what was intended.
options = optimoptions('ga', 'OutputFcn', @gaPlotRangeND);
[x, fval] = ga(@booth_func, 2, [], [], [], [], [], [], [], options)
function y = booth_func(x)
    y = (x(1) + 2 * x(2) - 7) ^ 2 + (2 * x(1) + x(2) - 5) ^ 2;
end
function [state,options,optchanged] = gaPlotRangeND(options, state, flag)
% gaPlotRangeND Plots the mean and the range of the population for n-dimensions.
% [STATE,OPTIONS,OPTCHANGED] = gaPlotRangeND(OPTIONS, STATE, FLAG) plots the 
% mean and the range (highest and the lowest) of individuals for each variable.  
  generation = state.Generation;
  population = state.Population;
  numVars = size(population, 2);
  M = mean(population);
  L = M - min(population);
  U = max(population) - M;
  persistent hAx hEB
  switch flag
    case 'init'
      f = figure();
      hAx=gobjects(size(M));                                        % prealloate for axes handles
      hEB=gobjects(size(M));                                        % and for errorbars, too...
      for i = 1:numVars
        hAx(i)=subplot(numVars, 1, i, 'Parent', f);                 % create subplot, save handle to each
        hEB(i)=errorbar(generation, M(:, i), L(:, i), U(:, i));     % save handle to ith EB
        xlim(hAx(i),[1 options.MaxGenerations+1]);
        title(hAx(i),['Range of Population, Mean for Variable ' num2str(i)])
        xlabel(hAx(i),'Generation')
      end
    case 'iter'
      for i = 1:numVars
        newX=[hEB(i).XData, generation];                            % return existing data
        newY=[hEB(i).XData, M(:, i)];
        newL=[hEB(i).YNegativeDelta,L(:, i)];                        % must use correct field names...
        newU=[hEB(i).YPositiveDelta,U(:, i)];       
        set(hEB(i),'Xdata',newX,'Ydata',newY,'YNegativeDelta',newL,'YPositiveDelta',newU);
      end
  end
  optchanged = false;
end
3 Comments
More Answers (1)
  dpb
      
      
 on 11 Aug 2024
        
      Edited: dpb
      
      
 on 12 Aug 2024
  
      Per the above comments, at least a start would be something more like...
function state = gaPlotRangeND(options, state, flag)
    %   gaPlotRangeND Plots the mean and the range of the population for n-dimensions.
    %   STATE = gaPlotRangeND(OPTIONS, STATE, FLAG) plots the mean and the range
    %   (highest and the lowest) of individuals for each variable.  
    generation = state.Generation;
    population = state.Population;
    numVars = size(population, 2);
    M = mean(population);
    L = M - min(population);
    U = max(population) - M;
    switch flag
        case 'init'
          hAx=gobjects(size(M));                                        % prealloate for axes handles
          hEB=gobjects(size(M));                                        % and for errorbars, too...
          for i = 1:numVars
            hAx(i)=subplot(numVars, 1, i);                              % create subplot, save handle to each
            set(hAx(i), 'xlim', [1, options.MaxGenerations+1]);
            hEB(i)=errorbar(generation, M(:, i), L(:, i), U(:, i));     % save handle to ith EB
            title(hAx(i),['Range of Population, Mean for Variable ' num2str(i)])
            xlabel(hAx(i),'Generation')
          end
        case 'iter'
          for i = 1:numVars
            newX=[hEB(i).XData, generation];                            % return existing data
            newY=[hEB(i).XData, M(:, i)];
            newL=[hEB(i).YNegativeData,L(:, i)];                        % must use correct field names...
            newU=[hEB(i).YPositiveData,U(:, i)];       
            set(hEB(i),'Xdata',newX,'Ydata',newY,'YNegativeData',newL,'YPositiveData',newU);
          end
    end
end
Should at least have a chance...
34 Comments
  Torsten
      
      
 on 14 Aug 2024
				Same for option 2:
options = optimoptions('ga', 'PlotFcn', @gaPlotRangeND);
[x, fval] = ga(@booth_func, 2, [], [], [], [], [], [], [], options);
function y = booth_func(x)
    y = (x(1) + 2 * x(2) - 7) ^ 2 + (2 * x(1) + x(2) - 5) ^ 2;
end
function state = gaPlotRangeND(options, state, flag)
  % gaPlotRangeND Plots the mean and the range of the population for n-dimensions.
  % STATE = gaPlotRangeND(OPTIONS, STATE, FLAG) plots the mean and the range
  % (highest and the lowest) of individuals for each variable.  
  generation = state.Generation;
  population = state.Population;
  numVars = size(population, 2);
  M = mean(population);
  L = M - min(population);
  U = max(population) - M;
  switch flag
    case 'init'
      hAx=gobjects(size(M));                                        % prealloate for axes handles
      hEB=gobjects(size(M));                                        % and for errorbars, too...
      for i = 1:numVars
        hAx(i)=subplot(numVars, 1, i);                              % create subplot, save handle to each
        hold(hAx(i),'on')                                           % hold on to add more later
        xlim(hAx(i),[1 options.MaxGenerations+1])
        hEB(i)=errorbar(generation, M(:, i), L(:, i), U(:, i));     % save handle to ith EB
        title(hAx(i),['Range of Population, Mean for Variable ' num2str(i)])
        xlabel(hAx(i),'Generation')
      end
    case 'iter'
      for i = 1:numVars
        hAxi=subplot(numVars,1,i);                                  % the axes handle 
        hEBi=findobj(hAxi,'Type','ErrorBar');                       % find the EB object
        newX=[hEBi.XData, generation];                              % return existing data
        newY=[hEBi.XData, M(:, i)];
        newL=[hEBi.YNegativeData,L(:, i)];                          % must use correct field names...
        newU=[hEBi.YPositiveData,U(:, i)];       
        set(hEBi,'Xdata',newX,'Ydata',newY,'YNegativeData',newL,'YPositiveData',newU);
      end
  end
end
  dpb
      
      
 on 14 Aug 2024
				
      Edited: dpb
      
      
 on 14 Aug 2024
  
			That's truly bizarre!  ga has to be mucking around with the HG environment behind the scenes...
I don't suppose you put a breakpoint into the 'iter' case to see what  gca returns as compared to the saved hAx, did you?  It's truly mind-boggling that those are really no longer valid handles to the initially-created axes...
See Also
Categories
				Find more on Convert Image Type in Help Center and File Exchange
			
	Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!














