Skip to main content

differential equations - Limitations of ParametricNDSolve family w.r.t objective functions


Observation:


I can see even for very simple modification in case of an scalar objective involving an definite integral in time ParametricNDSolve fails. Here is an example!


eqns = {y''[t] + y[t] == 3 a Sin[y[t]], y[0] == y'[0] == 1};

pfun =ParametricNDSolveValue[eqns,Integrate[y[s] - a s, {s, 0, 5}], {t, 0, 5}, {a},
Method -> {"ParametricSensitivity" -> "ForwardSensitivity"}];
pfun[1.5]


Meaningless output!



Same kind of output for pfun'[1.5] but from pfun''[1.5] onwards for higher derivatives we get numerical values which I guess are totally wrong.


However everything will be fine if one uses Integrate[y[s], {s, 0, 5}]! So I tried {"ParametricSensitivity" -> "AdjointSensitivity"} which is most suitable for integrated objective functions. Again failure but this time for both the cases. We get the following error




ParametricNDSolveValue::adjsens: The adjoint sensitivity method cannot be used for the output function {t,0,5}. It can only be used for output functions that are at a particular time or are a definite integral over time. >>



I feel this is a major inconsistency of implementation internal. Using Trace I found some esoteric IntegrateImproperDump` andInternalDependsOnQ.


What should be pfun[1.5]:


We know


Distribute[Integrate[y[s] - a s, {s, 0, 5}], Plus] === 
Integrate[-a s, {s, 0, 5}] + Integrate[y[s], {s, 0, 5}]


True




So we first can find pfun[1.5] using


eqns = {y''[t] + y[t] == 3 a Sin[y[t]], y[0] == y'[0] == 1};
pfun = ParametricNDSolveValue[eqns,
Integrate[y[s], {s, 0, 5}], {t, 0, 5}, {a},
Method -> {"ParametricSensitivity" -> "ForwardSensitivity"}];
(pfun[1.5] + Integrate[-a s, {s, 0, 5}]) /. a -> 1.5


-7.86673




and the first order sensitivity will be


(pfun'[1.5] + D[Integrate[-a s, {s, 0, 5}], a]) /. a -> 1.5


-7.87591



Crosschecking the first order sensitivity below!


fun1[aval_?NumericQ] :=NIntegrate[Block[{a = aval}, 
Evaluate@(y /. First@NDSolve[Evaluate@eqns, y, {t, 0, 5}])[t] -a t] , {t, 0, 5}];

Needs["NumericalCalculus`"];
ND[fun1[x], x, 1.5]


-7.87604



Pretty much as expected.


Question:



  • It will be great to know if we can use ParametricNDSolve family to find parameter dependency of integrated objective like the following: $$ G(p)=\int_a^{b} g(y(s),s,p) \,ds$$ where $g$ is a function of the dependent variable $y(s)$ of the underlying differential equation system and $p$ represents the parameter with respect to which the sensitivity $\frac{dG}{dp}$ is sought (i.e a in the above example).


  • Also why {"ParametricSensitivity" -> "AdjointSensitivity"} fails in the above example?


For some math reference check here.


BR



Answer



OK,


eqns = {y''[t] + y[t] == 3 a Sin[y[t]], y[0] == y'[0] == 1};
pfun = ParametricNDSolveValue[eqns,
Integrate[y[s] - a s, {s, 0, 5}], {t, 0, 5}, {a}
(*,Method\[Rule]{"ParametricSensitivity"\[Rule]\"ForwardSensitivity"}*)];

pfun[1.5]

does not return 'meaningless' stuff but a symbolic integral. And in fact you have requested it to return an Integrate. So to evaluate it just call N.


N[pfun[1.5]]
(* -7.86673 *)

And the same holds for the derivatives. Now, you can not request an NIntegrate in NDSolve, since that would give messages during the function call since then the input to NIntegrate were then symbolic. And similar for the derivative. As to why the second derivative self evaluates, I do not know. If you feel passionate about it, you might report a bug to WRI.


For a sanity check we can use:


Block[{a = 1.5}, 
eqns = {y''[t] + y[t] == 3 a Sin[y[t]], y[0] == y'[0] == 1};

nds = NDSolveValue[eqns, y[t], {t, 0, 5}];
NIntegrate[nds - a t, {t, 0, 5}]
]
(* -7.86673 *)

Using the


, Method -> {"ParametricSensitivity" -> "AdjointSensitivity"}

gives a warning message:


(*

ParametricNDSolveValue::adjsens: "The adjoint sensitivity method cannot be used for the output function {t,0,5}. It can only be used for output functions that are at a particular time or are a definite integral over time."
*)

So only something like this is possible:


eqns = {y''[t] + y[t] == 3 a Sin[y[t]], y[0] == y'[0] == 1};
pfun = ParametricNDSolveValue[eqns,
Integrate[y[1.5], {s, 0, 5}], {t, 0, 5}, {a}
, Method -> {"ParametricSensitivity" -> "AdjointSensitivity"}];
pfun[1.5]


I think ParametricNDSolve is quite a useful function and produces everything else then meaningless output.


Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...