I have a recursive function that can produce a difficult-to-know number of expressions, each needing a new variable multiplied to it. These variables will later be removed out by calculations involving integration or residue.
How can I develop these unknown number of variables? Maybe indexed? All examples I've seen on the internet are working with an a priori known object of a definite size, e.g. "item" in How can you dynamically create variables via a while loop? or Accessing the index in Python 'for' loops
I think I can boil it down to this simple example to use in my real script:
import sympy as s
p0,p1,p2,p3,p4=s.symbols('p0 p1 p2 p3 p4')
l = [p0, p1, p2, p3, p4]
def f(n):
if n == 0:
return l[n]
elif n == 1:
return l[n]
else:
return f(n-1)*l[n]+f(n-2)
f(3) # works
f(6) # doesnt' work - need to define ahead of time the
# dummy variables l[6], l[5], ....
# even if they are just symbols for (much) later numerical evaluation.
I need this above snippet to actually generate the needed unknowns ahead of time.
I saw some mentions of pandas, but couldn't find a good example for my need, nor even sure if that was the best route. Also saw things like, "...an unknown number of lines [file]...", or "...unknown number of arguments...", but those are, seemingly, not applicable.
Indexed objects represent an abstract thing with an index taking any values, with no restriction on how large the index can be.
import sympy as s
p = s.IndexedBase("p")
def f(n):
if n == 0 or n == 1:
return p[n]
else:
return f(n-1)*p[n] + f(n-2)
print(f(7))
Output
(p[0] + p[1]*p[2])*p[3] + (((p[0] + p[1]*p[2])*p[3] + p[1])*p[4] + p[0] + p[1]*p[2])*p[5] + (((p[0] + p[1]*p[2])*p[3] + p[1])*p[4] + ((p[0] + p[1]*p[2])*p[3] + (((p[0] + p[1]*p[2])*p[3] + p[1])*p[4] + p[0] + p[1]*p[2])*p[5] + p[1])*p[6] + p[0] + p[1]*p[2])*p[7] + p[1]
As an aside, things like p0,p1,p2,p3,p4=s.symbols('p0 p1 p2 p3 p4') can be done more easily with syms = s.symbols('p0:5') or even
n = ...
syms = s.symbols('p0:{}'.format(n))
This creates individual symbols, not an indexed object, so the number n has to be known at the time of creation. But still easier than listing p0 p1 and so on.
Related
I'm trying to write code that performs polynomial multiplication using a divide and conquer method. It uses a trick to do only 3 multiplications instead of 4 multiplications, involving splitting each polynomial into two halves and factoring out an x^n/2 from one of the two halves. My code is returning the wrong answer with some test cases and I'm not sure what I'm doing wrong.
Edit: Adding naive multiply function and test cases.
Here's what I've tried so far:
class Polynom:
"""stores polynomial object"""
def __init__(self, coefficients):
"""pass coefficient list in increasing order, i.e. x^0 + x^1 + x^2 + ... + x^n"""
self.coefficients = coefficients
self.length = len(coefficients)
self.nonZero = np.count_nonzero(self.coefficients)
def __add__(self, other):
for i in range(0,other.length):
try:
self.coefficients[i] = self.coefficients[i] + other.coefficients[i]
except:
self.coefficients.append(0)
self.coefficients[i] = self.coefficients[i] + other.coefficients[i]
self.length = len(self.coefficients)
self.nonZero = np.count_nonzero(self.coefficients)
return self
def __sub__(self, other):
for i in range(0,other.length):
self.coefficients[i] = self.coefficients[i] - other.coefficients[i]
self.length = len(self.coefficients)
self.nonZero = np.count_nonzero(self.coefficients)
return self
def split(self):
first_max_ind = int((self.length-1)/2)
first_coefs = self.coefficients[:first_max_ind]
first = Polynom(first_coefs)
second_coefs = self.coefficients[first_max_ind:]
second = Polynom(second_coefs)
return first, second
def divideAndConquer(a,b):
"""simple divide and conquer algorithm for multiplication"""
if a.nonZero == 2 and b.nonZero == 2:
c = naiveMultiply(a,b)
return c
else:
a0, a1 = a.split()
b0, b1 = b.split()
# calculate y, u, and z
y = divideAndConquer(a0 + a1, b0 + b1)
u = divideAndConquer(a0, b0)
z = divideAndConquer(a1, b1)
middleTerm = y - u - z
NOver2 = int(max(a.length/2, b.length/2))
lyst = [0] * int(NOver2 + 1)
lyst[NOver2] = 1
xNOver2 = Polynom(lyst)
return u + naiveMultiply(middleTerm, xNOver2) + naiveMultiply(z, xNOver2)
def naiveMultiply(a,b):
"""takes two polynomials and multiplies using naive approach"""
maxLength = a.length + b.length - 1
coefficientList = [0] * maxLength
for i in range(0,a.length):
for j in range(0, b.length):
power = i + j
coefficient = a.coefficients[i] * b.coefficients[j]
coefficientList[power] += coefficient
return Polynom(coefficientList)
a = Polynom([1,2,3,4,5])
b = Polynom([1,2,3,4,5])
naiveMultiply(a,b)
divideAndConquer(a,b)
The correct answer for test case: (1 + 2x + 3x^2 + 4x^3 + 5x^4)^2
should be:
1 + 4x + 10x^2 + 20x^3 + 35x^4 + 44x^5 + 46x^6 + 40x^7 + 25x^8
but I'm getting:
100 + 100x + 25x^2 + 0x^3 + 0x^4 + 0x^5
Your __add__ and __sub__ methods alter the self instance (note the assignments to self.coefficients).
Further down in the divideAndConquer method, I see that you are using a variable a0 in an expression a0 + a1, which would alter the value of a0 to become a0 + a1. You then use the same (altered) a0 variable in further expressions, possibly not aware that the variable now has a different value.
In general, overloaded operator methods should probably create a new instance instead of altering self, this will make them much safer to use.
in this case, for example, a safe implementation of __add__ would be
def __add__(self, other):
coefficients = list(self.coefficients)
for i in range(0,other.length):
try:
coefficients[i] = coefficients[i] + other.coefficients[i]
except:
coefficients.append(0)
coefficients[i] = coefficients[i] + other.coefficients[i]
return Polynom(coefficients)
(I've minimally fixed the self-altering; there are several further improvements that should be made to the method including what has been suggested in the comments.)
In addition to the Pyton-level implementation bugs, there are also math-level bugs here: the polynomials are split down their own middles, but that creates a problem when putting the pieces back together. Also the z piece shouldn't be scaled by the same amount as the middleTerm.
This already looks suspicious anyway:
NOver2 = int(max(a.length/2, b.length/2))
The goal with the calculation of middleTerm is that the scaled middleTerm (after shifting it) is identical to a0 * b1 * scaleB + a1 * scaleA * b0 (where scaleA corresponds to the size of a0, and scaleB corresponds to the size of b0). If scaleA == scaleB then that's no problem, multiply by the scale afterwards (by shifting - if you implement this scaling as a naiveMultiply then the algorithm isn't efficient). But in this code, the scales are not necessarily the same, they depend on the sizes of a and b. That's a problem.
The z piece is the products of two "upper halves", so it picks up two of those scale factors. So it should be multiplied by the square of the factor that the middle term is scaled by, or rather, shifted by twice as much as the middle term is shifted by.
(Why are not the math formulae showing correctly?)
I am performing a test over the Z3 library in Python (Collab) to see whether it knows to distinguish formulae.
The test is the following: (1) I make a quantifier elimination over a formula $phi_1$, (2) I change the formula in a way it remains semantically equivalent: for instance, $phi_1 \equiv (a<b+1)$ to $\phi_2 \equiv (a<1+b)$, (3) I test whether $phi_1=phi_2$.
To see whether $phi_1=phi_2$, I perform the following query: for all the variables, I see whether formulae imply each other. Like $\forall * . (\phi_1 \rightleftarrow \phi_2)$ Is this correct?
So, imagine I apply this on my machine:
x, t1, t2 = Reals('x t1 t2')
g = Goal()
g.add(Exists(x, And(t1 < x, x < t2)))
t = Tactic('qe')
res = t(g)
The result res is [[Not(0 <= t1 + -1*t2)]], so a semantically equivalent formula is: [[Not(0 <= -1*t2 + t1)]] Am I right?
Let us check whether [[Not(0 <= t1 + -1*t2)]] = [[Not(0 <= -1*t2 + t1)]]. So I apply the universal double-implication formula above:
w = Goal()
w.add(ForAll(t1, (ForAll(t2, And(
Implies(Not(0 <= -1*t2 + t1), Not(0 <= t1 + -1*t2)),
Implies(Not(0 <= t1 + -1*t2), Not(0 <= -1*t2 + t1)),
)))))
tt = Tactic('qe')
areThey = tt(w)
print (areThey)
And the result is.. [[]] I do not know how to interpret this. An optimistic approach is to think that it returns emptyness, since quantifier elimination has been capable to eliminate both quantifiers successfully (i.e. with true result).
I think this can be a problem of using a wrong tactic, or maybe Z3 does not deal OK with universal quantifiers.
However, the most probable situation is that I am probably missing something key and Z3 is clever enough to distinguish.
Any help?
This just means that the quantifier-elimination tactic reduced the goal to empty-subset; i.e., it eliminated it completely. You've nothing left to do.
In general, to check if two formulas are equivalent in z3, you assert the negation of their equivalence; and see if z3 can come up with a model: If the negation is satisfiable, then that is a counter-example for the original equivalence. If you get unsat, then you conclude that the original equivalence holds for all inputs. This is how you code that in z3:
from z3 import *
t1, t2 = Reals('t1 t2')
s = Solver()
fml1 = Not(0 <= -1*t2 + t1)
fml2 = Not(0 <= t1 + -1*t2)
s.add(Not(fml1 == fml2))
print(s.check())
If you run this, you'll see:
unsat
meaning the equivalence holds.
I'm using Sympy to make a custom function which converts complex square roots into their complex numbers. When I input -sqrt(-2 + 2*sqrt(3)*I) I get the expected output of -1 - sqrt(3)*I, however, inputting -sqrt(-2.0 + 2*sqrt(3)*I) (has a -2.0 instead of -2), I get the output -1.0 - 0.707106781186547*sqrt(6)*I.
I've tried to convert the input expression to a string, gotten rid of the '.0 ' and then executed a piece of code to return it to the type sympy.core.add.Mul, which usually works with other strings, but the variable expression is still a string.
expression = str(input_expression).replace('.0 ', '')
exec(f'expression = {expression}')
How do I get rid of the redundant use of floats in my expression, while maintaining its type of sympy.core.add.Mul, so that my function will give a nice output?
P.S. The number 0.707106781186547 is an approximation of 1/sqrt(2). The fact that this number is present in the second output means that my function is running properly, it just isn't outputting in the desired way.
Edit:
For whatever reason, unindenting and getting rid of the function as a whole, running the code as its own program gives the expected output. It's only when the code is in function form that it doesn't work.
Code as Requested:
from IPython.display import display, Math
from sympy.abc import *
from sympy import *
def imaginary_square_root(x, y):
return(sqrt((x + sqrt(x**2 + y**2)) / (2)) + I*((y*sqrt(2)) / (2*sqrt(x + sqrt(x**2 + y**2))))) # calculates the square root of a complex number
def find_imaginary_square_root(polynomial): # 'polynomial' used because this function is meant to change expressions including variables such as 'x'
polynomial = str(polynomial).replace('.0 ', ' ')
exec(f'polynomial = {polynomial}')
list_of_square_roots = [] # list of string instances of square roots and their contents
list_of_square_root_indexes = [] # list of indexes at which the square roots can be found in the string
polynomial_string = str(polynomial)
temp_polynomial_string = polynomial_string # string used and chopped up, hence the prefix 'temp_...'
current_count = 0 # counter variable used for two seperate jobs
while 'sqrt' in temp_polynomial_string: # gets indexes of every instance of 'sqrt'
list_of_square_root_indexes.append(temp_polynomial_string.index('sqrt') + current_count)
temp_polynomial_string = temp_polynomial_string[list_of_square_root_indexes[-1] + 4:]
current_count += list_of_square_root_indexes[-1] + 4
for square_root_location in list_of_square_root_indexes:
current_count = 1 # second job for 'current_count'
for index, char in enumerate(polynomial_string[square_root_location + 5:]):
if char == '(':
current_count += 1
elif char == ')':
current_count -= 1
if not current_count: # when current_count == 0, we know that the end of the sqrt contents have been reached
list_of_square_roots.append(polynomial_string[square_root_location:square_root_location + index + 6]) # adds the square root with contents to a list
break
for individual_square_root in list_of_square_roots:
if individual_square_root in str(polynomial):
evaluate = individual_square_root[5:-1]
x = re(evaluate)
y = im(evaluate)
polynomial = polynomial.replace(eval(individual_square_root), imaginary_square_root(x, y)) # replace function used here is Sympy's replace function for polynomials
return polynomial
poly = str(-sqrt(-2.0 + 2*sqrt(3)*I))
display(Math(latex(find_imaginary_square_root(poly))))
What exactly are you trying to accomplish? I still do not understand. You have a whole chunck of code. Try this out:
from sympy import *
def parse(expr): print(simplify(expr).evalf().nsimplify())
parse(-sqrt(-2.0 + 2*sqrt(3)*I))
-1 - sqrt(3)*I
I think everything that you're fighting to do here can be made easier with what sympy has built in. First, assuming that you're taking in user given strings, I'd recommend using the built in parser's of sympy. Second, sympy will do this exact calculation for you, although with a caveat.
from sympy.parsing.sympy_parser import parse_expr
def simplify_string(polynomial_str):
polynomial = parse_expr(polynomial_str)
return polynomial.powsimp().evalf()
Usage examples:
>>>simplify_string('-sqrt(-2 + 2*sqrt(3)*I)')
-1.0 - 1.73205080756888*I
>>>simplify_string('sqrt(sqrt(1 + sqrt(2)*I) + I*sqrt(3 - I*sqrt(5)))')
1.54878147282944 + 0.78803305913*I
>>>simpify_string('sqrt((3 + sqrt(2 + sqrt(3)*I)*I)*x**2 + (3 + sqrt(5)*I)*x + I*4)'
(x**2*(3.0 + I*(2.0 + 1.73205080756888*I)**0.5) + x*(3.0 + 2.23606797749979*I) + 4.0*I)**0.5
The problem is, that sympy will either work in floats, or exact. If you want sympy to calculate out the numerical value of a square root, it's going to display what could be an int as a float for clarity. You can't fix the typecasting, but a lot of the work that you're trying to do, sympy has built in under the hood.
Edit
You can use .nsimplify() on the polynomial to bring things back to nice looking numbers if possible, but you won't be able to have both evaluated roots, and nice displays in the same form.
The sqrtdenest batteries are already included. If you replace ints expressed as floats it will work:
>>> from sympy import sqrtdenest, sqrt, Float
>>> eq = -sqrt(-2.0 + 2*sqrt(3)*I)
Define a function that will extract Floats that are equal to ints
>>> intfloats = lambda x: dict([(i,int(i)) for i in x.atoms(Float) if i==int(i)])
Use it to transform eq and then apply the sqrtdenest
>>> eq.xreplace(intfloats(eq))
-sqrt(-2 + 2*sqrt(3)*I)
>>> sqrtdenest(_)
-1 + sqrt(3)
A problem with using nsimplify (or any mass simplification) is that it may do more than you want. It's best to use the most specific transformation as possible to limit the impact (and work).
/!\ sqrtdenest appears to have a problem that I will report: it is dropping the I
I have a very long math formula (just to put you in context: it has 293095 characters) which in practice will be the body of a python function. This function has 15 input parameters as in:
def math_func(t,X,P,n1,n2,R,r):
x,y,z = X
a,b,c = P
u1,v1,w1 = n1
u2,v2,w2 = n2
return <long math formula>
The formula uses simple math operations + - * ** / and one function call to arctan. Here an extract of it:
r*((-16*(r**6*t*u1**6 - 6*r**6*u1**5*u2 - 15*r**6*t*u1**4*u2**2 +
20*r**6*u1**3*u2**3 + 15*r**6*t*u1**2*u2**4 - 6*r**6*u1*u2**5 -
r**6*t*u2**6 + 3*r**6*t*u1**4*v1**2 - 12*r**6*u1**3*u2*v1**2 -
18*r**6*t*u1**2*u2**2*v1**2 + 12*r**6*u1*u2**3*v1**2 +
3*r**6*t*u2**4*v1**2 + 3*r**6*t*u1**2*v1**4 - 6*r**6*u1*u2*v1**4 -
3*r**6*t*u2**2*v1**4 + r**6*t*v1**6 - 6*r**6*u1**4*v1*v2 -
24*r**6*t*u1**3*u2*v1*v2 + 36*r**6*u1**2*u2**2*v1*v2 +
24*r**6*t*u1*u2**3*v1*v2 - 6*r**6*u2**4*v1*v2 -
12*r**6*u1**2*v1**3*v2 - 24*r**6*t*u1*u2*v1**3*v2 +
12*r**6*u2**2*v1**3*v2 - 6*r**6*v1**5*v2 - 3*r**6*t*u1**4*v2**2 + ...
Now the point is that in practice the bulk evaluation of this function will be done for fixed values of P,n1,n2,R and r which reduces the set of free variables to only four, and "in theory" the formula with less parameters should be faster.
So the question is: How can I implement this optimization in Python?
I know I can put everything in a string and do some sort of replace,compile and eval like in
formula = formula.replace('r','1').replace('R','2')....
code = compile(formula,'formula-name','eval')
math_func = lambda t,x,y,z: eval(code)
It would be good if some operations (like power) are substituted by their value, for example 18*r**6*t*u1**2*u2**2*v1**2 should become 18*t for r=u1=u2=v1=1. I think compile should do so but in any case I'm not sure. Does compile actually perform this optimization?
My solution speeds up the computation but if I can squeeze it more it will be great. Note: preferable within standard Python (I could try Cython later).
In general I'm interesting in a pythonic way to accomplish my goal maybe with some extra libraries: what is a reasonably good way of doing this? Is my solution a good approach?
EDIT: (To give more context)
The huge expression is the output of a symbolic line integral over an arc of circle. The arc is given in space by the radius r, two ortho-normal vectors (like the x and y axis in a 2D version) n1=(u1,v1,w1),n2=(u2,v2,w2) and the center P=(a,b,c). The rest is the point over which I'm performing the integration X=(x,y,z) and a parameter R for the function I'm integrating.
Sympy and Maple just take ages to compute this, the actual output is from Mathematica.
If you are curious about the formula here it is (pseudo-pseudo-code):
G(u) = P + r*(1-u**2)/(1+u**2)*n1 + r*2*u/(1+u**2)*n2
integral of (1-|X-G(t)|^2/R^2)^3 over t
You could use Sympy:
>>> from sympy import symbols
>>> x,y,z,a,b,c,u1,v1,w1,u2,v2,w2,t,r = symbols("x,y,z,a,b,c,u1,v1,w1,u2,v2,w2,t,r")
>>> r=u1=u2=v1=1
>>> a = 18*r**6*t*u1**2*u2**2*v1**2
>>> a
18*t
Then you can create a Python function like this:
>>> from sympy import lambdify
>>> f = lambdify(t, a)
>>> f(1)
18
And that f function is indeed simply 18*t:
>>> import dis
>>> dis.dis(f)
1 0 LOAD_CONST 1 (18)
3 LOAD_FAST 0 (_Dummy_18)
6 BINARY_MULTIPLY
7 RETURN_VALUE
If you want to compile the resulting code into machine code, you can try a JIT compiler such as Numba, Theano, or Parakeet.
Here's how I would approach this problem:
compile() your function to an AST (Abstract Syntax Tree) instead of a normal bytecode function - see the standard ast module for details.
Traverse the AST, replacing all references to the fixed parameters with their fixed value. There are libraries such as macropy that may be useful for this, I don't have any specific recommendation.
Traverse the AST again, performing whatever optimizations this might enable, such as Mult(1, X) => X. You don't have to worry about operations between two constants, as Python (since 2.6) optimizes that already.
compile() the AST into a normal function. Call it, and hope that the speed was increased by a sufficient amount to justify all the pre-optimization.
Note that Python will never optimize things like 1*X on its own, as it cannot know what type X will be at runtime - it could be an instance of a class that implements the multiplication operation in an arbitrary way, so the result is not necessarily X. Only your knowledge that all the variables are ordinary numbers, obeying the usual rules of arithmetic, makes this optimization valid.
The "right way" to solve a problem like this is one or more of:
Find a more efficient formulation
Symbolically simplify and reduce terms
Use vectorization (e.g. NumPy)
Punt to low-level libraries that are already optimized (e.g. in languages like C or Fortran that implicitly do strong expression optimization, rather than Python, which does nada).
Let's say for a moment, though, that approaches 1, 3, and 4 are not available, and you have to do this in Python. Then simplifying and "hoisting" common subexpressions is your primary tool.
The good news is, there are a lot of opportunities. The expression r**6, for example, is repeated 26 times. You could save 25 computations by simply assigning r_6 = r ** 6 once, then replacing r**6 every time it occurs.
When you start looking for common expressions here, you'll find them everywhere. It'd be nice to mechanize that process, right? In general, that requires a full expression parser (e.g. from the ast module) and is an exponential-time optimization problem. But your expression is a bit of a special case. While long and varied, it's not especially complicated. It has few internal parenthetical groupings, so we can get away with a quicker and dirtier approach.
Before the how, the resulting code is:
sa = r**6 # 26 occurrences
sb = u1**2 # 5 occurrences
sc = u2**2 # 5 occurrences
sd = v1**2 # 5 occurrences
se = u1**4 # 4 occurrences
sf = u2**3 # 3 occurrences
sg = u1**3 # 3 occurrences
sh = v1**4 # 3 occurrences
si = u2**4 # 3 occurrences
sj = v1**3 # 3 occurrences
sk = v2**2 # 1 occurrence
sl = v1**6 # 1 occurrence
sm = v1**5 # 1 occurrence
sn = u1**6 # 1 occurrence
so = u1**5 # 1 occurrence
sp = u2**6 # 1 occurrence
sq = u2**5 # 1 occurrence
sr = 6*sa # 6 occurrences
ss = 3*sa # 5 occurrences
st = ss*t # 5 occurrences
su = 12*sa # 4 occurrences
sv = sa*t # 3 occurrences
sw = v1*v2 # 5 occurrences
sx = sj*v2 # 3 occurrences
sy = 24*sv # 3 occurrences
sz = 15*sv # 2 occurrences
sA = sr*u1 # 2 occurrences
sB = sy*u1 # 2 occurrences
sC = sb*sc # 2 occurrences
sD = st*se # 2 occurrences
# revised formula
sv*sn - sr*so*u2 - sz*se*sc +
20*sa*sg*sf + sz*sb*si - sA*sq -
sv*sp + sD*sd - su*sg*u2*sd -
18*sv*sC*sd + su*u1*sf*sd +
st*si*sd + st*sb*sh - sA*u2*sh -
st*sc*sh + sv*sl - sr*se*sw -
sy*sg*u2*sw + 36*sa*sC*sw +
sB*sf*sw - sr*si*sw -
su*sb*sx - sB*u2*sx +
su*sc*sx - sr*sm*v2 - sD*sk
That avoids 81 computations. It's just a rough cut. Even the result could be further improved. The subexpressions sr*sw and su*sd for example, could be pre-computed as well. But we'll leave that next level for another day.
Note that this doesn't include the starting r*((-16*(. The majority of the simplification can be (and needs to be) done on the core of the expression, not on its outer terms. So I stripped those away for now; they can be added back once the common core is computed.
How do you do this?
f = """
r**6*t*u1**6 - 6*r**6*u1**5*u2 - 15*r**6*t*u1**4*u2**2 +
20*r**6*u1**3*u2**3 + 15*r**6*t*u1**2*u2**4 - 6*r**6*u1*u2**5 -
r**6*t*u2**6 + 3*r**6*t*u1**4*v1**2 - 12*r**6*u1**3*u2*v1**2 -
18*r**6*t*u1**2*u2**2*v1**2 + 12*r**6*u1*u2**3*v1**2 +
3*r**6*t*u2**4*v1**2 + 3*r**6*t*u1**2*v1**4 - 6*r**6*u1*u2*v1**4 -
3*r**6*t*u2**2*v1**4 + r**6*t*v1**6 - 6*r**6*u1**4*v1*v2 -
24*r**6*t*u1**3*u2*v1*v2 + 36*r**6*u1**2*u2**2*v1*v2 +
24*r**6*t*u1*u2**3*v1*v2 - 6*r**6*u2**4*v1*v2 -
12*r**6*u1**2*v1**3*v2 - 24*r**6*t*u1*u2*v1**3*v2 +
12*r**6*u2**2*v1**3*v2 - 6*r**6*v1**5*v2 - 3*r**6*t*u1**4*v2**2
""".strip()
from collections import Counter
import re
expre = re.compile('(?<!\w)\w+\*\*\d+')
multre = re.compile('(?<!\w)\w+\*\w+')
expr_saved = 0
stmts = []
secache = {}
seindex = 0
def subexpr(e):
global seindex
cached = secache.get(e)
if cached:
return cached
base = ord('a') if seindex < 26 else ord('A') - 26
name = 's' + chr(seindex + base)
seindex += 1
secache[e] = name
return name
def hoist(e, flat, c):
"""
Hoist the expression e into name defined by flat.
c is the count of how many times seen in incoming
formula.
"""
global expr_saved
assign = "{} = {}".format(flat, e)
s = "{:30} # {} occurrence{}".format(assign, c, '' if c == 1 else 's')
stmts.append(s)
print "{} needless computations quashed with {}".format(c-1, flat)
expr_saved += c - 1
def common_exp(form):
"""
Replace ALL exponentiation operations with a hoisted
sub-expression.
"""
# find the exponentiation operations
exponents = re.findall(expre, form)
# find and count exponentiation operations
expcount = Counter(re.findall(expre, form))
# for each exponentiation, create a hoisted sub-expression
for e, c in expcount.most_common():
hoist(e, subexpr(e), c)
# replace all exponentiation operations with their sub-expressions
form = re.sub(expre, lambda x: subexpr(x.group(0)), form)
return form
def common_mult(f):
"""
Replace multiplication operations with a hoisted
sub-expression if they occur > 1 time. Also, only
replaces one sub-expression at a time (the most common)
because it may affect further expressions
"""
mults = re.findall(multre, f)
for e, c in Counter(mults).most_common():
# unlike exponents, only replace if >1 occurrence
if c == 1:
return f
# occurs >1 time, so hoist
hoist(e, subexpr(e), c)
# replace in loop and return
return re.sub('(?<!\w)' + re.escape(e), subexpr(e), f)
# return f.replace(e, flat(e))
return f
# fix all exponents
form = common_exp(f)
# fix selected multiplies
prev = form
while True:
form = common_mult(form)
if form == prev:
# have converged; no more replacements possible
break
prev = form
print "--"
mults = re.split(r'\s*[+-]\s*', form)
smults = ['*'.join(sorted(terms.split('*'))) for terms in mults]
print smults
# print the hoisted statements and the revised expression
print '\n'.join(stmts)
print
print "# revised formula"
print form
Parsing with regular expressions is dicey business. That journey is prone to error, sorrow, and regret. I guarded against bad outcomes by hoisting some exponentiations that didn't strictly need to be, and by plugging random values into both the before and after formulas to make sure they both give the same results. I recommend the "punt to C" strategy if this is production code. But if you can't...
I need to plot the position of a particle at time t, given the following formulae: s(t) = -0.5*g(s)*t^2+v0*t, where g(s) = G*M/(R+s(t))^2 (G, M, and R are constants, s being a value, not the function s(t)). The particle is being shot up vertically, and I want to print its current position every second until it hits the ground. But I can't figure out how to define one function without using the other before it's defined. This is my code so far:
G = 6.6742*10^(-11)
M = 5.9736*10^24
R = 6371000
s0 = 0
v0 = 300
t = 0
dt = 0.005
def g(s):
def s(t):
s(t) = -0.5*g(s)*t^2+v0*t
g(s) = G*M/(R+s(t))^2
def v(t):
v(t) = v(t-dt)-g(s(t-dt))*dt
while s(t) >= 0:
s(t) = s(t-dt)+v(t)*dt
t = t+dt
if t == int(t):
print s(t)
When I run the function, it says that it can't assign the function call.
The error means that you can't write s(t) = x, because s(t) is a function, and assignment on functions is performed with def .... Instead, you'll want to return the value, so you'd rewrite it like this:
def g(s):
def s(t):
return -0.5*g(s)*t^2+v0*t
return G*M/(R+s(t))^2
However, there are other issues with that as well. From a computational standpoint, this calculation would never terminate. Python is not an algebra system and can't solve for certain values. If you try to call s(t) within g(s), and g(s) within s(t), you'd never terminate, unless you define a termination condition. Otherwise they'll keep calling each other, until the recursion stack is filled up and then throws an error.
Also, since you defined s(t) within g(s), you can't call it from the outside, as you do several times further down in your code.
You seem to be confused about several syntax and semantic specifics of Python. If you ask us for what exactly you'd like to do and provide us with the mathematical formulae for it, it might be easier to formulate an answer that may help you better.
Edit:
To determine the position of a particle at time t, you'll want the following code (reformatted your code to Python syntax, use ** instead of ^ and return statements):
G = 6.6742*10**(-11)
M = 5.9736*10**24
R = 6371000
s0 = 0
v0 = 300
t = 0
dt = 0.005
sc = s0 # Current position of the particle, initially at s0
def g(s):
return -G*M/(R+s)**2
def s(t):
return 0.5*g(sc)*t**2 + v0*t + s0
count = 0
while s(t) >= 0:
if count % 200 == 0:
print(sc)
sc = s(t)
count += 1
t = dt*count
Python functions can call each other, but that's not how a function returns a value. To make a function return a particular value, use return, e.g.,
def v(t):
return v(t - dt) - g(s(t - dt)) * dt
Furthermore, I don't really understand what you're trying to do with this, but you'll probably need to express yourself differently:
while s(t) >= 0:
s(t) = s(t-dt)+v(t)*dt
t = t+dt