user191919
user191919

Reputation: 754

Informing sympy about inequality between variables

I am trying to solve a system in Sympy of the form

max(a,b+c) == a^2

I would like for example, to tell Sympy to search for a solution where $max(a,b+c) = a$ and $max(a,b+c) = b+c$. Is that possible in some way? I trying doing it through solve and solving a system of inequalities as in:

import sympy as sp
b = sp.Symbol('b', finite = True)
c = sp.Symbol('c', finite = True)
eq = sp.Max(a,b+c) - a**2
sp.solve([eq, a > b+c], a)

But I get the error:

The inequality, Eq(-x**2 + Max(x, _b + _c), 0), cannot be solved using solve_univariate_inequality.

Is there anyway such type of equations can be solved? Or can I at least substitute $Max(a,b+c)$ to some case at least to simplify the expression?

Upvotes: 0

Views: 762

Answers (1)

Chris du Plessis
Chris du Plessis

Reputation: 1370

Option 1

SymPy struggles solving equations with Min and Max. It is a little bit better at solving Piecewise equalities but it is still not great. Here is how I would tackle this specific problem using rewrite(Piecewise):

from sympy import *

a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2
solution = solve(eq.rewrite(Piecewise), a)
print(solution)

This gives

[Piecewise((0, b <= -c), (nan, True)), Piecewise((1, b + c <= 1), (nan, True)), Piecewise((-sqrt(b + c), b + c > -sqrt(b + c)), (nan, True)), Piecewise((sqrt(b + c), b + c > sqrt(b + c)), (nan, True))]

enter image description here

So this tells you that SymPy found 4 solutions all conditional on what b and c are. They seem like valid solutions after plugging them in. I'm not sure if those are all the solutions though.

SymPy might struggle a lot more if equations are more complicated than this.

The solutions would probably look even better if you added positive=True instead of real=True in the code above. Always try to give as much information as possible when defining symbols.


Option 2

Another route for solving these equations would be by substituting Max(a, b+c) for a and keep in mind that those solutions are for a >= b+c and repeat for b+c >= a. This would probably work better for more complicated equations.

For this specific example can do so by doing something like:

from sympy import *

a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2

eq1 = eq.subs(Max(a, b+c), a)
solution1 = solveset(eq1, a)
eq2 = eq.subs(Max(a, b+c), b+c)
solution2 = solveset(eq2, a)

solution = Piecewise((solution1, a > b+c), (solution2, a < b+c), (solution1.union(solution2), True))
print(solution)

Giving the same answer as above but a bit more readable:

Piecewise((FiniteSet(0, 1), a > b + c), (FiniteSet(sqrt(b + c), -sqrt(b + c)), a < b + c), (FiniteSet(0, 1, sqrt(b + c), -sqrt(b + c)), True))

Notice how you need to know the arguments of the Max before hand and that there is only one Max. Combining conditions with more than 1 max will be difficult especially since both solutions hold when they are equal.

I suggest this option if you are solving equations interactively instead of an in an automated fashion.


Option 3

I haven't tested this one but I hope this provides the same answers in the more general case where you have multiple Max varying arguments for each Max. Each Max can only take in 2 arguments though.

from sympy import *

a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2

eqs = [eq]
conditions = [True]
for f in preorder_traversal(eq):
    new_eqs = []
    new_conds = []
    if f.func == Max:
        for equation, condition in zip(eqs, conditions):
            new_eqs.append(equation.subs(f, f.args[0]))
            new_conds.append(And(condition, f.args[0] >= f.args[1]))

            new_eqs.append(equation.subs(f, f.args[1]))
            new_conds.append(And(condition, f.args[0] <= f.args[1]))
        eqs = new_eqs
        conditions = new_conds
solutions = []
for equation in eqs:
    solutions.append(solveset(equation, a))

pieces = [(solution, condition) for solution, condition in zip(solutions, conditions)]
solution = Piecewise(*pieces)
print(solution)

This gives the same as above except for that last equality section:

Piecewise((FiniteSet(0, 1), a >= b + c), (FiniteSet(sqrt(b + c), -sqrt(b + c)), a <= b + c))

I could not combine both solutions when both of the inequalities hold so you just have to keep that in mind.

Upvotes: 2

Related Questions