How can one represent distinct non-numeric symbols in sympy? - python-3.x

I am experimenting with the representation of a trivial statistics problem in Sympy:
For a sample space S, there are 6 possible distinct outcomes
a,b,c,d,e,f. We can define event A as having occurred if any of
a,b,c have, and event B as having ocurred if any of b,c,d have.
Given a probability mass function pmf defined over S, what is the
probability of event A?
When attempting to implement this symbolically, I receive the following error:
~/project/.envs/dev/lib/python3.6/site-packages/sympy/stats/frv.py in _test(self, elem)
164 elif val.is_Equality:
165 return val.lhs == val.rhs
--> 166 raise ValueError("Undecidable if %s" % str(val))
167
168 def __contains__(self, other):
ValueError: Undecidable if Eq(d, a) | Eq(d, b) | Eq(d, c)
The problem is implemented as follows with comments on the failing lines of code:
from sympy import Eq, Function, symbols
from sympy.logic import Or
from sympy.sets import FiniteSet, Union
from sympy.stats import FiniteRV, P
# 1. Define a sample space S with outcomes: a,b,c,d,e,f; Define events A, B
A = FiniteSet(*symbols('a b c'))
B = FiniteSet(*symbols('b c d'))
S = Union(A, B, FiniteSet(*symbols('e f')))
display("Sample Space", S)
pmfFunc = Function("pmf")
pmfDict = {v: pmfFunc(v) for v in S}
X = FiniteRV('X', pmfDict)
a,b = symbols('a b')
# 2. P(X = a) = pmf(a)
display(P(Eq(X,a)))
# 3. A.as_relational(X) yields `(X=a) \lor (X=b) \lor (X=c)`
display(A.as_relational(X))
# 4. P(X = a \lor X = b) = pmf(a) + pmf(b)
# - Actual Output: ValueError: Undecidable if Eq(c, a) | Eq(c, b)
display(P(Or(Eq(X,a), Eq(X,b)))) # [FAILS]
# 5. P(A) = pmf(a) + pmf(b) + pmf(c)
# - Actual Output: ValueError: Undecidable if Eq(d, a) | Eq(d, b) | Eq(d, c)
display(P(A.as_relational(X))) # [FAILS]
I obtain expected output up to display(A.as_relational(X)):
Interpreting the failure message suggests that Sympy is unable to tell that the set members are distinct. Replacing the symbols with integers resolves the error and I get output similar to what I desire.
A = FiniteSet(1, 2, 3)
B = FiniteSet(2, 3, 4)
S = Union(A, B, FiniteSet(5, 6))
If I am not misunderstanding the error or the fundamental use of the library, is there a way to tell Sympy that a collection of symbols is entirely distinct? I have attempted to replace the symbols with Dummy instances without success, and I have also attempted to leverage the assumptions module without success:
facts = [Eq(a,b) if a is b else Not(Eq(a,b)) for a, b in itertools.product(S, S)]
with assuming(*facts):
I would like to avoid confusing mappings between integers and symbolic forms, as user error may not be apparent when the results are printed as latex. I am willing to adopt some burden in a workaround (e.g., as it would have been maintaining a collection of Dummy instances), but I have yet to find an acceptable workaround.

Interesting question. Maybe it can be done with with assuming(Ne(a,b), ...): context but I take a more pragmatic approach: replace symbols with cos(non-zero integer) which SymPy can easily distinguish as equal or not:
>>> reps = dict(zip(var('a:f'),(cos(i) for i in range(1,7))))
>>> ireps = {v:k for k,v in reps.items()}
>>> a,b,c,d,e,f = [reps[i] for i in var('a:f')]
Then remove your a, b = symbols... line and replace display(x) with display(x.subs(ireps) to get
('Sample Space', FiniteSet(a, b, c, d, e, f))
(pmf(a),)
(Eq(X, a) | Eq(X, b) | Eq(X, c),)
(pmf(a) + pmf(b),)
(I use cos(int) instead of int because I am not sure whether any computation would result in addition of two elements and I want to make sure they stay distinct.)
Another approach would be to define a constant class that derives from Symbol:
class con(Symbol):
def __hash__(self):
return id(self)
def __eq__(a,b):
if isinstance(b, con):
return a.name == b.name
_eval_Eq = __eq__
a,b,c,d,e,f=map(con,'abcdef')
display=lambda*x:print(x)
from sympy import Eq, Function, symbols
from sympy.logic import Or
from sympy.sets import FiniteSet, Union
from sympy.stats import FiniteRV, P
A = FiniteSet(a,b,c)
B = FiniteSet(b,c,d)
S = Union(A, B, FiniteSet(e,f))
pmfFunc = Function("pmf")
pmfDict = {v: pmfFunc(v) for v in S}
X = FiniteRV('X', pmfDict)
display("Sample Space", S)
display(P(Eq(X,a)))
display(A.as_relational(X))
display(P(Or(Eq(X,a), Eq(X,b))))
display(P(A.as_relational(X)))
gives
('Sample Space', FiniteSet(a, b, c, d, e, f))
(pmf(a),)
(Eq(X, a) | Eq(X, b) | Eq(X, c),)
(pmf(a) + pmf(b),)
(pmf(a) + pmf(b) + pmf(c),)

Related

Python - How to parse Boolean Sympy tree expressions to Boolean Z3Py expressions

I have some CNFs of Boolean expressions from the logic module in Sympy.
I get their Sympy expression trees with srepr() (see documentation).
Find below an example with two CNFs.
from sympy import Symbol
from sympy.logic.boolalg import And, Or, Not
# (a | ~c) & (a | ~e) & (c | e | ~a)
expr_1 = And(Or(Symbol('a'), Not(Symbol('c'))), Or(Symbol('a'), Not(Symbol('e'))), Or(Symbol('c'), Symbol('e'), Not(Symbol('a'))))
# (b | ~d) & (b | ~e) & (d | e | ~b)
expr_2 = And(Or(Symbol('b'), Not(Symbol('d'))), Or(Symbol('b'), Not(Symbol('e'))), Or(Symbol('d'), Symbol('e'), Not(Symbol('b'))))
I want to give those expression trees to a Z3Py solver as Boolean constraints.
For that, I think that need to:
transform sympy.Symbol() to z3.Bool(), and
transform the sympy logic operators to Z3 logic operators (e.g., sympy.logic.boolalg.And() to z3.And())
Then, I would add the constraints to a Z3 solver to output a solution.
If we continue with the example, as I see it, we would have the two following constraints (I wrote explicitly that I use Z3 Boolean operators to avoid confusion with the Sympy ones):
import z3 as z3
from z3 import Bool
const_1 = z3.And(z3.Or(Bool('a'), z3.Not(Bool('c'))), z3.Or(Bool('a'), z3.Not(Bool('e'))), z3.Or(Bool('c'), Bool('e'), z3.Not(Bool('a'))))
const_2 = z3.And(z3.Or(Bool('b'), z3.Not(Bool('d'))), z3.Or(Bool('b'), z3.Not(Bool('e'))), z3.Or(Bool('d'), Bool('e'), z3.Not(Bool('b'))))
How could we parse Sympy Boolean expression trees for Z3Py in an automated fashion? Is there a better way to do it than what I presented as an example?
You're on the right track. Essentially, you need to "compile" SymPy to Z3. This can be achieved in a variety of ways, but it's not a cheap/easy thing to do since you'd need to analyze large swaths of SymPy code. However, looks like your expressions are "simple" enough, so you can get away with a simple translator. Start by looking at how SymPy trees can be recursively processed: https://docs.sympy.org/latest/tutorials/intro-tutorial/manipulation.html#recursing-through-an-expression-tree
If you're in a hurry, you can use Axel's program, given in the other answer. Here's a version that is probably a bit more idiomatic and easier to extend and more robust:
import sympy
import z3
# Sympy vs Z3. Add more correspondences as necessary!
table = { sympy.logic.boolalg.And : z3.And
, sympy.logic.boolalg.Or : z3.Or
, sympy.logic.boolalg.Not : z3.Not
, sympy.logic.boolalg.Implies: z3.Implies
}
# Sympy vs Z3 Constants
constants = { sympy.logic.boolalg.BooleanTrue : z3.BoolVal(True)
, sympy.logic.boolalg.BooleanFalse: z3.BoolVal(False)
}
def compile_to_z3(exp):
"""Compile sympy expression to z3"""
pexp = sympy.parsing.sympy_parser.parse_expr(exp)
pvs = {v: z3.Bool(str(v)) for v in pexp.atoms() if type(v) not in constants}
def cvt(expr):
if expr in pvs:
return pvs[expr]
texpr = type(expr)
if texpr in constants:
return constants[texpr]
if texpr in table:
return table[texpr](*map(cvt, expr.args))
raise NameError("Unimplemented: " + str(expr))
return cvt(pexp)
if __name__ == '__main__':
z3.solve(compile_to_z3("false"))
z3.solve(compile_to_z3("a & ~b | c"))
z3.solve(compile_to_z3("false >> (a & ~b | c)"))
This prints:
no solution
[c = False, b = False, a = True]
[]
You can add new functions to table to extend it as you see fit.
I couldn't resist and implemented a basic converter.
from sympy import symbols
from sympy.parsing.sympy_parser import parse_expr
from z3 import *
# extract all variables and define a SymPy expression
def create_sympy_expression(expr):
declare_sympy_symbols(expr)
return parse_expr(expr)
# assume all single-character operands as SymPy variables
dicz3sym = {}
def declare_sympy_symbols(expr):
for c in expr:
if 'a' <= c <= 'z':
if not c in dicz3sym:
dicz3sym[c] = z3.Bool(c)
def transform_sympy_to_z3(exp):
params = [transform_sympy_to_z3(arg) for arg in exp.args]
func = str(exp.func)
if func == "And":
return z3.And(params)
elif func == "Not":
return z3.Not(params[0])
elif func == "Or":
return z3.Or(params)
elif exp.name in dicz3sym:
return dicz3sym[exp.name]
else:
raise NameError("unknown/unimplemented operator: " + func)
if __name__ == '__main__':
exp = create_sympy_expression("a & ~b | c")
z3exp = transform_sympy_to_z3(exp)
s = Solver()
s.add(z3exp)
if s.check() == sat:
m = s.model()
print("Solution found:")
for v in dicz3sym:
print(f"{v} = {m[dicz3sym[v]]}")
else:
print("No solution. Sorry!")

python 'concatenate' requires extra parentheses

I'm trying to concatenate 3 lists. When I try to use concatenate, like so, I get an error (TypeError: 'list' object cannot be interpreted as an integer):
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.concatenate(a, b, c)
But if I put "extra" parentheses, it works like so:
z = np.concatenate((a, b, c))
Why?
I am not sure what library you are using (concatenate is not a built-in python 3.x function). However, I'll explain what I think is going on.
When you call concatenate(a, b, c), the function concatenate is sent three parameters: a, b, and c. concatenate then performs some logic that is (presumably) not the desired behavior.
When you call concatenate((a, b, c)), a tuple (effectively a list that cannot be changed) is created with a value of (a, b, c), which is evaluated to ([1], [2], [3]). Then this tuple is passed to the concatenate function. The following code is actually equivalent to your second code snippet:
a = [1]
b = [2]
c = [3]
y = (a, b, c) # This evaluates to ([1], [2], [3]).
z = concatenate(y)
I hope I've explained this clearly enough. Here's an article that explains tuples in more depth, if I haven't: https://www.w3schools.com/python/python_tuples.asp
EDIT: Thanks for including the library. Here's the code for what you're probably trying to do:
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.array(a + b + c) # Lists can be concatenated using the `+` operator. Then, to make a numpy array, just call the constructor

python finding the greatest common divisor of two numbers program

I am a new Python learner. I am trying to finding the greatest common divisor of two numbers (a =1071 and b = 462 for example). I have written two programs for this. the first one is working but the second one gives the wrong answer. what is the problem with my program??
# first program (works)
a, b = 1071, 462
while b:
a, b = b, a % b
print(a)
# second program (doesn't work truly)
a = 1071
b = 462
while b:
a = b
b = a % b
print(a)
Explanation:
Yes, HSK is right. In the 2nd loop:
while b:
a = b
b = a % b
print(a)
First a is changed to b and then what you do is b = b% b. But here:
a, b = b, a % b
it is executed as one-line so a is still a.
Solution:
So just add a third variable:
a = 1071
b = 462
while b:
c = a
a = b
b = c % b
print(c)
One thing that distinguishes Python from other programming languages is that it is interpreted rather than compiled. This means that it is executed line by line.
The second doesn't work because, for the calculation of b, you need to use the old a, not the new a that got generated on the line before (this is actually set to b so you will get b % b, which will generally be zero). The equivalent to the first loop would be:
while b:
oldA = a
a = b
b = oldA % b
print(a)
The tuple assignment in Python can be considered an atomic operation where all the values on the right side are loaded up, then assigned to all the variables on the left side.
def divisor(n):
if n ==1 or n ==2 or n ==3:
return []
else:
result=[]
aux=2
while aux <= n:
if n % aux == 0:
result.append(aux)
aux=aux+1
return result
def func (m,n):
div1=divisor(m)
div2=divisor(n)
result =[]
for x in div1:
for y in div2:
if x == y:
result.append(x)
return result
print(func (x,y))

back solve for a variable using python

I'm trying to write a function to back solve for a variable from another function in python, kind of like what Excel solver does.
To simplify my example, I have a function takes in several variables then calculate a price. I will be passing actual values (a,b,c,d,x) into this function so it returns a numeric value.
def calc_price(a,b,c,d,x):
value = a+b*c-d + x
return value
Now I'm given a target price, and a,b,c,d. Only unknown is variable x, so I want to back solve variable x. I want to build this into a function that takes into the same variables as calc_price, with an additional variable target_price.
def solve(target_price, a,b,c,d):
#this function takes in values for target_price, a,b,c,d
#and should do something like this:
target_price = calc_price(a,b,c,d,x)
solve for x <---------this is the part I'm not sure how to do
return x
I created a function like this below to back solve the value x by a loop but it's inefficient in calculating large datasets, so I'm looking for a more efficient solution.
def solve(target_price,a,b,c,d):
x = 0.01
while x < 1:
if abs(target_price - calc_price(a,b,c,d,x)) < 0.001:
return x
x += 0.001
Thank you!
Consider this a demo (as your task is still a bit unclear to me) and make sure to read scipy's docs to learn about the basic guarantees these method provides.
One could argue, that an approach based on root-finding is more appropriate (we are minimizing a function here; therefore the abs-construction in the residual-function), but this approach here does not need you to give some bracketing-interval.
Code:
import numpy as np
from scipy.optimize import minimize_scalar
np.random.seed(0)
""" Utils """
def calc_price(x, a, b, c, d):
value = a+b*c-d + x
return value
def calc_price_res(x, target, a, b, c, d):
value = a+b*c-d + x
return abs(value - target) # we are looking for f(x) == 0
""" Create fake-data (typically the job of OP!) """
a, b, c, d, x = np.random.random(size=5)
fake_target = calc_price(x, a, b, c, d)
print('a, b, c, d: ', a, b, c, d)
print('real x: ', x)
print('target: ', fake_target)
print('noisy obj (just to be sure): ', calc_price_res(x, fake_target, a, b, c, d))
""" Solve """
res = minimize_scalar(calc_price_res, args=(fake_target, a, b, c, d))
print('optimized x: ', res.x)
print('optimized fun: ', res.fun)
Output:
a, b, c, d: 0.548813503927 0.715189366372 0.602763376072 0.544883182997
real x: 0.423654799339
target: 0.858675077275
noisy obj (just to be sure): 0.0
optimized x: 0.423654796297
optimized fun: 3.04165614917e-09

unique number of occurrance in a number range

I am interested in figuring out how many times each number occurs once in a numberrange of 5 digits, like 01234, 01243, 01324, etc ....
I know that the occurence is 120 unique times
At the moment I have programmed it with a lot of for loops and a lot of ands.
as you can see in the code
number = 5
for a in range(number):
for b in range(number):
for c in range(number):
for d in range(number):
for e in range(number):
if (a != b and a != c and a != d and a != e and
b != c and b != d and b != e and c != d and
c != e and d != e):
print ('{}{}{}{}{}'.format(a, b, c, d, e))
Is there a different and a nicer way to program the code above?
Greets,
Superfly
Someone suggested using itertools. Here is the code to use:
from itertools import permutations
#generator
factorials = permutations(set(range(5)))
for fact in factorials:
print(fact)
If you would not like to use itertools, but would like to generate every string, you can use a list comprehension to put it all in one line.
rn = range(number)
number= 5
sequences = [(a,b,c,d,e) for a in rn for b in rn for c in rn for d in rn for e in rn if a != b and a!= c and a!= d and a!= e and b!= c and b!=d and b!=e and c!=d and c!= e and d!= e]
Alternatively, you could define a method to recursively create new lists and append items
def rGenFactorials(L, leng=5, collected = [], restrictions=set()):
# base case
if len(L) == leng:
collected.append(L)
return
#induction
options = set(range(leng))
options -= restrictions
for op in options:
# copy restrictions
r = set(restrictions)
# add new restriction
r.add(op)
# copy list
l = L[:]
# append unused int
l.append(op)
# recursive invocation
rGenFactorials(L=l,collected = collected, restrictions=r)
C = []
rGenFactorials([], collected = C)
for c in C:
print(c)

Resources