Automatically simplify redundant arithmetic relations - python

I am looking for a way to automatically determine, e.g., that (a < 12) & (a < 3) & (c >= 4) is the same as (a < 3) & (c >= 4). I looked into Matlab's symbolic toolbox and SymPy in Python, but these are apparently only capable of simplifying purely Boolean logic (e.g., simplify(a & b | b & a) -> ans=(a & b))
Is there a way of using these symbolic math tools like described above?
Edit
As noted in the comment to #user12750353's answer, I would like to also simplify systems of relations that are concatenated with a Boolean OR, e.g., ((a < 12) & (a < 3) & (c >= 4)) | (a < 1).

SymPy sets can be used to do univariate simplification, e.g. ((x < 3) & (x < 5)).as_set() -> Interval.open(-oo, 3) and sets can be converted back to relationals. The following converts a complex expression to cnf form, separates args with respect to free symbols and simplifies those that are univariate while leaving multivariate arguments unchanged.
def f(eq):
from collections import defaultdict
from sympy import to_cnf, ordered
cnf = to_cnf(eq)
args = defaultdict(list)
for a in cnf.args:
args[tuple(ordered(a.free_symbols))].append(a)
_args = []
for k in args:
if len(k) == 1:
_args.append(cnf.func(*args[k]).as_set().as_relational(k[0]))
else:
_args.append(cnf.func(*args[k]))
return cnf.func(*_args)
For example:
>>> from sympy.abc import a, c
>>> f((a < 1) | ((c >= 4) & (a < 3) & (a < 12)))
(a < 3) & ((c >= 4) | (a < 1))

You can take a look in sympy inequality solvers for some options.
I could apply reduce_inequalities to your problem
from sympy.abc import a, c
import sympy.solvers.inequalities as neq
t = neq.reduce_inequalities([a < 12, a < 3, c >= 4])
And it results
(4 <= c) & (-oo < a) & (a < 3) & (c < oo)
It also works with some more complex examples
as long as you have one variable per inequality.

Related

A function works fine with one number, but not an array. What am I missing?

Pardon my ignorance, but I'm very new to coding in python. I have a pretty simple function; it just needs to make a calculation based on b's relative location to a and c:
a = 6
b = 3
c = 2
def function(a, b, c):
if ((a >= b) & (b >= c)):
return b - c
elif ((a <= b) & (b >= c)):
return a - c
else:
return 0
t = function(a, b, c)
print(t)
When I run it with simple numbers like above, it gives me the right answer no matter what I make b. (In this case 1)
But When I run it with a,b, and c as Numpy Arrays, it only returns b - c across the entire "t" array.
It's not too much different, but here's what I'm using for the array version:
def function(a, b, c):
if ((a >= b) & (b >= c)).any():
return b - c
elif ((a <= b) & (b >= c)).any():
return a - c
else:
return 0
t = function(a, b, c[i>1])
print(t)
(The [i>1] is there because there is a variable amount of array input, and another function will be used for when [i = 0])
I've also tried this:
t = np.where(((prev2 >= Head_ELV) & (Head_ELV >= Bottom_ELV)).any, Head_ELV - Bottom_ELV, 0)
but ran into the same result.
Would a while-loop work better?
I don't think you need looping here as the problem can be solved using array operations. You could try the below, assuming the arrays are of the same length.
# import numpy to be able to work with arrays
import numpy as np
def function(a, b, c):
# declare array t with only zeros
t = np.zeros_like(a)
# declare filters
mask_1 = (a >= b) * (b >= c)
mask_2 = (a <= b) * (b >= c)
# modifying t based on the filters above
t[mask_1] = (b - c)[mask_1]
t[mask_2] = (a - c)[mask_2]
return t
# example 2d arrays
a = np.array([[1800,5], [5,5]])
b = np.array([[3416,2], [3,4]])
c = np.array([[1714,2], [3,4]])
# run function
function(a, b, c)

Given 2 int values, return True if one is negative and other is positive

