Dave
Dave

Reputation: 2073

How to rearrange sympy expressions containing a relational operator

I have expressions containing a relational operator, symbols, and constants. I'd like to rearrange the expressions so that (to the extent possible) all the constant terms are on one side of the relational operator, and the remaining terms on the other side. For example, I'd like to rearrange:

x - 5 > y - z

to:

x - y + z > 5

Is there an existing sympy method for doing this? If not, where should I start in extending sympy?

Upvotes: 4

Views: 1148

Answers (2)

smichr
smichr

Reputation: 19029

The omission of this from the canonical method was probably an oversight.

Perhaps the following would work for your else clause

r, l = (ineq.lhs - ineq.rhs).as_coeff_Add()
if r < 0:
    l, r = -r, -l
return Relational(l, r, ineq.rel_op).canonical

One might imagine that canonical would remove common factors, too, so 2x<4y would become x<2*y. SymPy would be open to a pull request implementing such changes.

Upvotes: 0

J Richard Snape
J Richard Snape

Reputation: 20344

Somewhat surprisingly, I couldn't find a way to do this "out of the box". You can use the method in this question to make any one variable the sole subject of the left hand side (LHS) of the inequality but I can't seem to make the constant term the subject.

So, I wrote my own version and reproduce it below. I've tested it on the example given and a couple of other examples. It tries to make the right hand side (RHS) consist of either zero or only constant terms based on an optional parameter. There may well be corner cases where it fails - use / modify with caution.

Code:

import sympy
from sympy.core.relational import Relational

mult_by_minus_one_map = {
    None: '==',
    '==': '==',
    'eq': '==',
    '!=': '!=',
    '<>': '!=',
    'ne': '!=',
    '>=': '<=',
    'ge': '<=',
    '<=': '>=',
    'le': '>=',
    '>': '<',
    'gt': '<',
    '<': '>',
    'lt': '>',
}

def move_inequality_constants(ineq, zero_on_right=False):
    l = ineq.lhs
    r = ineq.rhs
    op = ineq.rel_op
    all_on_left = l - r
    if zero_on_right:
        return Relational(all_on_left, sympy.sympify(0), op)
    else:
        coeff_dict = all_on_left.as_coefficients_dict()
        var_types = coeff_dict.keys()
        new_rhs = sympy.sympify(0)
        for s in var_types:
            if s == 1:
                all_on_left = all_on_left - coeff_dict[s]
                new_rhs = new_rhs - coeff_dict[s]
        if new_rhs < 0:
            all_on_left = all_on_left * -1
            new_rhs = new_rhs * -1
            op = mult_by_minus_one_map[op]
        return Relational(all_on_left,new_rhs,op)

# test code to demo function below    
from sympy.abc import x,y,z

test_ineqs = [ x - 5 > y - z,
               x**2 + x - 5 > y + x**2 - z,
               x + 5 > y - z,
               x**3 + y**2 >= x + 5*y - z - 15]

for k in test_ineqs:
    print('Re-arranging '+ str(k))
    kn = move_inequality_constants(k)
    print('Gives '+str(kn))
    print('Or equivalently ' + str(move_inequality_constants(k, True)))
    print('====')

Output:

Re-arranging x - 5 > y - z
Gives x - y + z > 5
Or equivalently x - y + z - 5 > 0
====
Re-arranging x**2 + x - 5 > x**2 + y - z
Gives x - y + z > 5
Or equivalently x - y + z - 5 > 0
====
Re-arranging x + 5 > y - z
Gives -x + y - z < 5
Or equivalently x - y + z + 5 > 0
====
Re-arranging x**3 + y**2 >= x + 5*y - z - 15
Gives -x**3 + x - y**2 + 5*y - z <= 15
Or equivalently x**3 - x + y**2 - 5*y + z + 15 >= 0

Upvotes: 1

Related Questions