How does "substituting $x=x$" change the behavior of this Sage function?

69 Views Asked by At

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.

1

There are 1 best solutions below

2
On BEST ANSWER

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.