Replace a variable in a fuction handle by a function

4 views (last 30 days)
I am fitting a nonlinear model with nlinfit. First I create the function and then invoke the solver:
%
% initial solution
b1 = [6.808e-3, 229.8e3/8.314];
%
% model
originalM = @(c,x)(exp(-c(1)*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref))));
%
% data structure
x1 = double([t,temp]);
y1 = double(yf);
xnew1 = x1;
ynew1 = y1;
%
% fit the model
[cDD,res,jac,CovB] = nlinfit(x1,y1,originalM,b1);
This works fine. My question is how to replace the parameters c(1), c(2) in the originalM model by functions dependent on another set of parameters? Imagine that c(1) in model above is equal to a function wich depends on a new set of parameters c(3) and c(4)
c(1) = 0.5+exp(-c(3)+c(4);
How can we program this?

Answers (4)

Dyuman Joshi
Dyuman Joshi on 19 Oct 2023
You can directly substitute the expression into the function handle -
originalM = @(c,x)(exp(-(0.5+exp(-c(3)+c(4)))*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref))));
Note that since there is a parenthesis missing from the expression, I have assumed the expression to be -
c(1) = 0.5+exp(-c(3)+c(4));
  4 Comments
James Tursa
James Tursa on 19 Oct 2023
Edited: James Tursa on 19 Oct 2023
@Belmiro Duarte Keep in mind that function handles contain snapshots of all the inputs as they existed at the time the function handle created. Subsequently changing variables that the function handle depends on will have no effect on the function handle. Unfortunatly, it sounds like that is what you want to do. E.g.,
f = @(t) t^2
f = function_handle with value:
@(t)t^2
g = @(t) 3 + f(t)
g = function_handle with value:
@(t)3+f(t)
g(2)
ans = 7
All well and good. Now suppose you want to use a different function f:
f = @(t) t^3
f = function_handle with value:
@(t)t^3
Then re-evaluate g which depends on f:
g(2)
ans = 7
Oops! Nothing changed for g, even though it uses f. Why? Because g contains a snapshot of what f was at the time g was created. Subsequently changing f had no effect on g. The solution to all of this is that you must re-create the function handle if you want it to use the new inputs. E.g., re-create it now and then call the re-created g:
g = @(t) 3 + f(t)
g = function_handle with value:
@(t)3+f(t)
g(2)
ans = 11
The newly created g now has a snapshot of the new f.
Bottom line is once you create a function handle it uses fixed copies of the inputs embedded in the function handle itself.
One way around this is to have the function handle f be one of the inputs to g instead of embedded in g. E.g.,
f = @(t) t^2
f = function_handle with value:
@(t)t^2
g = @(t,f) 3 + f(t)
g = function_handle with value:
@(t,f)3+f(t)
g(2,f)
ans = 7
f = @(t)t^3
f = function_handle with value:
@(t)t^3
g(2,f)
ans = 11
Now things work as maybe you want it to, where you create g once and can change f on the fly to get updated g behavior.
Dyuman Joshi
Dyuman Joshi on 19 Oct 2023
fun = @(c) 0.5+exp(-c(3)+c(4));
originalM = @(c,x)(exp(-fun(c)*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref))));

Sign in to comment.


Torsten
Torsten on 19 Oct 2023
Edited: Torsten on 19 Oct 2023
Define "modelfun" for "nlinfit" not as a function handle, but as a normal function. Within this function, you can do everything you like with the predefined f:
[cDD,res,jac,CovB] = nlinfit(x1,y1,@(c,x)modelfun(c,x,f),b1);
...
function y = modelfun(c,x,f)
y = exp(-f(c)*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref)));
end

Belmiro Duarte
Belmiro Duarte on 20 Oct 2023
Moved: Torsten on 20 Oct 2023
Thanks for the suggestion. I followed it, but I am still strugling with a problem.
I am sending a ficticious file which simulates what I want and the problem which appears only in the last line.
Basically, I obtain a vector of symbolic functions from previous calculations. All the elements of this vector are in turn functions of the parameterer to obtaining by nlinfit. I debugged the code and seems this functions
test1()
Error using load
Unable to find file or directory 'Data1.txt'.

