How to not double count sympy numbers - python

I need for a project to calculate some sort of simplicity of some expressions and among other things I need to assign to each number appearing in the expression a complexity (something like log2(x), such that the complexity is measured in bits, i.e. log2(4)=2 bits). Anyway, for a give sympy expression I have this piece of code:
is_atomic_number = lambda expr: expr.is_Atom and expr.is_number
eq_numbers = [subexpression for subexpression in preorder_traversal(expr) if is_atomic_number(subexpression)]
It mainly works, but if I have something of the form 2*(x+y), that returns as the numbers appearing in the expression (2,2) instead of just 2, which is what I want. Is there a way to make sure that when something gets factored out, I count it only once? Thank you!

The atoms method might be what you want:
>>> (2*(x+y)).atoms(Number)
{2}
The replace method has the option too keep track of the mapping used, but it doesn't handle identical items that were replaced by different values
>>> (2*x + x**2).replace(lambda x:x.is_Number, lambda x: Dummy(), map=True)
(Dummy_1*x + x**Dummy_2, {2: Dummy_2})
So the following might work:
>>> def flatargs(expr):
... if expr.is_Atom: yield expr
... else:
... for a in expr.args:
... for i in flatargs(a): yield i
>>> list(flatargs(2*x+x**2))
[x, 2, 2, x]
>>> from sympy.utilities.iterables import multiset
>>> multiset([i for i in flatargs(2*x+x**2+y/2) if i.is_Number)
{1/2: 1, 2: 2}
If you want the numerator and denominator separately from flatargs you would have to handle the Rational atom as a special case and deal with the cases when numerator or denomitor is 1. But hopefully what is here will get you on your way.

Related

Turn floats to integers in sympy

Is there a fast way to turn floats that are actually integers, to integers in python? For example: 2.0*x+3.1, I would like to appear as 2*x+3.1. I could loop through the numbers in the expression and check one by one if x = int(x) and then replace them (or something like that), but I was wondering if there is a faster, built-in method to do so.
Thank you!
There are a few ways to do this. You could nsimplify(expr, rational=True) but that will change the 3.1 to 31/10 and then you would have to undo that. The use of replace does not replace thing which are tested as equal. So the only one-pass solution that I know of is a custom function:
>>> def intify(expr):
... floats = S(expr).atoms(Float)
... ints = [i for i in floats if int(i) == i]
... return expr.xreplace(dict(zip(ints, [int(i) for i in ints])))
...
>>> intify(2.0*x+3.1)
2*x + 3.1

Efficiently calculating mathematical formulas with exponents

I'm implementing a program that calculates an equation: F(n) = F(n-1) + 'a' + func1(func2(F(n-1))).
func1 takes every 'a' and makes it 'c' and every 'c' becomes 'a'.
func2 reverses the string (e.x. "xyz" becomes "zyx").
I want to calculate the Kth character of F(10**2017).
The basic rules are F(0) = "" (empty string), and examples are F(1) = "a", F(2) = "aac", and so on.
How do I do this efficiently?
The basic part of my code is this:
def op1 (str1):
if str1 == 'a':
return 'c'
else:
return 'a'
def op2 (str2):
return str2[::-1]
sinitial = ''
while (counter < 10**2017):
Finitial = Finitial + 'a' + op1(op2(Finitial))
counter += 1
print Finitial
Let's start by fixing your original code and defining a function to compute F(n) for small n. We'll also print out the first few values of F. All code below is for Python 3; if you're using Python 2, you'll need to make some minor changes, like replacing str.maketrans with string.maketrans and range with xrange.
swap_ac = str.maketrans({ord('a'): 'c', ord('c'): 'a'})
def F(n):
s = ''
for _ in range(n):
s = s + 'a' + s[::-1].translate(swap_ac)
return s
for n in range(7):
print("F({}) = {!r}".format(n, F(n)))
This gives the following output:
F(0) = ''
F(1) = 'a'
F(2) = 'aac'
F(3) = 'aacaacc'
F(4) = 'aacaaccaaaccacc'
F(5) = 'aacaaccaaaccaccaaacaacccaaccacc'
F(6) = 'aacaaccaaaccaccaaacaacccaaccaccaaacaaccaaaccacccaacaacccaaccacc'
A couple of observations at this point:
F(n) is a string of length 2**n-1. That means that F(n) grows fast. Computing F(50) would already require some serious hardware: even if we stored one character per bit, we'd need over 100 terabytes to store the full string. F(200) has more characters than there are estimated atoms in the solar system. So the idea of computing F(10**2017) directly is laughable: we need a different approach.
By construction, each F(n) is a prefix of F(n+1). So what we really have is a well-defined infinite string, where each F(n) merely gives us the first 2**n-1 characters of that infinite string, and we're looking to compute its kth character. And for any practical purpose, F(10**2017) might as well be that infinite string: for example, when we do our computation, we don't need to check that k < 2**(10**2017)-1, since a k exceeding this can't even be represented in normal binary notation in this universe.
Luckily, the structure of the string is simple enough that computing the kth character directly is straightforward. The major clue comes when we look at the characters at even and odd positions:
>>> F(6)[::2]
'acacacacacacacacacacacacacacacac'
>>> F(6)[1::2]
'aacaaccaaaccaccaaacaacccaaccacc'
The characters at even positions simply alternate between a and c (and it's straightforward to prove that this is true, based on the construction). So if our k is even, we can simply look at whether k/2 is odd or even to determine whether we'll get an a or a c.
What about the odd positions? Well F(6)[1::2] should look somewhat familiar: it's just F(5):
>>> F(6)[1::2] == F(5)
True
Again, it's straightforward to prove (e.g., by induction) that this isn't simply a coincidence, and that F(n+1)[1::2] == F(n) for all nonnegative n.
We now have an effective way to compute the kth character in our infinite string: if k is even, we just look at the parity of k/2. If k is odd, then we know that the character at position k is equal to that at position (k-1)/2. So here's a first solution to computing that character:
def char_at_pos(k):
"""
Return the character at position k of the string F(n), for any
n satisfying 2**n-1 > k.
"""
while k % 2 == 1:
k //= 2
return 'ac'[k//2%2]
And a check that this does the right thing:
>>> ''.join(char_at_pos(i) for i in range(2**6-1))
'aacaaccaaaccaccaaacaacccaaccaccaaacaaccaaaccacccaacaacccaaccacc'
>>> ''.join(char_at_pos(i) for i in range(2**6-1)) == F(6)
True
But we can do better. We're effectively staring at the binary representation of k, removing all trailing '1's and the next '0', then simply looking at the next bit to determine whether we've got an 'a' or a 'c'. Identifying the trailing 1s can be done by bit-operation trickery. This gives us the following semi-obfuscated loop-free solution, which I leave it to you to unwind:
def char_at_pos2(k):
"""
Return the character at position k of the string F(n), for any
n satisfying 2**n-1 > k.
"""
return 'ac'[k//(1+(k+1^k))%2]
Again, let's check:
>>> F(20) == ''.join(char_at_pos2(i) for i in range(2**20-1))
True
Final comments: this is a very well-known and well-studied sequence: it's called the dragon curve sequence, or the regular paper-folding sequence, and is sequence A014577 in the online encyclopaedia of integer sequences. Some Google searches will likely give you many other ways to compute its elements. See also this codegolf question.
Based on what you have already coded, here's my suggestion:
def main_function(num):
if num == 0:
return ''
previous = main_function(num-1)
return previous + 'a' + op1(op2(previous))
print(main_function(10**2017))
P.S: I'm not sure of the efficiency.

How to check if complex number is a whole number

Given a value numerical value x, you can just do this float(x).is_integer() to check if it's an integer. Is there a way to do this for complex numbers?
I'm trying to use list comprehension to take only the integer roots of a polynomial over a finite field which are integers.
[r for r in solve(f,domain=FiniteField(p)) if float(r).is_integer()]
but if the solve function returns complex roots, this doesn't work.
Does anyone know how to either: check if a given (possibly complex number) is an integer OR know if there's a SymPy function to get the roots of a polynomial over a finite field which are integers?
Use the ground_roots function along with the modulus keyword argument. This returns the roots modulo p, with multiplicities. Here's an example:
>>> from sympy import Symbol, ground_roots
>>> x = Symbol('x')
>>> f = x**5 + 7*x + 1
>>> p = 13
>>> ground_roots(f, modulus=p)
{-5: 1, 4: 2}
That says that the roots of poly modulo 13 are -5 and 4, with the root 4 having multiplicity 2.
By the way, it looks to me as though complex numbers are a red herring here: the roots of an integral polynomial over a finite field can't naturally be regarded as complex numbers. The call to solve in your original post is ignoring the domain argument and is simply giving algebraic numbers (which can reasonably be interpreted as complex numbers) as its results, which is probably why you ended up looking at complex numbers. But these aren't going to help when trying to find the roots modulo a prime p.
float.is_integer(z.real) tells you if the real part is integer
If you want to check if imaginary part is zero, you can do this:
In [17]: a=2 + 2j
In [18]: bool(a.imag)
Out[18]: True
In [19]: b=2 + 0j
In [20]: bool(b.imag)
Out[20]: False
Using a list comprehension as in your original question, one could do:
roots = [complex(r) for r in solve(f, domain=FiniteField(p))]
int_roots = [z.real for z in roots if z.real.is_integer() and not z.imag]

subs() for large number of variables

I am working on optimization, and I want to code a functions where I can sub() larger number of variables.
for this, I generated a m functions with n variables. For example, let say 3 functions and 2 variables.
num_var=2
x=symbols('x0:num_var')
I generated this functions:
f=[5*x0 + 4*x1 + 6, -4*x0 - 5*x1 - 6, -8*x0 - 10]
and I have a point like:
point=[-2.8,1.74]
If I want to sub() in each function, what is the fastest way to do this?
f[0].subs([(x[0],point[0]),(x[1],point[1])])
(this will give evaluation of first function in point)
And I get three function evaluations in list:
fun_eval=[some number,some number, ... (in size of number of functions)]
The question is how can I write a code to do evaluations for larger number of variables in large number of functions?
>>> from sympy import *
>>> x0, x1 = x = symbols('x:2')
>>> f = [5*x0+4*x1+6, -4*x0-5*x1-6, -8*x0-10]
>>> point = (-2.8, 1.74)
>>> [fi.subs(zip(x, point)) for fi in f]
[-1.04000000000000, -3.50000000000000, 12.4000000000000]
See http://docs.sympy.org/latest/tutorial/basic_operations.html#lambdify. If you want to efficiently evaluate a function at a large number of points, you should use lambdify.
>>> func = lambdify(x, f)
>>> func(*point)
[-1.04, -3.5, 12.399999999999999]
By default, it uses subs, but if you have numpy installed, you can use it for much faster evaluation, by passing module="numpy" to lambdify.
By the way, your symbols('x0:num_var') does not actually use the num_var variable. You need to use symbols('x0:%d' % num_var).

"'generator' object is not subscriptable" error

Why am I getting this error, from line 5 of my code, when attempting to solve Project Euler Problem 11?
for x in matrix:
p = 0
for y in x:
if p < 17:
currentProduct = int(y) * int(x[p + 1]) * int(x[p + 2]) * int(x[p + 3])
if currentProduct > highestProduct:
print(currentProduct)
highestProduct = currentProduct
else:
break
p += 1
'generator' object is not subscriptable
Your x value is is a generator object, which is an Iterator: it generates values in order, as they are requested by a for loop or by calling next(x).
You are trying to access it as though it were a list or other Sequence type, which let you access arbitrary elements by index as x[p + 1].
If you want to look up values from your generator's output by index, you may want to convert it to a list:
x = list(x)
This solves your problem, and is suitable in most cases. However, this requires generating and saving all of the values at once, so it can fail if you're dealing with an extremely long or infinite list of values, or the values are extremely large.
If you just needed a single value from the generator, you could instead use itertools.islice(x, p) to discard the first p values, then next(...) to take the one you need. This eliminate the need to hold multiple items in memory or compute values beyond the one you're looking for.
import itertools
result = next(itertools.islice(x, p))
As an extension to Jeremy's answer some thoughts about the design of your code:
Looking at your algorithm, it appears that you do not actually need truly random access to the values produced by the generator: At any point in time you only need to keep four consecutive values (three, with an extra bit of optimization). This is a bit obscured in your code because you mix indexing and iteration: If indexing would work (which it doesn't), your y could be written as x[p + 0].
For such algorithms, you can apply kind of a "sliding window" technique, demonstrated below in a stripped-down version of your code:
import itertools, functools, operator
vs = [int(v) for v in itertools.islice(x, 3)]
for v in x:
vs.append(int(v))
currentProduct = functools.reduce(operator.mul, vs, 1)
print(currentProduct)
vs = vs[1:]

Categories