0

My problem is that in the below code, every time I want to change $g$ I also have to change $f$.

Let's say we have three functions: $f$, $h$, and $g$ ($h$ is not really relevant for the question, but its there).

Now let's say that we don't specify what $g$ is yet, because we don't want to restrict it to one particular form. But we define $f$ as follows:

f[x_]:=h[x]+NIntegrate[g[y],{y,0,x}]

Now let's say we want to plot $f[5]$ as a function of a parameterization of $g$:

g[y_]:=a*y^b
(*I am tempted to write the following:*)
Plot3D[f[5],{a,0,1},{b,0,1}]

But the above code obviously doesn't do anything. In order for the approach to work, we would have to instead define

f[x_,a_,b_]:=h[x]+NIntegrate[g[y,a,b],{y,0,x}]
g[y_,a_,b_]:=a*y^b
Plot3D[f[5,a,b],{a,0,1},{b,0,1}]

There are two problems with this approach:

  1. It is kind of clumsy

  2. It is not versatile. If we want a specification of $g$ that has 3 parameters, then we need to redefine $f$ as well.

  3. If we have higher levels of nesting of functions, this becomes even more of a hassle.

Is there a way to do this in a cleaner way without passing parameters from function to function, or at least in a way that if we want to change the parameter specification of $g$, we don't have to change all the others as well?

Is there a Canonical way to do this?

user56834
  • 545
  • 3
  • 10

2 Answers2

3

Whenever I see an equation with an integral, I always think about trying to convert it into an ODE. Your question falls into this category. So, let's differentiate your equation:

eqn = f[x] == h[x] + Inactive[NIntegrate][g[y], {y, 0, x}];
D[eqn, x]

f'[x] == g[x] + h'[x]

Now, let's solve this ODE and plot the solution:

g[x_] := a x^b
h[x_] := x^2

f = ParametricNDSolveValue[
    {u'[x] == h'[x] + g[x], u[0] == h[0]},
    u[5],
    {x, 0, 5},
    {a, b}
];

Plot3D[f[a, b], {a, 0, 1}, {b, 0, 1}]

enter image description here

Addendum

You can include g in the definition of f as follows:

f[g_, p_] := ParametricNDSolveValue[
    {u'[x] == h'[x] + g, u[0] == h[0]}, 
    u[5], 
    {x, 0, 5}, 
    p
]

Then, we can use different g functions:

f1 = f[a x^b, {a, b}];

Plot3D[f1[a, b], {a, 0, 1}, {b, 0, 1}]

enter image description here

f2 = f[a Cos[b x], {a, b}];

Plot3D[f2[a, b], {a, 0, 1}, {b, 0, 1}]

enter image description here

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • what is the name of the technique you use in the end, when you define f2 not as a function, and then later treating it as a function? (so I can look it up in the references of the mathematica language) – user56834 Jan 17 '18 at 18:25
  • @Programmer2134 ParametricNDSolveValue returns a ParametricFunction object. If pf is a ParametricFunction object, than pf[parameters..] evaluates to a number in this case, but more typical is that it evaluates to an InterpolatingFunction object. So, f[..] returns a ParametricFunction object, and then the Plot3D function evaluates the ParametricFunction object with a and b values. – Carl Woll Jan 17 '18 at 18:45
2

There are probably half a dozen ways to do what OP wants. Below are given two relatively simple ones.

args___

I would say the simplest way is to have arbitrary arguments passed from f to g.

ClearAll[f, g, h]    
h[x_] := x^2    
f[x_, args___] := h[x] + NIntegrate[g[y, args], {y, 0, x}]

Using f with a definition of g:

g[y_, a_, b_] := a*y^b    
f[6, 0, 1]

(* 36. )

Using f with another definition of g:

g[y_, a_, b_, c_, d_] := a + b*y + c*y^2 + d*y^3
f[6, 1, 2, 3, 4]

(* 1590. *)

A version of this approach is to use OptionsPattern; another version is to use Association. (For the latter see this answer.)

Using SubValues

Here the definition of f takes the functions h and g as arguments (instead of the parameters of g as done above.)

ClearAll[f, g, g1, g2, h]
f[h_, g_][x_?NumberQ] := h[x] + NIntegrate[g[y], {y, 0, x}]
h[x_] := x^2

Here is a couple of definitions of g:

g1[y_, a_, b_] := a*y^b

g2[y_, a_, b_, c_, d_] := a + b*y + c*y^2 + d*y^3

Here is how we invoke them:

f[h, g1[#, 0, 1] &][6]

(* 36. *)

f[h, g2[#, 1, 2, 3, 4] &][6]

(* 1590. *)

The convenience of this approach is that we can specify g "on the spot." For example,

f[h, (#^2 + 3*#) &][6]

(* 162. *)

Note that with the approach in the previous section we do enforce the use of a definition of g (which might be desired in some cases.)

Anton Antonov
  • 37,787
  • 3
  • 100
  • 178
  • Thank you. Couple of questions: 1. What does the "?" do in "?NumberQ"? 2. why do you use f[..][..] (i.e. 2 sets of brackets instead of 1: f[...])? Is there a name for this? (so I can look it up in the documentation) 3. Is there a way to do it without the "#" (Which I think defines a "pure function")? 4. I cannot find "SubValues" in the documentation. Is there now a new term for it? – user56834 Jan 19 '18 at 19:48
  • @Programmer2134 1. _?NumberQ is a pattern matching for something that passes the test NumberQ. 2. For clarity. You can use single [] if your prefer. See "tutorial/TheRepresentationOfDerivatives" in the documentation. 3. Why? If # bothers you use args___; or just define g3[x_]:=g2[x,1,2,3,4] and then pass g3. 4. It was given a reference to SubValues in another comment. – Anton Antonov Jan 20 '18 at 11:49