1
$\begingroup$

TL/DR: Why do the Sage functions defined below behave differently?

Consider the following recursively-defined sequence of functions, $g_n: \mathbb{R}\to\mathbb{R}$: $$ g_0(x) = 1, $$ and for $n\geq 1$, $$ g_n(x) = \int_0^x g_{n-1}(x-t) \, dt. $$ We can prove by induction that $g_n(x) = \frac{1}{n!}x^n$, for all $n$ and $x$. For the induction step, treat $x$ as fixed and substitute $u = x-t$, $du= -dt$. Alternatively, we could use differentiation under the integral sign to show that $g_n'(x) = \frac{1}{(n-1)!}x^{n-1}$ for $n\geq 1$, and then note that $g_n(0) = 0$. There may be other approaches.

So, the result is not in doubt. My question is about how one might check this result in Sage.

Here is an implementation that seems to work:

t = var('t')
x = var('x')

def g(n):
    if n == 0:
        return lambda x: 1
    else:
        expression = integral(g(n-1)(x-t), t, 0, x)
        return lambda x: expression.subs(x=x)

For example if I do g(8)(1), I get 1/40320. This is the correct result, because $8!=40320$. (Here I am implementing $g_8$ as g(8), so technically g is a function which takes natural numbers to functions--in other words, g is a sequence of functions.)

Here's one that doesn't work (with t,x defined as above):

def h(n):
    if n == 0:
        return lambda x: 1
    else:
        expression = integral(h(n-1)(x-t), t, 0, x)
        return lambda x: expression

Now h(8)(1) gives x^8, which clearly is not what I want, because $g_8(1)$ should not have any free variables! But the only difference between these two versions is the .subs(x=x) in the last line, so I guess my question is:

How does "substituting $x$ for $x$" cause different behavior?

I suspect that the issue is some kind of subtlety about reusing the same variable name at different levels of recursion, but I still would have (naïvely) expected the two versions to either both work or both fail.

$\endgroup$

1 Answer 1

2
$\begingroup$

As you have noticed, there is a difference between the functions

a = lambda x: expression.subs(x=x)

and

b = lambda x: expression

where expression is a symbolic expression which is defined in terms of some variable var('x').

Namely, e.g. a(5) evaluates to expression.subs(x=5), whereas b(5) evaluates to expression which will still depend on var('x') because no substitution is done. Note b does not use its argument at all, whereas a passes its argument as a keyword argument to subs where the key has the name 'x' (corresponding to the name of the variable which you want to substitute). Your confusion is probably due to the names of the lambdas arguments. The above are equivalent to:

a = lambda z: expression.subs(x=z)

and

b = lambda z: expression

which makes it clearer what happens.

$\endgroup$
2
  • $\begingroup$ +1, Thanks, this is excellent! (I'll leave the question open a bit longer, and then most likely accept this answer.) I think it was not obvious to me that a(5) would evaluate to expression.subs(x=5), rather than, say, expression.subs(5=5) or something (which would likely produce an error, but anyway). I think I just didn't understand .subs() very well. So I'll think about that and then maybe ask a different question if necessary. Thanks again! $\endgroup$
    – mathmandan
    Apr 24, 2020 at 21:51
  • $\begingroup$ You're welcome! For that you should also understand keyword arguments in Python. $\endgroup$ Apr 24, 2020 at 22:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .