l7ll7
l7ll7

Reputation: 1329

Converting symbols generated with "cls=sympy.Function" parameter to normal symbols

I want to solve a nonlinear system of equations with the help ofsympy. Unfortunately, this is my setup

import sympy
x = sympy.Symbol('x')
f,g,h= sympy.symbols("f g h", cls=sympy.Function)
#here I do some computations to arrive at the equations below
eq1 = 2*f(x)*g(x)-h(x)
eq2 = f(x)**2+h(x)*g(x)
system = [eq1,eq2]
unknowns = [f(x),h(x)]

I would like to solve this toy example for f(x) and h(x), but sypmy.nonlinsolve(system,unknowns) does not work, since it expects the system and unknown to be of symbol and not Function type.

Is there an easy way to convert the system and the unknowns to such types? By this I mean, if the code above would have been

import sympy
x = sympy.Symbol('x')
f,g,h= sympy.symbols("f g h")
#here I do some computations to arrive at the equations below
eq1 = 2*f*g-h
eq2 = f**2+h*g
system = [eq1,eq2]
unknowns = [f,h]

this would have worked perfectly. How can I transform f,g,h to such types?

(Using this second code is not an option, since I arrive at those equations by using differentiation, so I need the cls=sympy.Function parameter; alternatively, if anybody knows how to do differentiation with symbols, so that I would be able to use the second code version, that would also work.)

Upvotes: 1

Views: 727

Answers (1)

user6655984
user6655984

Reputation:

Several solutions:

Use dev version of SymPy

In the development version (GitHub) this works out of the box; it replaces functions by symbols internally.

>>> sympy.nonlinsolve(system, unknowns)
{(0, 0), (-2*g(x)**2, -4*g(x)**3)}

Use solve

solve does the function-symbol swap already in the current release SymPy 1.1.1.

sympy.solve(system, unknowns)  

returns [(0, 0), (-2*g(x)**2, -4*g(x)**3)]

Borrow recast_to_symbols from the future of SymPy

In the development version the replacement logic is isolated into recast_to_symbols function.

from sympy.solvers.solvers import recast_to_symbols
e, s, d = recast_to_symbols(systems, unknowns)
print(e, s, d)

prints

[2*_X0*g(x) - _X1, _X0**2 + _X1*g(x)] [_X0, _X1] {_X0: f(x), _X1: h(x)}

Here e is the new equations, s the new symbols, and d the dictionary for restoring original things. The idea is that one can use

sympy.nonlinsolve(e, s).xreplace(d)

to get the solution. (Actually, your example xreplace is not even needed because the solution does not contain any of the replaced symbols.)

If you don't want to use the development version, an easy solution is to copy the function from the source on GitHub:

def recast_to_symbols(eqs, symbols):
    """Return (e, s, d) where e and s are versions of eqs and
    symbols in which any non-Symbol objects in symbols have
    been replaced with generic Dummy symbols and d is a dictionary
    that can be used to restore the original expressions.
    Examples
    ========
    >>> from sympy.solvers.solvers import recast_to_symbols
    >>> from sympy import symbols, Function
    >>> x, y = symbols('x y')
    >>> fx = Function('f')(x)
    >>> eqs, syms = [fx + 1, x, y], [fx, y]
    >>> e, s, d = recast_to_symbols(eqs, syms); (e, s, d)
    ([_X0 + 1, x, y], [_X0, y], {_X0: f(x)})
    The original equations and symbols can be restored using d:
    >>> assert [i.xreplace(d) for i in eqs] == eqs
    >>> assert [d.get(i, i) for i in s] == syms
    """

    from sympy import Dummy, Symbol
    from sympy.core.compatibility import iterable

    if not iterable(eqs) and iterable(symbols):
        raise ValueError('Both eqs and symbols must be iterable')
    new_symbols = list(symbols)
    swap_sym = {}
    for i, s in enumerate(symbols):
        if not isinstance(s, Symbol) and s not in swap_sym:
            swap_sym[s] = Dummy('X%d' % i)
            new_symbols[i] = swap_sym[s]
    new_f = []
    for i in eqs:
        try:
            new_f.append(i.subs(swap_sym))
        except AttributeError:
            new_f.append(i)
    swap_sym = {v: k for k, v in swap_sym.items()}
    return new_f, new_symbols, swap_sym

Upvotes: 1

Related Questions