jonk
jonk

Reputation: 225

Sympy doesn't find a solution for a system of four equations when I already know the solution exists

I already know that the solution to a system of four equations are these:

The SymPy code I'm using:

z1, z2, w1, w2 = symbols( 'z1, z2, w1, w2', real=True )
ex1 = 2 * (z1*w1 + z2*w2)
ex2 = w1**2 + w2**2 + 4*z1*z2*w1*w2
ex3 = 2*w1*w2 * (z1*w2 + z2*w1)
ex4 = w1**2 * w2**2
eq1 = Eq( ex1, 10 )
eq2 = Eq( ex2, 45 )
eq3 = Eq( ex3, 105 )
eq4 = Eq( ex4, 105 )
solve( [ eq1, eq2, eq3, eq4 ], [ z1, z2, w1, w2 ] )
[]                      # Note that the result is empty.

I can demonstrate the solution I have, though:

solution = { z1:0.620702965049498, z2:0.957974461859108, w1:3.38936579272158, w2:3.02326493881663 }
for i in [ ex1, ex2, ex3, ex4 ]: i.subs( solution )
9.99999999999999        # Very close to 10 in eq1
44.9999999999999        # Very close to 45 in eq2
105.000000000000        # Matches 105 in eq3
105.000000000000        # Matches 105 in eq4

I'm wondering what I may have done wrong in setting this up for SymPy. Or if there's a known solver problem? (My SymPy version is '1.10.1'.)

Note: I'm working on the expression of a homogeneous solution for a 4th order Bessel equation as used in electronics filter design, where I've factored it into two 2nd order expressions.

Upvotes: 1

Views: 248

Answers (2)

smichr
smichr

Reputation: 19135

Oscar has given several nice answers using the Groebner basis; beside the one above see also this one. If you apply that approach to your equations you will find that there are 8 solutions (but mainly 2, with permutations of signs on terms...and mainly 1 when you consider symmetry of exchanging z1 and z2 while exchanging w1 and w2).

First compute the basis of the equations in form f(X) = 0; X = (z1,z2,w1,w2)

from sympy import *
...
...your work from above
...
F = Tuple(*[i.rewrite(Add) for i in (eq1,eq2,eq3,eq4)])
X = (z1,z2,w1,w2)
G, Y = groebner(F, X)
G = Tuple(*G)  # to make substitution easier below

G[-1] is a 12th order polynomial in w2 for which the real roots can be found: there are 4.

w2is = real_roots(G[-1])
assert len(w2is) == 4  # [i.n(3) for i in w2is] -> [-3.39, -3.02, 3.02, 3.39]

G[0] and G[1] are linear in z1 and z2, respectively, and G[2] is quadratic in w1. So substituting in a value for w2 and solving should give 2 solutions for each value of w2 for a total of 8 solutions.

sols = []
for _w2 in w2is:
    for si in solve(G[:-1].subs(w2,_w2.n()), dict=True):
        si.update({w2:_w2})
        sols.append(si)
        print({k: v.n(2) for k,v in si.items()})
{w1: -3.0, z1: -0.96, z2: -0.62, w2: -3.4}
{w1: 3.0, z1: 0.96, z2: -0.62, w2: -3.4}
{w1: -3.4, z1: -0.62, z2: -0.96, w2: -3.0}
{w1: 3.4, z1: 0.62, z2: -0.96, w2: -3.0}
{w1: -3.4, z1: -0.62, z2: 0.96, w2: 3.0}
{w1: 3.4, z1: 0.62, z2: 0.96, w2: 3.0}
{w1: -3.0, z1: -0.96, z2: 0.62, w2: 3.4}
{w1: 3.0, z1: 0.96, z2: 0.62, w2: 3.4}

Upvotes: 2

Davide_sd
Davide_sd

Reputation: 13185

Since you are looking for numerical solutions, you probably don't need SymPy.

Here I'm going to use scipy's fsolve to solve your system of non-linear equations:

from scipy.optimize import fsolve
# convert your equations to numerical functions
eqns = [lambdify([z1, z2, w1, w2], eq.rewrite(Add)) for eq in [eq1, eq2, eq3, eq4]]

def func(p):
    # p contains the current iteration parameters: z1, z2, w1, w2
    return [eq(*p) for eq in eqns]

sol = fsolve(func, (1, 1, 1, 1))
print(sol)
# [0.62070297 0.95797446 3.38936579 3.02326494]

Note that you can also use Sympy's nsolve to solve a system of equations for numerical values, but you need to provide some good initial guess, which for your case might not be trivial:

nsolve([eq1, eq2, eq3, eq4], [z1, z2, w1, w2], [0.5, 1, 3, 3])

Upvotes: 2

Related Questions