Error in solution>test1 (line 10)
dat = load('Data1.txt');
function test1
%
% find the transformation
%
clear all;
%
% read ficticious data
dat = load('Data1.txt');
[ntime, nvar] = size(dat);
t = dat(:,1);
temp = dat(:,2);
yf = dat(:,3);
np = 3;
%
% data structure
x1 = double([t,temp]);
y1 = double(yf);
xnew1 = x1;
xnew1(:,2) = 1.0./xnew1(:,2);
ynew1 = y1;
%
% simulated matrix and initial parameters
th1 = 6.808e-3;
th2 = 229.8e-3/8.314;
th3 = 1.0/615.0;
tz0 = [th1;th2;th3];
Q = [1, 0.5, -0.8; 0.2, 0.6, -0.4; 0.25, 0.05, 0.2];
%
% generation of symbolic variables that will be substituted by the
% parameters to obtain by fitting
c = transpose(sym("c%d", [1 np]));
%
% solution of a symbolic algebraic system
tz = vpa(simplify(Q*c+tz0));
%
% creation of symbolic variables c(i) to replace those initially created
% c_i
p1 = str2sym('c(1)');
p2 = str2sym('c(2)');
p3 = str2sym('c(3)');
%
% replacement of c_i by c(i) in the expressions. tn is a vector of 3
% symbolic functions on c(i)
tn = subs(tz,{'c1','c2','c3'},{p1,p2,p3});
%
% find the reference point
t_in = vpa(subs(tn,{p1,p2,p3},{th1, th2, th3}));
t_in = transpose(t_in);
%
% fit the model. The model is in modelfunc function and the vector of functions tn is to be passed
% and used. Here, the vector tn contains 3 elements and each one is a function
% of c(i) which are to be fitted.
%
% This part is not working. However, tn is passing correctly to modelfunc
[cDD,res,jac,CovB] = nlinfit(xnew1,ynew1, @(c,xnew1) modelfunc(c,xnew1,tn),t_in);
end
%%
function [F] = modelfunc(c,x,tn)
F = exp(-tn(1).*x(:,1).*exp(-tn(2).*(x(:,2)-tn(3))));
end
  2 Comments
Torsten
Torsten on 20 Oct 2023
Edited: Torsten on 20 Oct 2023
All inputs to nlinfit must be numeric. Check whether xnew1, ynew1, t_in and tn are all numerical vectors (thus not symbolic).
Dyuman Joshi
Dyuman Joshi on 20 Oct 2023
@Belmiro Duarte - Can you explain what do you mean by "This part is not working"?
Do you get an error? If so, copy and paste the whole error message i.e. all of the red text.
If otherwise, please specify.

Sign in to comment.


Belmiro Duarte
Belmiro Duarte on 21 Oct 2023
@Torsten Yes. you are right I had two variables that were symbolic. One of them is t_in (the initial set of parameters), and I already fixed it. Another is the vector of symbolic functions tn . That I do not know how to handle because it contain the functions to generate the model to fit. That is, I want my model dependent on c pararameters which appear in modelfunc because the functions tn depend on them. Any ideas how to overcome it?
@Dyuman Joshi. This is the message I got after having
t_in = transpose(t_in);
by
t_in = double(transpose(t_in));
...
Warning: Solution does not exist because the system is inconsistent.
> In symengine
In sym/privBinaryOp (line 1218)
In \ (line 562)
In nlinfit>LMfit (line 587)
In nlinfit (line 284)
In test41 (line 55)
Conversion to logical from sym is not possible.
Error in nlinfit>LMfit (line 600)
if sse < sseold
Error in nlinfit (line 284)
[beta,J,~,cause,fullr] = LMfit(X,yw, modelw,beta,options,verbose,maxiter);
Error in test41 (line 55)
[cDD,res,jac,CovB] = nlinfit(xnew1,ynew1, @(c,x) modelfunc(c,x,tn),t_in);
It seems the problem is related with the fact that tn is symbolic mentioned by @Torsten. Probably I am missing something here.
  4 Comments
Torsten
Torsten on 21 Oct 2023
Edited: Torsten on 21 Oct 2023
Don't overcomplicate things.
Pass the function handles to your model function and evaluate them when needed:
[cDD,res,jac,CovB] = nlinfit(xnew1,ynew1, @(c,x) modelfunc(c,x,@(z)tr1(z(1),z(2),z(3)),...
@(z)tr2(z(1),z(2),z(3)),@(z)tr3(z(1),z(2),z(3))),t_in);
and use them inside your function when required:
exp(-tr1(c).*x(:,1).*exp(-tr2(c).*(x(:,2)-tr3(c))))
Belmiro Duarte
Belmiro Duarte on 22 Oct 2023
Moved: Dyuman Joshi on 22 Oct 2023
Thanks @Torsten. Your suggestion solved the problem. Cheers

Sign in to comment.

Products


Release

R2022b

Community Treasure Hunt

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

Start Hunting!