# Problem using optimization for modeling

GROUPS:
 Paul McGill 3 Votes I'm trying use NMinimize to determine the parameters of a model, but I'm having problems using OutputResponse as part of the objective function. The model represents lithium-ion cells used to power an autonomous underwater vehicle. I can measure a real cell's voltage response to changes in output current, and I want to compare that output to the output of my model for a given set of model circuit parameters. If I can minimize the difference between the output of my model and the output of a real cell, then my model will be correct. In general, I'm trying to minimize the difference between two curves.I use OutputResponse to generate the output of my model. My problem is that OutputResponse can't simulate the response of a system that contains symbolic parameters. But my model parameters have to be symbolic so NMinimize can vary them. Here is my code (sorry for the long posting):Write the transfer function of the circuit that models the complex impedance of the cell.tf = (Rser + Rfast/((Rfast s Cfast) + 1) + Rslow/((Rslow s Cslow) + 1));Convert the tf into a transfer function model.tfm = TransferFunctionModel[{{tf}}, s];Generate a battery current profile.Ibatt = UnitStep[t - 1] - UnitStep[t - 5];Plot[Ibatt, {t, 0, 10}, Exclusions -> None]Assign values to the circuit parameters.vals = {Rser -> 0.1, Rfast -> 0.05, Cfast -> 10, Rslow -> 0.01, Cslow -> 100};Voc = 4.2;Find the output response for these circuit values and current profile.y = OutputResponse[tfm /. vals, Ibatt, {t, 10}];Plot[Voc - y, {t, 0, 10}]Define some different circuit values to get a different response and plot both responses.dvals = {Rser -> 0.06, Rfast -> 0.08, Cfast -> 15, Rslow -> 0.015, Cslow -> 50};dy = OutputResponse[tfm /. dvals, Ibatt, {t, 10}];Plot[{Voc - y, Voc - dy}, {t, 0, 10}]Find the area of the difference between the two responses.Plot[Abs[y - dy], {t, 0, 10}]NIntegrate[Abs[y - dy], {t, 0, 10}]{0.156543}I now have an objective function and can try modeling. Given only the first response curve as a template, find the circuit values that produced that response as a test of the modeling algorithm. Do this by minimizing the difference between the template and the output of a model whose circuit values are adjusted by NMinimize. In this case I already know the answer, but if it works then I can use measurements of an actual cell as my template. NMinimize[  NIntegrate[   Abs[    OutputResponse[tfm, Ibatt, {t, 0, 10}] - y   ], {t, 0, 10}  ], {Rser, Rfast, Cfast, Rslow, Cslow} ]  OutputResponse::symbs: Cannot simulate response of system StateSpaceModel[{{{0,1},{-(1/(Cfast Cslow Rfast Rslow)),-((Cfast Rfast+Cslow Rslow)/(Cfast Cslow Rfast Rslow))}},{{0},{1}},{{-(Rser/(Cfast Cslow Rfast Rslow))+(Rfast+Rser+Rslow)/(Cfast Cslow Rfast Rslow),-((Rser (Cfast Rfast+Cslow Rslow))/(Cfast Cslow Rfast Rslow))+(<<1>>+<<2>>+<<1>>)/<<1>>}},{{Rser}}},<<18>>->None] that contains symbolic parameter(s). >>As you can see, OutputResponse can't produce an output because it has symbolic parameters.How do I get NMinimize to set the values of the parameters before OutputResponse uses them? Will NMinimize work at all with this type of objective function?Thanks for any help,Paul
