0
$\begingroup$

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?

$\endgroup$
  • $\begingroup$ You could use g and h also as arguments to f... $\endgroup$ – Henrik Schumacher Jan 17 '18 at 11:21
  • $\begingroup$ This answer for a question about using global variables has several suggestions to achieve the versatility OP wants. $\endgroup$ – Anton Antonov Jan 17 '18 at 12:41
  • $\begingroup$ Also look here where SubValues are used. Can't find SubValues in Help though. $\endgroup$ – Hugh Jan 17 '18 at 15:17
  • $\begingroup$ @HenrikSchumacher, could you point me to something on how to do that? $\endgroup$ – user56834 Jan 17 '18 at 19:05
3
$\begingroup$

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

$\endgroup$
  • $\begingroup$ 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) $\endgroup$ – user56834 Jan 17 '18 at 18:25
  • $\begingroup$ @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. $\endgroup$ – Carl Woll Jan 17 '18 at 18:45
2
$\begingroup$

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.)

$\endgroup$
  • $\begingroup$ 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? $\endgroup$ – user56834 Jan 19 '18 at 19:48
  • $\begingroup$ @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. $\endgroup$ – Anton Antonov Jan 20 '18 at 11:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.