Symbolic conditional sum - python

Is there a way to calculate, or at least state properly, a symbolic sum which iterates over a sequence conditionally?
For example, if a(n) is a sequence (of reals) and c(n) is a "condition" (boolean function defined on the integers), then I wish to compute, or at least state, the sum over all a(n)'s for which c(n).
Formally, I'd like to do something like this:
n = Symbol('n', integer=True, positive=True)
a = 1 / n**2
c = Eq(n%3, 1)
## s = Sum(a, (n, 0, oo), condition=c)
So s should be the following sum:
1/1 + 1/16 + 1/49 + 1/100 + 1/169 + ...
Of course, in this case I can define s manually, but I wonder whether I can do it automatically somehow.
Edit:
By manually I mean
Sum(1/(3*n+1)**2, (n, 0, oo))
This can be evaluated (and it is about 1.12173301393634).
An attempt I've made (which failed) is as follows:
Sum(Eq(n%3, 1) * (1/n**2), (n, 0, oo))
Trying to evaluate this using .evalf() resulted
AttributeError: 'BooleanFalse' object has no attribute '_eval_evalf'
Edit (2):
Here's another attempt, which yields a wrong result:
p = Piecewise((1/(n**2), Eq(n%3, 1)), (0, True))
Sum(p, (n, 0, oo)).evalf()
## returns 1.00000000000000
Either I've done something wrong, or this is a sympy bug.

Using a Piecewise is definitely the correct way to do this. The wrong result is a bug in SymPy, which you should report at https://github.com/sympy/sympy/issues/new. Your second method won't work because in SymPy booleans are not implicitly integers (True and False are not 1 and 0).

Or avoid piecewise by defining your function as 1/(3*n+1)**2
>>> Sum((3*n + 1)**(-2), (n, 0, oo))
Sum((3*n + 1)**(-2), (n, 0, oo))
>>> print filldedent(_.doit())
exp(-4*I*pi/3)*polylog(2, exp_polar(4*I*pi/3))/3 + pi**2/18 +
exp(-2*I*pi/3)*polylog(2, exp_polar(2*I*pi/3))/3
>>> Sum((3*n + 1)**(-2), (n, 0, oo)).n()
1.12173301393634
You can see that the remapping works by using Symbol-trickery. e.g. the 1st 6 terms:
>>> Add(*[(1/n).subs(n,Symbol(str((3*i+1)**2))) for i in range(5)])
1/49 + 1/169 + 1/16 + 1/100 + 1/1

If it's a linear sequence then you could use Integal's transform method to do the transformation for you:
>>> i=Integral(1/x**2,(x,1,oo))
>>> i.transform(x,3*x - 2)
Integral(3/(3*x - 2)**2, (x, 1, oo))
>>> (Sum(*_.args)/3).n(3) # divide by 3 to handle renorm
1.12

Related

Simplifying sums of symbolic min terms

I'm new to SymPy, so this might actually be easy, but I couldn't find anything in the documentation that quite matched. I'm trying to simplify the following expression:
>>> from sympy import *
>>> i, N = symbols('i N', integer=True, negative=False)
>>> summation(Min(N-8*i, 8), (i, 0, ceiling(N/8)-1)).simplify()
Sum(Min(8, N - 8*i), (i, 0, ceiling(N/8) - 1))
This could simplify to just N (and is true in general for a positive k, in place of 8). Is there some way I can get this to happen?

(Python) Solving an equation with an unknown number, find that number with an equation