1 year ago
12 Replies
 Bill Simpson 3 Votes After evaluating all but your last block of code I look at the followingIn[14]:= tfmOut[14]= TransferFunctionModel[{{{Rfast + Rser + Rslow + Cfast*Rfast*Rser*s + Cfast*Rfast*Rslow*s + Cslow*Rfast*Rslow*s + Cslow*Rser*Rslow*s + Cfast*Cslow*Rfast*Rser*Rslow*s^2}}, (1 + Cfast*Rfast*s)*(1 + Cslow*Rslow*s)}, s]Thus I change your last block of code to be In[15]:= f[{Rse_?NumericQ, Rfa_?NumericQ, Cfa_?NumericQ, Rsl_?NumericQ, Csl_?NumericQ}] :=  (Print[{tfm /. {Rser -> Rse, Rfast -> Rfa, Cfast -> Cfa, Rslow -> Rsl, Cslow -> Csl}}]];    NIntegrate[Abs[OutputResponse[tfm/.{Rser->Rse,Rfast->Rfa,Cfast->Cfa,Rslow->Rsl,Cslow->Csl}, Ibatt, {t,0,10}]-y], {t,0,10}]  ); NMinimize[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, Rfast, Cfast, Rslow, Cslow}]  During evaluation of In[16]:={TransferFunctionModel[{{{1.07688+0.15247*s-0.02527*s^2}},(1-0.07403*s)*(1+0.34557*s)},s]}  During evaluation of In[16]:= {TransferFunctionModel[{{{1.07688+0.15247*s-0.02527*s^2}},(1-0.07403*s)*(1+0.34557*s)},s]}During evaluation of In[16]:= NMinimize::nnum: The function value {1.19169*10^51} is not a number at{Cfast,Cslow,Rfast,Rser,Rslow} = {0.989531,0.284773,0.349234,0.987641,-0.259993}. >>Out[17]= NMinimize[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, Rfast, Cfast, Rslow, Cslow}]I make some of those changes based on looking at your use of OutputResponse in a previous code block and the rest based on experimentation.By introducing a dummy function f and using ?NumericQ on each of the arguments I forced everything to be numeric to satisfy NIntegrate.By using :=(Print[],NIntegrate[]); I get a look at your integrand every time f is called.The error message from NMinimize blowing up may be because I've misused part of your code.Hopefully something in this will give you the needed clue to get to the next step.
