5

I have two univariate functions, f(x) and g(x), and I'd like to substitute g(x) = y to rewrite f(x) as some f2(y).

Here is a simple example that works:

In [240]: x = Symbol('x')

In [241]: y = Symbol('y')

In [242]: f = abs(x)**2 + 6*abs(x) + 5

In [243]: g = abs(x)

In [244]: f.subs({g: y})
Out[244]: y**2 + 6*y + 5

But now, if I try a slightly more complex example, it fails:

In [245]: h = abs(x) + 1

In [246]: f.subs({h: y})
Out[246]: Abs(x)**2 + 6*Abs(x) + 5

Is there a general approach that works for this problem?

dshin
  • 2,354
  • 19
  • 29

1 Answers1

5

The expression abs(x)**2 + 6*abs(x) + 5 does not actually contain abs(x) + 1 anywhere, so there is nothing to substitute for.

One can imagine changing it to abs(x)**2 + 5*(abs(x) + 1) + abs(x), with the substitution result being abs(x)**2 + 5*y + abs(x). Or maybe changing it to abs(x)**2 + 6*(abs(x) + 1) - 1, with the result being abs(x)**2 + 6*y - 1. There are other choices too. What should the result be?

There is no general approach to this task because it's not a well-defined task to begin with.

In contrast, the substitution f.subs(abs(x), y-1) is a clear instruction to replace all occurrences of abs(x) in the expression tree with y-1. It returns 6*y + (y - 1)**2 - 1.

The substitution above of abs(x) + 1 in abs(x)**2 + 6*abs(x) + 5 is a clear instruction too: to find exact occurrences of the expression abs(x) + 1 in the syntax tree of the expression abs(x)**2 + 6*abs(x) + 5, and replace those subtrees with the syntax tree of the expression abs(x) + 1. There is a caveat about heuristics though.

Aside: in addition to subs SymPy has a method .replace which supports wildcards, but I don't expect it to help here. In my experience, it is overeager to replace:

>>> a = Wild('a')
>>> b = Wild('b')
>>> f.replace(a*(abs(x) + 1) + b, a*y + b)
5*y/(Abs(x) + 1) + 6*y*Abs(x*y)/(Abs(x) + 1)**2 + (Abs(x*y)/(Abs(x) + 1))**(2*y/(Abs(x) + 1))   

Eliminate a variable

There is no "eliminate" in SymPy. One can attempt to emulate it with solve by introducing another variable, e.g.,

fn = Symbol('fn')
solve([Eq(fn,  f), Eq(abs(x) + 1, y)], [fn, x])

which attempts to solve for "fn" and "x", and therefore the solution for "fn" is an expression without x. If this works

In fact, it does not work with abs(); solving for something that sits inside an absolute value is not implemented in SymPy. Here is a workaround.

fn, ax = symbols('fn ax')
solve([Eq(fn,  f.subs(abs(x), ax)), Eq(ax + 1, y)], [fn, ax]) 

This outputs [(y*(y + 4), y - 1)] where the first term is what you want; a solution for fn.

0 _
  • 10,524
  • 11
  • 77
  • 109
  • The result I desire is an expression that eliminates `x` entirely. I don't necessarily expect `subs()` to be the right function for that purpose; my question is whether there is any way to tell sympy to give me an expression that doesn't contain `x`. – dshin Jul 28 '17 at 22:52
  • Thanks. The workaround works here, but sounds like there is not a general approach. – dshin Aug 01 '17 at 15:35