Replace a variable in a fuction handle by a function

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?

답변 (4개)

Dyuman Joshi
Dyuman Joshi 2023년 10월 19일
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

Or if you don't want to have to recreate the function handle, make a new one:
originalM = @(c,x)(exp(-c(1)*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref))));
newM = @(c, x) originalM([0.5+exp(-c(3)+c(4)), c(2), c(3), c(4)], x);
This causes newM to ignore what its caller passes in as c(1), but that sounds like what you want it to do. Or if originalM doesn't need c(3) or c(4) (only newM needs them to create the first two elements of c for use by originalM) you could just skip passing them in.
originalM = @(c,x)(exp(-c(1)*x(:,1).*exp(-c(2)*(1.0./x(:,2)-1.0/Tref))));
% Using an arbitrary expression of c(3) and c(4) for c(2)
newM = @(c, x) originalM([0.5+exp(-c(3)+c(4)), c(3)-c(4)], x);
Belmiro Duarte
Belmiro Duarte 2023년 10월 19일
이동: Dyuman Joshi 2023년 10월 19일
Thank you both. Both alternatives are interesting but I want to automate the procedure. So the function
0.5+exp(-c(3)+c(4));
is just a result of intermediate symbolic calculations. Other forms can be obtained. I would like to include a generic function f(c) in the handle @(c,x) where f(c) can be 0.5+exp(-c(3)+c(4)) or having another symbolic representation.
Do you have suggestions for this requirement?
@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.
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))));

댓글을 달려면 로그인하십시오.

Torsten
Torsten 2023년 10월 19일
편집: Torsten 2023년 10월 19일
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 2023년 10월 20일
이동: Torsten 2023년 10월 20일
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

Torsten
Torsten 2023년 10월 20일
편집: Torsten 2023년 10월 20일
All inputs to nlinfit must be numeric. Check whether xnew1, ynew1, t_in and tn are all numerical vectors (thus not symbolic).
@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.

댓글을 달려면 로그인하십시오.

Belmiro Duarte
Belmiro Duarte 2023년 10월 21일

0 개 추천

@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

You must use "matlabFunction" to turn symbolic functions into numerical function handles.
Belmiro Duarte
Belmiro Duarte 2023년 10월 21일
이동: Torsten 2023년 10월 21일
Yes... I can transfor the symbolic expressions in tn by using
t1 = matlabFunction(tz(1));
t2 = matlabFunction(tz(2));
t3 = matlabFunction(tz(3));
I will obtain numeric functions
tr1 =
function_handle with value:
@(c1,c2,c3)c1+c2.*5.0e-1-c3.*8.0e-1+6.808e-3
tr2 =
function_handle with value:
@(c1,c2,c3)c1.*2.0e-1+c2.*6.0e-1-c3.*4.0e-1+2.764012509020929e-2
tr3 =
function_handle with value:
@(c1,c2,c3)c1.*2.5e-1+c2.*5.0e-2+c3.*2.0e-1+1.626016260162602e-3
Now, I have two problems: 1. collapse the scalars into a vector c(1:3); 2. combine these functions in a new one, say
exp(-tr1.*x(:,1).*exp(-tr2.*(x(:,2)-tr3))) I can use for fitting. Is that it or am I not following your idea?
Torsten
Torsten 2023년 10월 21일
편집: Torsten 2023년 10월 21일
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 2023년 10월 22일
이동: Dyuman Joshi 2023년 10월 22일
Thanks @Torsten. Your suggestion solved the problem. Cheers

댓글을 달려면 로그인하십시오.

카테고리

제품

릴리스

R2022b

질문:

2023년 10월 19일

이동:

2023년 10월 22일

Community Treasure Hunt

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

Start Hunting!

Translated by