def logical_xor(a, b): # for example, -1 and 1
print (a < 0) # evaluates to True
print (b < 0) # evaluates to False
print (a < 0 != b < 0) # EVALUATES TO FALSE! why??? it's True != False
return (a < 0 != b < 0) # returns False when it should return True
print ( logical_xor(-1, 1) ) # returns FALSE!
# now for clarification
print ( True != False) # PRINTS TRUE!
Could someone explain what is happening? I'm trying to make a one liner:
lambda a, b: (a < 0 != b < 0)
All comparison operators in Python have the same precedence. In addition, Python does chained comparisons. Thus,
(a < 0 != b < 0)
breaks down as:
(a < 0) and (0 != b) and (b < 0)
If any one of these is false, the total result of the expression will be False.
What you want to do is evaluate each condition separately, like so:
(a < 0) != (b < 0)
Other variants, from comments:
(a < 0) is not (b < 0) # True and False are singletons so identity-comparison works
(a < 0) ^ (b < 0) # bitwise-xor does too, as long as both sides are boolean
(a ^ b < 0) # or you could directly bitwise-xor the integers;
# the sign bit will only be set if your condition holds
# this one fails when you mix ints and floats though
(a * b < 0) # perhaps most straightforward, just multiply them and check the sign
Your code doesn't work as intended because != takes higher precedence than a < 0 and b < 0. As itzmeontv suggests in his answer, you can simply decide the precedence yourself by surrounding logical components with parentheses:
(a < 0) != (b < 0)
Your code attempts to evaluate a < (0 != b) < 0
[EDIT]
As tzaman rightly points out, the operators have the same precedence, but your code is attempting to evaluate (a < 0) and (0 != b) and (b < 0). Surrounding your logical components with parentheses will resolve this:
(a < 0) != (b < 0)
Operator precedence: https://docs.python.org/3/reference/expressions.html#operator-precedence
Comparisons (i.a. chaining): https://docs.python.org/3/reference/expressions.html#not-in
You can use this
return (a < 0) != (b < 0)
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
So it becomes
(a < 0) and (0 != b) and (b < 0)
See https://docs.python.org/3/reference/expressions.html#not-in
In Python, comparison operators are of the same precedence, and they are non-associative. There is a separate rule for sequences of comparison operators, the chaining rule. Python documentation states about that:
if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.
Further, a op1 b and b op2 c and ... y opN z evaluates left to right.
a < 0 and 0 != b and b < 0
a < 0 will evaluated to False, and the further evaluation will be stopped due to short-circuit evaluation. So, the whole expression will be evaluated as False.

python sympy simplify and Eq

This may not be a question, just an observation, but is sympy supposed to work this way.
I have a two complicated expression, A and E, and I am trying to find out if they are equivalent. If I simplify one, say E, and use Eq(A,E) it does not return True, but the two separated with "==". If would have expected that sympy would be smart enough to work out they are equal. Eq(simplify(A),E) returns True. Here is the code ...
from sympy import *
B = symbols('B')
C = symbols('C')
F = symbols('F')
G = symbols('G')
H = symbols('H')
A = (B - C)*(G*(B + C) - (B - C - F)*H)**2
D = 2*(B**2+B*F-C**2)**2
E = A/D
ED=simplify(E*D)
print("E*D= {0}").format(str(ED))
print("A = {0}").format(str(A))
print("0 = {0}").format(str(simplify(A-ED)))
print("T = {0}").format(Eq(A,ED))
print("T = {0}").format(Eq(simplify(A),ED))
and the output
E*D= (B - C)*(G*(B + C) + H*(-B + C + F))**2
A = (B - C)*(G*(B + C) - H*(B - C - F))**2
0 = 0
T = (B - C)*(G*(B + C) - H*(B - C - F))**2 == (B - C)*(G*(B + C) + H*(-B + C + F))**2
T = True
Note the -H versus +H in the last expression.
Equality does not do any simplification and two objects are identical only if they are structurally (not mathematically) zero. Proving mathematical equality (in general) is not a simple problem so if they aren't identical (as in this case) SymPy doesn't even begin chasing the "equality rabbit" to its hole :-). This is the expected behavior. If you want to let SymPy try some simplification on its own, try using the equals method:
>>> A.equals(simplify(E*D))
True

Many numpy array manipulations in one array

I need to translate a matlab code to python numpy code
I have 4 2-dimension arrays (A, B, C and D) and want to create a new one "res".
the code in matlab
index1 = find(A == 2 & B > C);
index2 = find(A == 2 & B >= D & B <= C);
res = uint8(zeros(100,100));
res(index1) = 5;
res(index2) = 4;
in Python/numpy I figured out I can create new arrays like this:
res = ((A == 2) & (B > C)) * 5
res = ((A == 2) & (B >= D) & (B <= C)) * 4
but how can I combine this 2 results in only one array? Like the matlab code does.
The translation (in this case) is almost one-for-one:
index1 = ((A == 2) & (B > C))
index2 = ((A == 2) & (B >= D) & (B <= C))
res = np.zeros((100, 100), dtype='uint8')
res[index1] = 5
res[index2] = 4
Alternatively, you could define res as
res = np.where(A == 2, np.where(B > C, 5, np.where(B >= D, 4, 0)), 0)
though I don't think that helps readability :)
PS. As Mr. E suggests, mask1 might be a better variable name than index1 since index (usually) suggests integer values while mask suggest booleans, which is what we have here.