1 year ago
 Shenghui Yang 4 Votes Though not recomended, you can still use NMinimize or FindMinimum in this way. Here is a piece of code that should work regardless of problematic numeric performance: Define the system and input: tf[Rser_, Rfast_, Cfast_, Rslow_, Cslow_, s_] := (Rser + Rfast/((Rfast s Cfast) + 1) + Rslow/((Rslow s Cslow) + 1)) tfm[Rser_, Rfast_, Cfast_, Rslow_, Cslow_, s_] :=    TransferFunctionModel[{{tf[Rser, Rfast, Cfast, Rslow, Cslow, s]}}, s] Ibatt = UnitStep[t - 1] - UnitStep[t - 5];Define responce y and responce f with parametery = OutputResponse[tfm[0.1, 0.05, 10, 0.01`, 100, t], Ibatt, {t, 10}]; f[Rser_?NumericQ, Rfast_?NumericQ, Cfast_?NumericQ, Rslow_?NumericQ, Cslow_?NumericQ] :=    First@NIntegrate[         Abs[OutputResponse[tfm[Rser, Rfast, Cfast, Rslow, Cslow, t],Ibatt, {t, 0, 10}] - y],         {t, 0, 10},         Method -> {"LocalAdaptive", "SymbolicProcessing" -> 0, Method -> {"TrapezoidalRule"}} ]You can use the optimization function here to find the close result: and use a dynamic variable to track the process: FindMinimum[         f[Rser, Rfast, Cfast, Rslow, Cslow],         {{Rser, 0.05}, {Rfast, 0.08}, {Cfast, 5}, {Rslow, 1/10}, {Cslow, 50}}, StepMonitor :> (x = Cslow)]Dynamic[x]The result I have for this set of value is not bad even though it halted due to poor convergence: During evaluation of In[30]:= FindMinimum::sdprec: Line search unable to find a sufficient decrease in the function value with MachinePrecision digit precision. >>Out[30]= {0.173594, {Rser -> 0.0555499, Rfast -> 0.0593808, Cfast -> 5., Rslow -> 0.0779124, Cslow -> 50.}}For this type of non-linear system, you may want to find some heuristical values first before you go ahead. Proper start value and boundaries for the parameters are very helpful in general.
1 year ago
 Paul McGill 2 Votes Bill - I really like your approach of using a dummy function. I don't understand how adding the pattern test to the function arguments forces them to be numbers. I thought that adding those qualifiers to function arguments tests the arguments and rejects them if they don't match, but it doesn't force them to be anything. Oh well, there's a lot about Mma I don't understand...I defined my dummy function as follows f[{Rse_?NumericQ, Rfa_?NumericQ, Cfa_?NumericQ, Rsl_?NumericQ, Csl_?NumericQ}] :=  (error =     NIntegrate[     Abs[OutputResponse[        tfm /. {Rser -> Rse, Rfast -> Rfa, Cfast -> Cfa, Rslow -> Rsl,           Cslow -> Csl}, Ibatt, {t, 0, 10}] - y     ], {t, 0, 10}    ];   Print[error, " ", Rser, " ", Rfast, " ", Cfast, " ", Rslow, " ", Cslow];)Unfortunately, when I run the minimization, even adding constraints, I getNMinimize[{f[{Rser, Rfast, Cfast, Rslow, Cslow}], Rser > 0,   Rser < 0.5, Rfast > 0, Rfast < 0.5, Cfast > 10, Cfast < 100,   Rslow > 0, Rslow < 0.5, Cslow > 50, Cslow < 200}, {Rser, Rfast, Cfast, Rslow, Cslow}]{1.74048} 0.485254 0.301249 93.0382 0.148942 135.54{1.74048} 0.485254 0.301249 93.0382 0.148942 135.54{1.74048} Rser Rfast Cfast Rslow CslowNMinimize::nnum: The function value Null is not a number at {Cfast,Cslow,Rfast,Rser,Rslow} = {93.0382,135.54,0.301249,0.485254,0.148942}. >>The good news is that NMinimize is working with numeric arguments. The bad news is that the arguments don't seem to change and then it fails after just two iterations. I'm not sure what's going on but will play with it some more. Thanks again for looking at it for me.Shenghui - Thank you also for taking the time to help with this. I'm going to try your suggestions tomorrow. Perhaps your method will work with constraints.
1 year ago
 You need your f[] to return a real value, not a list and not Null. Your version isn't doing that.Print out what your version of f returns and see whether you think that would be something that could be minimized.Perhaps this version will help you.f[{Rse_?NumericQ, Rfa_?NumericQ, Cfa_?NumericQ, Rsl_?NumericQ, Csl_?NumericQ}] := (error = NIntegrate[Abs[OutputResponse[tfm/.{Rser->Rse,Rfast->Rfa,Cfast->Cfa,Rslow->Rsl,Cslow->Csl},Ibatt,{t,0,10}]-y],{t,0,10}];   Print[error, " ", Rse, " ", Rfa, " ", Cfa, " ", Rsl, " ", Csl];   First[error]);NMinimize[{f[{Rser, Rfast, Cfast, Rslow, Cslow}], 0 "RandomSearch"]Looking at that as it runs shows how slow it is and how it seems to choose some very odd values for your five parameters, but at least it isn't immediately failing and it seems to be gradually crawling towards a minimum.The other person who responded to you began to question whether this whole approach to the problem is wise or not. I'm not even beginning to point you towards whether what you have chosen is appropriate or what the speed of convergence will be. I'm still just trying to get you to a point where you write working code. There are reasons that are not always obvious and are sometimes nearly hidden why a bit of code I write is the way it is. Much of this is based on the underlying model of calculation that Mathematica uses. I'm not sure how to get you to a place where parts of that are clear to you.About the ?NumericQ, many of the algorithms in Mathematica do things you might not expect. A very common problem is for someone to write a function that depends on the parameters having actual numeric values, instead of just being x and y and z. This often happens because something like NMinimize or Integrate might call your function first with just symbols instead of beginning with numeric values. The guess is that NMinimize or Integrate is "probing" or testing your function, to see if it can get a symbolic result back and save a lot of time testing with numerics. If you have written f[x_,y_,z_] which you expect to be given numbers and which will fail when called with f[p,q,r] then you have a problem like you described at the beginning, "why is my function failing?!?!?" By using ?Numeric you are only defining your f for constant numeric arguments. Then when Integrate or Minimize probes your f to see if it can get a symbolic answer it finds there is no f defined for f[p,q,r] and so it starts over with only numeric values and begins searching from there.  So, ?Numeric does not somehow magically transform p into a number, but it does force NMinimize to see it must use numeric constants for parameters to not fail or have your function fail. Again, this is some understanding, some experience, some shared knowledge, some folklore and some superstition all combined to be able to write Mathematica code that (mostly) works.
1 year ago
 Shenghui Yang 1 Vote @Bill, you are right:This often happens because something like NMinimize or Integrate might call your function first with just symbols instead of beginning with numeric values. The guess is that NMinimize or Integrate is "probing" or testing your function, to see if it can get a symbolic result back and save a lot of time testing with numerics.This is officially implemented via the HoldAll attribute of NMinimize and its siblings.
1 year ago
 Ilian Gachevski 3 Votes This is officially implemented via the HoldAll attribute of NMinimize and its siblings.NMinimize does not have the HoldAll attribute. FindMinimum does, but all that means is that the argument is not evaluated before being passed to function. Then, to quote the documentation, FindMinimum "first localizes the values of all variables, then evaluates f with the variables being symbolic, and then repeatedly evaluates the result numerically". The use of a black box objective function f (defined using _?NumericQ) prevents the symbolic evaluation step by keeping f unevaluated for non-numeric arguments.
1 year ago
 Paul McGill 1 Vote Wow, you guys are amazing. I can't tell you how much I appreciate your patient and clear answers to my questions.You need your f[] to return a real value, not a list and not Null. Your version isn't doing that.D'oh! I've been using Mma since the late 1980s, and consider myself an intermediate-level programmer (I'm an EE), but that was a rookie mistake. I've never completely understood how Mma decides what to return from a function, so I usually just experiment until it works. Now I know to always test my objective function to make sure it returns something that can be optimized.The other person who responded to you began to question whether this whole approach to the problem is wise or not. I'm not even beginning to point you towards whether what you have chosen is appropriate or what the speed of convergence will be. I'm still just trying to get you to a point where you write working code.Yes, you guys are right - I may be going about this all wrong and I'm open to suggestions on a better approach. My goal was to get something working and then improve it. Even if it's very slow in its current form, I don't mind waiting a long time for the answer because I only have to do the modeling once. Sometimes I find that writing code that runs slowly gets me to an answer more quickly than spending a lot of time optimizing code that runs quickly. I can also work on other things while my slow code is running.About the ?NumericQ, many of the algorithms in Mathematica do things you might not expect.That's certainly true! Now I understand that the pattern test is, in this case, a flag to NMinimize to tell it not to bother to look at the objective function symbolically. I would never have figured that out on my own. I would think that this slows down the optimization because NMinimize can't "look" at the solution surface to see its shape or complexity, but I don't think I have an alternative.I'm currently running Bill's code but it only tries about 2 vectors per second, so I turned off the printing and I'll let it run overnight. I've also switched to differential evolution, as my previous reading on it claims that it's very robust for complex, nonlinear solution spaces. If it doesn't converge then I'll try Shenghui's approach since NMinimize and FindMinimum seem to use different algorithms. I'll let you know what I find.
1 year ago
 Todd Rowland 1 Vote It might be possible to get a close enough answer by searching the whole parameter space (in the spirit of Stephen Wolfram's "A New Kind of Science")This code will take a while, but it will finish in an overnight run.data = Table[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, 0, .5, .1}, {Rfast, 0, .5, .1}, {Cfast, 10, 100, 10}, {Rslow, 0, .5, .1}, {Cslow, 50, 200, 30}]; {Min[data], Position[data, Min[data]]}That way you can get a better sense for what is going on by studying data, and also you get an answer with some feeling for its correctness.