Assuming a symbol is between zero and one with SymPy?
Asked Answered
T

2

6

I am doing symbolic manipulation of very large expressions in Python using SymPy. Most of the symbols that I am manipulating represent nonnegative, real numbers, less than or equal to one.

How can I tell SymPy these assumptions? I have figured out that I can do the following when creating the symbol.

import sympy as sym

x = sym.symbols('x', real=True, nonnegative=True)

but I don't see how to impose the upper bound of one.

Towhaired answered 24/2, 2018 at 3:49 Comment(0)
I
10

Unfortunately, the currently implemented system of assumptions does not provide a way to impose such a bound. For some purposes, it may be reasonable to introduce algebraic structure that implies the bound: for example,

t = sym.symbols('t', nonnegative=True)
x = t/(1+t)

Now SymPy knows that x is between 0 and 1:

>>> x < 1
True
>>> x >= 0
True

Whether this is helpful depends on how natural this substitution for the expressions you are working with. Another option is x = sym.exp(-t)

Ingratitude answered 24/2, 2018 at 5:18 Comment(1)
Great idea, although simplified expressions will of course be given in terms of t rather than x, which is not desirable. And awkwardly, simplify using x=exp(-t) will expand into an expression from which it cannot thereafter recognise 0<x<1Embosom
E
0

Here is a trick similar in spirit to the accepted answer, but which retains the original symbol in the expression.

Consider:

from sympy import symbols, sqrt, conjugate, Q

x = symbols('x', real=True, nonnegative=True)

expr = sqrt(1-x) * conjugate( sqrt(1-x) )

Assuming 0 <= x <= 1, we know that expr would simplify to 1-x but we cannot directly make use of this assumptiom in sympy:

expr.simplify()
>>> sqrt(1 - x)*conjugate(sqrt(1 - x))

expr.refine(Q.positive(1-x))
>>> sqrt(1 - x)*conjugate(sqrt(1 - x))

We wish to communicate to sympy that 1-x is always positive or zero. If we could replace 1-x with a symbol y declared as nonnegative=True, then simplify() could recognise sqrt(1-x) = sqrt(y) is real and simplify away the conjugate and product.

We can do just that, and we don't even need to use a new symbol! We substitute x -> 1-x so that sub-expressions f(1-x) become f(x) which can now be simplified, leveraging that their parameter is positive. Then, we restore 1-x -> x by again substituting x -> 1-x.

expr = expr.subs(x, 1-x).simplify().subs(x, 1-x)
>>> 1 - x

This seems safe; our substitution is mathematically equivalent to asserting that x <= 1, which coupled with our explicit sympy assumption x >= 0, achieves the intended bound.

We can easily extend this to simplify expressions assuming 0 <= x <= b for a positive bound b:

expr = expr.subs(x, b-x).simplify().subs(x, b-x)
Embosom answered 9/3 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.