Skip to content

Ensures the plot event always calculates SLD #411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions API/events/eventManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ function setEvents(value)
persistent events

if isempty(events)
% Events are stored in a 3 column cell containing the ID,
% event type and callback for the event.
events = cell(0, 3);
end

Expand All @@ -36,6 +38,15 @@ function setEvents(value)
value = events;
end

function state=hasEvent(event_type)

state = false;
events = eventManager.getEvents();
if any(find([events{:, 2}] == event_type, 1))
state = true;
end
end

function funcID = getCallbackID(callback)
% Generates a id for a callback handle function
%
Expand Down
32 changes: 32 additions & 0 deletions API/events/hasPlotHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
function state = hasPlotHandler()
% Checks if a function is listening for the plot event
%
% state = hasPlotHandler();
persistent helper;

initialised = false;
state = false;
coder.extrinsic('eventManager.hasEvent')
if coder.target('MATLAB') || coder.target('MEX')
if eventManager.hasEvent(coderEnums.eventTypes.Plot)
state = true;
end
else
coder.cinclude('eventHelper.hpp');
coder.updateBuildInfo('addLinkFlags','-ldl');
if isempty(helper)
% Declaration for coder
helper = coder.opaque('eventHelper','NULL','HeaderFile','eventHelper.hpp');

% Make an instance
helper = coder.ceval('eventHelper');
path = [getenv('RAT_PATH'), 0];
coder.ceval('std::mem_fn(&eventHelper::init)', helper, path);
end

initialised = coder.ceval('std::mem_fn(&eventHelper::isInitialised)', helper);
if initialised
state = coder.ceval('std::mem_fn(&eventHelper::hasPlotHandler)', helper);
end
end
end
5 changes: 0 additions & 5 deletions API/events/triggerEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,6 @@ function triggerEvent(eventType, varargin)
elseif eventType == coderEnums.eventTypes.Progress
coder.ceval('std::mem_fn(&eventHelper::updateProgress)', helper, [varargin{1},0], varargin{2});
elseif eventType == coderEnums.eventTypes.Plot
hasPlotHandler = coder.ceval('std::mem_fn(&eventHelper::hasPlotHandler)', helper);
if ~hasPlotHandler
return;
end

result = varargin{1};
problemStruct = varargin{2};
subRoughs = result.contrastParams.subRoughs;
Expand Down
4 changes: 2 additions & 2 deletions cppDeploy.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@

% Clean up
delete 'compile/fullCompile/deploy.zip' 'compile/fullCompile/cppDeploy/buildInfo.mat'...
'compile/fullCompile/cppDeploy/rtw_proj.tmw' 'compile/fullCompile/cppDeploy/defines.txt'
'compile/fullCompile/cppDeploy/RATMain.a';
'compile/fullCompile/cppDeploy/rtw_proj.tmw' 'compile/fullCompile/cppDeploy/defines.txt'...
'compile/fullCompile/cppDeploy/RATMain.a' 'compile/fullCompile/cppDeploy/RATMain.lib';
10 changes: 8 additions & 2 deletions minimisers/DE/deopt.m
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@
I_refresh = S_struct.I_refresh;
I_plotting = S_struct.I_plotting;

doPlotEvent = hasPlotHandler();

%-----Check input variables---------------------------------------------
if (I_NP < 5)
I_NP=5;
Expand Down Expand Up @@ -343,8 +345,10 @@
end

% Trigger the output event...
if rem(I_iter, controls.updatePlotFreq) == 0
if doPlotEvent && rem(I_iter, controls.updatePlotFreq) == 0
controls.calcSLD = true;
[~,result] = fname(FVr_bestmem,problem,controls);
controls.calcSLD = false;
triggerEvent(coderEnums.eventTypes.Plot, result, problem);
end

Expand All @@ -366,9 +370,11 @@
triggerEvent(coderEnums.eventTypes.Message, ...
sprintf('Iteration: %g, Best: %f, fWeight: %f, F_CR: %f, I_NP: %g\n\n', I_iter-1,S_bestval.FVr_oa(1),fWeight,F_CR,I_NP));
end
if rem(I_iter-1, controls.updatePlotFreq) ~= 0
if doPlotEvent && rem(I_iter-1, controls.updatePlotFreq) ~= 0
% This should ensure the final result is always plotted irrespective of update frequency
controls.calcSLD = true;
[~,result] = fname(FVr_bestmem,problem,controls);
controls.calcSLD = false;
triggerEvent(coderEnums.eventTypes.Plot, result, problem);
end
end
55 changes: 29 additions & 26 deletions minimisers/simplex/fMinSearch.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
tolf = optimget(options,'TolFun',defaultopt,'fast');
maxfun = optimget(options,'MaxFunEvals',defaultopt,'fast');
maxiter = optimget(options,'MaxIter',defaultopt,'fast');
funValCheck = strcmp(optimget(options,'FunValCheck',defaultopt,'fast'),'on');

switch dis % Changed from TMW fminsearch
case {'notify','notify-detailed'}
Expand All @@ -85,20 +84,10 @@
% Convert to function handle as needed.
% funfcn = fcnchk(funfcn,length(varargin));
% Add a wrapper function to check for Inf/NaN/complex values
controls = varargin{2};
problemStruct = varargin{1};
if funValCheck
% Add a wrapper function, CHECKFUN, to check for NaN/complex values without
% having to change the calls that look like this:
% f = funfcn(x,varargin{:});
% x is the first argument to CHECKFUN, then the user's function,
% then the elements of varargin. To accomplish this we need to add the
% user's function to the beginning of varargin, and change funfcn to be
% CHECKFUN.
varargin = [{funfcn}, varargin];
funfcn = @checkfun;
end
%
controls = varargin{2};
params = varargin{3};
doPlotEvent = hasPlotHandler();
n = numel(x);