How do I find the difference between two values without knowing which is larger?

I was wondering if there was a function built into Python that can determine the distance between two rational numbers but without me telling it which number is larger.
e.g.
>>>distance(6,3)
3
>>>distance(3,6)
3
Obviously I could write a simple definition to calculate which is larger and then just do a simple subtraction:
def distance(x, y):
if x >= y:
result = x - y
else:
result = y - x
return result
but I'd rather not have to call a custom function like this.
From my limited experience I've often found Python has a built in function or a module that does exactly what you want and quicker than your code does it. Hopefully someone can tell me there is a built in function that can do this.
abs(x-y) will do exactly what you're looking for:
In [1]: abs(1-2)
Out[1]: 1
In [2]: abs(2-1)
Out[2]: 1
Although abs(x - y) and equivalently abs(y - x) work, the following one-liners also work:
math.dist((x,), (y,)) (available in Python ≥3.8)
math.fabs(x - y)
max(x - y, y - x)
-min(x - y, y - x)
max(x, y) - min(x, y)
(x - y) * math.copysign(1, x - y), or equivalently (d := x - y) * math.copysign(1, d) in Python ≥3.8
functools.reduce(operator.sub, sorted([x, y], reverse=True))
All of these return the euclidean distance(x, y).
Just use abs(x - y). This'll return the net difference between the two as a positive value, regardless of which value is larger.
If you have an array, you can also use numpy.diff:
import numpy as np
a = [1,5,6,8]
np.diff(a)
Out: array([4, 1, 2])
So simple just use abs((a) - (b)).
will work seamless without any additional care in signs(positive , negative)
def get_distance(p1,p2):
return abs((p1) - (p2))
get_distance(0,2)
2
get_distance(0,2)
2
get_distance(-2,0)
2
get_distance(2,-1)
3
get_distance(-2,-1)
1
use this function.
its the same convention you wanted.
using the simple abs feature of python.
also - sometimes the answers are so simple we miss them, its okay :)
>>> def distance(x,y):
return abs(x-y)
This does not address the original question, but I thought I would expand on the answer zinturs gave. If you would like to determine the appropriately-signed distance between any two numbers, you could use a custom function like this:
import math
def distance(a, b):
if (a == b):
return 0
elif (a < 0) and (b < 0) or (a > 0) and (b > 0):
if (a < b):
return (abs(abs(a) - abs(b)))
else:
return -(abs(abs(a) - abs(b)))
else:
return math.copysign((abs(a) + abs(b)),b)
print(distance(3,-5)) # -8
print(distance(-3,5)) # 8
print(distance(-3,-5)) # 2
print(distance(5,3)) # -2
print(distance(5,5)) # 0
print(distance(-5,3)) # 8
print(distance(5,-3)) # -8
Please share simpler or more pythonic approaches, if you have one.
If you plan to use the signed distance calculation snippet posted by phi (like I did) and your b might have value 0, you probably want to fix the code as described below:
import math
def distance(a, b):
if (a == b):
return 0
elif (a < 0) and (b < 0) or (a > 0) and (b >= 0): # fix: b >= 0 to cover case b == 0
if (a < b):
return (abs(abs(a) - abs(b)))
else:
return -(abs(abs(a) - abs(b)))
else:
return math.copysign((abs(a) + abs(b)),b)
The original snippet does not work correctly regarding sign when a > 0 and b == 0.
abs function is definitely not what you need as it is not calculating the distance. Try abs (-25+15) to see that it's not working. A distance between the numbers is 40 but the output will be 10. Because it's doing the math and then removing "minus" in front. I am using this custom function:
def distance(a, b):
if (a < 0) and (b < 0) or (a > 0) and (b > 0):
return abs( abs(a) - abs(b) )
if (a < 0) and (b > 0) or (a > 0) and (b < 0):
return abs( abs(a) + abs(b) )
print distance(-25, -15)
print distance(25, -15)
print distance(-25, 15)
print distance(25, 15)
You can try:
a=[0,1,2,3,4,5,6,7,8,9];
[abs(x[1]-x[0]) for x in zip(a[1:],a[:-1])]

Categories

Resources