Obtaining extra output argument(s) from the objective function used by fsolve in MATLAB

I have a MATLAB code (see below) that employs 'fsolve' from the optimization toolbox for a root finding problem.

The bottleneck is that, within the objective function calculation, there is a computationally expensive function (denoted as 'expensive_function' in the below code, and is located in the matlabpath). The variable 'x_sol' holds solution returned by 'fsolve'.

The issue is that, within the main code, I need access to the 'other_results' struct computed by the expensive function for further calculations. It is wasteful to recompute the 'other_results' struct using the solution value (such as by using the following line of code within 'main' )

other_results = expensive_function(x_sol,param);

Is there a way to get access to this struct without re-evaluating the expensive_function?

PS: I have been cautioned throughout my career against using global/persistent variables, and I have thus far stayed clear of them. I don't know if this is the only recourse to solving this issue, or there exists a better/clever way ?

%% main code

x_sol = fsolve(@(x) compute_residual(x,param) , x0);

% code below needs to use various values from the "other_results" struct computed in the obj_fun  function

line with some computation on 'other_results' result
line with some other computation using 'other_results' struct
....
...
and so on.

________________________________________________________

function residual = compute_residual(x, param)
other_results = expensive_function(x,param);  % other_results is a struct returned by a very computationally expensive function
residual = abs(other_results.a * other_results.b - x)   % the abs(diff)
end
• There is a side issue here that you may not have considered. If other_results depends on the value of x passed into expensive_function, it is not necessarily the same as if you called expensive_function(x_sol...) in the main code-- i.e. x_sol may not be equal to x in the last call to compute_residual. – Bill Greene Feb 23 '17 at 15:03
• The root 'x_sol' computed by 'fsolve' uses the last value of 'other_results' in computing the residual. Thus, upon a successful termination of fsolve, the 'other_results' last computed was done so by using 'x_sol' right ? Hence, it is wasteful to re-evaluate the expensive_function again, using x_sol as the input argument. – Krishna Feb 23 '17 at 16:12
• @Krishna: That is not necessarily true, @BillGreene is correct in that compute_residual may be called several times with different values for each fsolve iteration. This behavior may depend on the algorithm used and the objective function. Your answer may be close, but it might not be identical. The best method is to recompute other_results afterwards. You could also possibly use the OutputFcn option as it is called at the end of every iteration. – horchler Feb 23 '17 at 17:01

As I said in my comment, I don't think you can necessarily count on the value of x in the last call to your function being the value that is returned as the solution.

But, that being said, here is a way to do what you want. The solution in the code I show below relies on two components. First you define a MATLAB class that derives from handle. This makes it a so-called handle class so that when it is passed to a function as an argument, it is passed by reference, and therefore its contents can be modified by that function. Second, an anonymous function definition is used to pass an instance of this class into your fsolve function so you can store your solution there. The example code below shows this explicitly.

The class definition is

classdef SolutionSaver < handle
properties
x, expensiveResult;
end
end

And the test case that uses this class is

function solutionSaveTest
ss = SolutionSaver;
func = @(x) calcF(x, ss);
x = fsolve(func, 1);
fprintf('x=%18.10e, ss.x=%18.10e\n', x, ss.x);
ss.expensiveResult
end

function f=calcF(x, ss)
e=expensive_calc(x);
f = e - 5;
ss.x = x;
ss.expensiveResult = e;
end

function e=expensive_calc(x)
e = x^2;
end

I ran this example in Octave and, in this case, the saved x was equal to the returned x. But, in general, it is probably worth testing that fact before using the saved solution data.