Reputation: 1329
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
Reputation:
Several solutions:
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)}
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)]
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