I making a code (for fun and practice) to balance chemical equations. I want to try and balance N + A so it = Z
N = 2
A = 2
Z = 6
if N + A != Z:
print('X')
balancer = ???
The balancer should be 3 so that if I make an equation e.g (balancer x N) + A = Z it would be true. How would I make the balancer be three with out directly inputing it.
Thanks :)
You can to do the basic algebra by hand:
(balancer * N) + A = Z
(balancer * N) = Z - A # subtract A from both sides
balancer = (Z - A) / N # divide both sides by N
… and then it's trivial to turn that into code—that last line actually is the valid Python code, with no changes.
Or, if you want Python to do this for you, just by specifying (balancer * N) + A = Z as an equation… Python doesn't have anything built in to do that, but there are algebra libraries like SymPy to do.
You'll really want to work through the whole tutorial, but briefly…
First, you have to first tell it that your variables are variables:
>>> from sympy import symbols, solve, Eq
>>> A, N, Z, balancer = symbols('A N Z balancer')
Then, build an equation. The left side can just be (balancer * N) + a and Z, but you can't just put an = or == between them; you have to use Eq:
>>> equation = Eq((balancer * N) + A, Z)
Now you can substitute in the values for your variables:
>>> equation.subs(dict(N=2, A=2, Z=6))
Eq(2*balancer + 2, 6)
And finally, solve for valid solutions:
>>> solve(equation.subs(dict(N=2, A=2, Z=6))
[2]
Or, if you'd prefer to solve it algebraically and then substitute, instead of the other way around:
>>> solve(equation, 'balancer')
[(-A + Z)/N]
>>> [s.subs(dict(N=2, A=2, Z=6)) for s in solve(equation, 'balancer')]
[2]
You need a condition to test whether the left side, N + A, is greater than or less than than the right side, Z. You could use (N + A) - Z, yielding -2, which tells you that you're missing two atoms. From there you'll need to write some logic to determine which atoms it is that you're missing.
With simple variables pointing to integers, there's no way to intuitively predict which atoms you'll need to add. Presumably you're working from an equation, though, so I'd suggest you look into a regex solution to that parse that. Something like this:
>>> import re
>>> m = re.findall('(\d*)((?:[A-Z][a-z]?\d*)+)', '2CH4 + O2')
>>> for n, molecule in m:
... print(n or 1, molecule)
...
2 CH4
1 O2
And then parse the atoms similarly from there.

Modulo computations in Sympy fail

I have a computation in Sympy, which I want to be modulo 2.
However, if I just write
from sympy.abc import x
(((x + 1) % 2) + 1) % 2
Then this has the output Mod(Mod(x + 1, 2) + 1, 2), i.e. the two +1 do not vanish. Even calling .simplify() does not have the desired effect.
Another post (Modulo 2 arithmetics in SymPy) suggested sympy.trunc. But this function does not work if the expression is integer.
I even tried sympy.trunc(sympy.sympify(1),2), but this still raised an Error.
So how do I get a modulo function that works on integers and all coefficients?
PS: I am aware that this is a basic question, and I was confused, that I could not find an answer.
A better way to handle this is to use Poly with the modulus flag:
>>> Poly(x + 1, x, modulus=2) + Poly(1, x, modulus=2)
Poly(x, x, modulus=2)

Python sympy - simplify nonzero factors from an equation

I am using the sympy library for python3, and I am handling equations, such as the following one:
a, b = symbols('a b', positive = True)
my_equation = Eq((2 * a + b) * (a - b) / 2, 0)
my_equations gets printed exactly as I have defined it ((2 * a + b) * (a - b) / 2 == 0, that is), and I am unable to reduce it even using simplify or similar functions.
What I am trying to achieve is simplifying the nonzero factors from the equation (2 * a + b and 1 / 2); ideally, I'd want to be able to simplify a - b as well, if I am sure that a != b.
Is there any way I can reach this goal?
The point is that simplify() is not capable (yet) of complex reasoning about assumptions. I tested it on Wolfram Mathematica's simplify, and it works. It looks like it's a missing feature in SymPy.
Anyway, I propose a function to do what you're looking for.
Define this function:
def simplify_eq_with_assumptions(eq):
assert eq.rhs == 0 # assert that right-hand side is zero
assert type(eq.lhs) == Mul # assert that left-hand side is a multipl.
newargs = [] # define a list of new multiplication factors.
for arg in eq.lhs.args:
if arg.is_positive:
continue # arg is positive, let's skip it.
newargs.append(arg)
# rebuild the equality with the new arguments:
return Eq(eq.lhs.func(*newargs), 0)
Now you can call:
In [5]: simplify_eq_with_assumptions(my_equation)
Out[5]: a - b = 0
You can easily adapt this function to your needs. Hopefully, in some future version of SymPy it will be sufficient to call simplify.

numpy array to list conversion issue

For some reason, evalRow(list(array([0, 1, 0, 0, 0]))) and evalRow([0, 1, 0, 0, 0]) give different results. However if I use magicConvert (here to debug this) instead of list to go from numpy array to list it works as expected:
def magicConvert(a):
ss = str(list(a))[1:-1]
return map(int, ss.split(","))
# You don't actually need to read these functions, just here to reproduce the error:
from itertools import *
def evalRow(r):
grouped = map(
lambda (v, l): (v, len(tuple(l))),
groupby(chain([2], r, [2])))
result = 0
for player in (1, -1):
for (pre, mid, post) in allTuples(grouped, 3):
if mid[0] == player:
result += player * streakScore(mid[1], (pre[0] == 0) + (post[0] == 0))
return result
def streakScore(size, blanks):
return 0 if blanks == 0 else (
100 ** (size - 1) * (1 if blanks == 1 else 10))
def allTuples(l, size):
return map(lambda i: l[i : i + size], xrange(len(l) - size + 1))
The difference in the behaviour is due to the fact that doing list(some_array) returns a list of numpy.int64, while, doing the conversion via the string representation (or equivalently using the tolist() method) returns a list of python's ints:
In [21]: import numpy as np
In [22]: ar = np.array([1,2,3])
In [23]: list(ar)
Out[23]: [1, 2, 3]
In [24]: type(list(ar)[0])
Out[24]: numpy.int64
In [25]: type(ar.tolist()[0])
Out[25]: builtins.int
I believe the culprit is the 100 ** (size - 1) part of your code:
In [26]: 100 ** (np.int64(50) - 1)
Out[26]: 0
In [27]: 100 ** (50 - 1)
Out[27]: 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
In [28]: type(100 ** (np.int64(50) - 1))
Out[28]: numpy.int64
What you see is the int64 overflowing, hence the result of the exponentiation are essentially "random", while python's ints have unlimited range and give the correct result.
To summary:
If you want to convert between numpy and python data types use the proper methods, in this case array.tolist()
Remember that numpys data types have limited range, hence you should check for overflows and expect strange results in other situations. If you do not use the proper methods for conversion you might end up using numpy data types when you didn't expect (as in this case).
Never assume it's a bug in python/numpy/a very widely used library. The chances to find a bug in such trivial cases in such well-tested and widely used softwares is really small. If the program gives unexpected results, 99.999% of the times it's because you are doing something wrong. So, before blaming on others try to check step by step what your program is doing.
I tested it and it gave me differnet results. Don't ask me why, maybe a bug?
Anyway always use the tolist() function to convert a numpy array to a list.
evalRow(array([0, 1, 0, 0, 0]).tolist()) == evalRow([0, 1, 0, 0, 0])
#output: True

Categories

Resources