% Initialize parameters
Expand All @@ -112,7 +101,11 @@
v = zeros(n,n+1); fv = zeros(1,n+1);
v(:,1) = xin; % Place input guess in the simplex! (credit L.Pfeffer at Stanford)
x(:) = xin; % Change x to the form expected by funfcn
[fv(:,1), result] = funfcn(x,varargin{:});
if doPlotEvent
controls.calcSLD = true;
end
[fv(:,1), result] = funfcn(x, problemStruct, controls, params);
controls.calcSLD = false;
func_evals = 1;
itercount = 0;
coder.varsize('how',[1 Inf],[0 1]);
Expand Down Expand Up @@ -164,7 +157,9 @@
% fprintf('%g \n', func_evals)
end

triggerEvent(coderEnums.eventTypes.Plot, result, problemStruct);
if doPlotEvent
triggerEvent(coderEnums.eventTypes.Plot, result, problemStruct);
end

% OutputFcn and PlotFcns call
% if haveoutputfcn || haveplotfcn
Expand All @@ -191,10 +186,13 @@
y(j) = zero_term_delta;
end
v(:,j+1) = y;
x(:) = y; [f, result] = funfcn(x,varargin{:});
if doPlotEvent && j==n
controls.calcSLD = true;
end
x(:) = y; [f, result] = funfcn(x, problemStruct, controls, params);
fv(1,j+1) = f;
end

controls.calcSLD = false;
% sort so v(1,:) has the lowest function value
[fv,j] = sort(fv);
v = v(:,j);
Expand All @@ -215,7 +213,7 @@
% fprintf('%s \n', 'func_evals = ')
% fprintf('%g \n', func_evals)
end
if rem(itercount, controls.updatePlotFreq) == 0
if doPlotEvent && rem(itercount, controls.updatePlotFreq) == 0
triggerEvent(coderEnums.eventTypes.Plot, result, problemStruct);
end
if isRATStopped(controls.IPCFilePath)
Expand Down Expand Up @@ -247,6 +245,9 @@
% The iteration stops if the maximum number of iterations or function evaluations
% are exceeded
while func_evals < maxfun && itercount < maxiter
if doPlotEvent
controls.calcSLD = true;
end
if max(abs(fv(1)-fv(2:n+1))) <= max(tolf,10*eps(fv(1))) && ...
max(max(abs(v(:,2:n+1)-v(:,onesn)))) <= max(tolx,10*eps(max(v(:,1))))
break
Expand All @@ -257,13 +258,14 @@
% xbar = average of the n (NOT n+1) best points
xbar = sum(v(:,1:n), 2)/n;
xr = (1 + rho)*xbar - rho*v(:,end);
x(:) = xr; [fxr, result] = funfcn(x,varargin{:});
x(:) = xr; [fxr, result] = funfcn(x, problemStruct, controls, params);
func_evals = func_evals+1;

if fxr < fv(:,1)
% Calculate the expansion point
xe = (1 + rho*chi)*xbar - rho*chi*v(:,end);
x(:) = xe; [fxe, result] = funfcn(x,varargin{:});

x(:) = xe; [fxe, result] = funfcn(x, problemStruct, controls, params);
func_evals = func_evals+1;
if fxe < fxr
v(:,end) = xe;
Expand All @@ -284,7 +286,7 @@
if fxr < fv(:,end)
% Perform an outside contraction
xc = (1 + psi*rho)*xbar - psi*rho*v(:,end);
x(:) = xc; [fxc, result] = funfcn(x,varargin{:});
x(:) = xc; [fxc, result] = funfcn(x, problemStruct, controls, params);
func_evals = func_evals+1;

if fxc <= fxr
Expand All @@ -298,7 +300,7 @@
else
% Perform an inside contraction
xcc = (1-psi)*xbar + psi*v(:,end);
x(:) = xcc; [fxcc, result] = funfcn(x,varargin{:});
x(:) = xcc; [fxcc, result] = funfcn(x, problemStruct, controls, params);
func_evals = func_evals+1;

if fxcc < fv(:,end)
Expand All @@ -313,7 +315,7 @@
if strcmp(how,'shrink')
for j=2:n+1
v(:,j)=v(:,1)+sigma*(v(:,j) - v(:,1));
x(:) = v(:,j); [fv(:,j), result] = funfcn(x,varargin{:});
x(:) = v(:,j); [fv(:,j), result] = funfcn(x, problemStruct, controls, params);
end
func_evals = func_evals + n;
end
Expand All @@ -334,7 +336,8 @@
% fprintf('%s \n', 'func_evals = ')
% fprintf('%s \n', num2str(func_evals))
end
if rem(itercount, controls.updatePlotFreq) == 0
controls.calcSLD = false;
if doPlotEvent && rem(itercount, controls.updatePlotFreq) == 0
triggerEvent(coderEnums.eventTypes.Plot, result, problemStruct);
end
if isRATStopped(controls.IPCFilePath)
Expand Down Expand Up @@ -362,7 +365,7 @@
% This should ensure the final result is printed at the end of a run irrespective of update frequency
triggerEvent(coderEnums.eventTypes.Message, sprintf(' %5.0f %5.0f %12.6g %s\n', itercount, func_evals, fv(1), how));
end
if rem(itercount, controls.updatePlotFreq) ~= 0
if doPlotEvent && rem(itercount, controls.updatePlotFreq) ~= 0
% This should ensure the final result is always plotted irrespective of update frequency
triggerEvent(coderEnums.eventTypes.Plot, result, problemStruct);
end
Expand Down