How to use sympy solve in for loop - python

I'm using sympy to solve an equation in a for loop in which at each interaction a variable (kp) multiples the function. But in each interaction the length of the output increases. I have an array k and kp is selected from k
k = [2,4,5,7,9]
for kp in k:
didt = beta * kp * teta
dteta = integrate(1/((kp-1) * pk * didt / avgk),teta)
dt = integrate(1,(t,0 ,1))
teta2 = solve(dteta - dt ,teta)
#print(solve(dteta - dt ,teta))
didt2 = beta * solve(dteta - dt ,teta) *kp
print(didt2)
Also, the output for didt2 for 1st iteration is
[1.49182469764127, 1.49182469764127]
for the second one is
[11.0231763806416, 11.0231763806416, 11.0231763806416, 11.0231763806416]
for the 3rd one is [54.5981500331442, 54.5981500331442, 54.5981500331442, 54.5981500331442, 54.5981500331442]
I'm just wondering, why the length of didt2 increases at each interaction?

It looks like solve() returns a list. This means that beta * solve(dteta - dt ,teta) *kp doesn't do what you think. Rather than multiplying the result, you are duplicating the elements of the returned list. For a simple example, try to see what the output is:
[0] * 10
In your case, kp takes on the values of 2, 4, and 5 on each iteration of the list, so the output you see is the result of doing
[1.49182469764127] * 2
[11.0231763806416] * 4
[54.5981500331442] * 5
These all result in lists with the exact length of the value of kp. It does not do numeric multiplication.

Related

Geometric series: calculate quotient and number of elements from sum and first & last element

Creating evenly spaced numbers on a log scale (a geometric progression) can easily be done for a given base and number of elements if the starting and final values of the sequence are known, e.g., with numpy.logspace and numpy.geomspace. Now assume I want to define the geometric progression the other way around, i.e., based on the properties of the resulting geometric series. If I know the sum of the series as well as the first and last element of the progression, can I compute the quotient and number of elements?
For instance, assume the first and last elements of the progression are and and the sum of the series should be equal to . I know from trial and error that it works out for n=9 and r≈1.404, but how could these values be computed?
You have enough information to solve it:
Sum of series = a + a*r + a*(r^2) ... + a*(r^(n-1))
= a*((r^n)-1)/(r-1)
= a*((last element * r) - 1)/(r-1)
Given the sum of series, a, and the last element, you can use the above equation to find the value of r.
Plugging in values for the given example:
50 = 1 * ((15*r)-1) / (r-1)
50r - 50 = 15r - 1
35r = 49
r = 1.4
Then, using sum of series = a*((r^n)-1)/(r-1):
50 = 1*((1.4^n)-1)(1.4-1)
21 = 1.4^n
n = log(21)/log(1.4) = 9.04
You can approximate n and recalculate r if n isn't an integer.
We have to reconstruct geometric progesssion, i.e. obtain a, q, m (here ^ means raise into power):
a, a * q, a * q^2, ..., a * q^(m - 1)
if we know first, last, total:
first = a # first item
last = a * q^(m - 1) # last item
total = a * (q^m - 1) / (q - 1) # sum
Solving these equation we can find
a = first
q = (total - first) / (total - last)
m = log(last / a) / log(q)
if you want to get number of items n, note that n == m + 1
Code:
import math
...
def Solve(first, last, total):
a = first
q = (total - first) / (total - last)
n = math.log(last / a) / math.log(q) + 1
return (a, q, n);
Fiddle
If you put your data (1, 15, 50) you'll get the solution
a = 1
q = 1.4
n = 9.04836151801382 # not integer
since n is not an integer you, probably want to adjust; let last == 15 be exact, when total can vary. In this case q = (last / first) ^ (1 / (n - 1)) and total = first * (q ^ n - 1) / (q - 1)
a = 1
q = 1.402850552006674
n = 9
total = 49.752 # now n is integer, but total <> 50
You have to solve the following two equations for r and n:
a:= An / Ao = r^(n - 1)
and
s:= Sn / Ao = (r^n - 1) / (r - 1)
You can eliminate n by
s = (r a - 1) / (r - 1)
and solve for r. Then n follows by log(a) / log(r) + 1.
In your case, from s = 50 and a = 15, we obtain r = 7/5 = 1.4 and n = 9.048...
It makes sense to round n to 9, but then r^8 = 15 (r ~ 1.40285) and r = 1.4 are not quite compatible.

Python - multiple iterations over lists of different size

Does anyone could help with this code? I'm writing this code to compute some values over some equations. I satarted by reading the CSV values into a dictionary, but after calculating the values to reach the final set of parameters, I cannot find a way to iterate repeatedly over the same list.
To simply, I have two input lists dPdT and listT. I need to iterate every parameter of list dPdT over listT and produce three different lists P.
I thank anyone willing to help. This is a study project for a course.
# Request user's interval parameters for calculations
print("Inform the temperature range and interval (°C). Only integers.")
minT = int(input("Min: "))
maxT = int(input("Max: "))
stepT = int(input("Temperature interval: "))
# create a list of temperature values to compute pressure parameters
listT = []
for x in range(minT, (maxT+stepT), stepT):
listT.append(x)
# Open CSV file in read mode to acces data and read it into a dictionary
with open(CSVfile, "r") as CSV:
reader = csv.DictReader(CSV)
listDict = []
# Creates a list of dictionaries with the fluid inclusion parameters
for lines in reader:
listDict.append(lines)
# Define list parameters to be computated
a, b, c, dPdT, P = [], [], [], [], []
# Loop iterates over the dictionary list and computates parameters a,b,c stored in lists a,b,c
for i, rows in enumerate(listDict):
a.append(i)
b.append(i)
c.append(i)
if "sal" in rows:
a[i] = (18.28 + 1.4413 * float(rows["sal"]) + 0.0047241 * float(rows["sal"]) ** 2
+ 0.0024213 * float(rows["sal"]) ** 3 + 0.000038064 * float(rows["sal"]) ** 4)
b[i] = (0.019041 - 1.4268 * 0.01 * float(rows["sal"]) + 5.66012 * 0.0001 * float(rows["sal"]) ** 2
- 4.2329 * 0.000001 * float(rows["sal"]) ** 3 - 3.0354 * 0.00000001 * float(rows["sal"]) ** 4)
c[i] = (- 1.5988 * 0.0001 + 3.6892 * (10 ** -5) * float(rows["sal"]) - 1.9473 * (10 ** -6) * float(rows["sal"]) ** 2
+ 4.1674 * (10 ** -8) * float(rows["sal"]) ** 3 - 3.3008 * (10 ** -10) * float(rows["sal"]) ** 4)
# Loop iterates over the dictionary list and computates dPdT to store the values in list dPdT
for i, rows in enumerate(listDict):
dPdT.append(i)
if "th" in rows:
dPdT[i] = a[i] + b[i] * float(rows["th"]) + c[i] * float(rows["th"]) ** 2
# Loop populates list P (pressure values)
for i in range(len(listT)):
P.append(i)
# problem starts here: I need to iterate over the listT or P, repeating it for every dPdT values.
# Loop to calculate P based on lits dPdT and listT.
while i in range(len(dPdT)):
for j in range(len(P)):
P[j] = dPdT[j] * listT[j]
I need to iterate every parameter of list dPdT over listT and produce three different lists P.
I'm sorry it's hard to understand your question. Explain this line.
Do you want to multiply each element in dPdT with each element in listT resulting in a P array of length = len(dPdT) * len(listT) ??? Because your current code is doing exactly that.
Edit:
Ok, I understand. I think the best way to go about it is to make P a 2d array. Basically, P will be a huge array containing multiple arrays within it. len(P) = len(dPdT) and len(P[i]) = len(listT)
P = [ [0 for i in range(len(listT)] for j in range(len(dPdT)) ]
for row in range(len(dPdT)):
for col in range(len(listT)):
P[row][col] = dPdT[row] * listT[col]
I hope this is what you wanted. Basically, the nth element of P will also be array containing the numbers in listT all multiplied by the nth element of dPdT.
:)

How to speed up this numpy.arange loop?

In a python program, the following function is called about 20,000 times from another function that is called about 1000 times from yet another function that executes 30 times. Thus the total number of times this particular function is called is about 600,000,000. In python it takes more than two hours (perhaps much longer; I aborted the program without waiting for it to finish), while essentially the same task coded in Java takes less than 5 minutes. If I change the 20,000 above to 400 (keeping everything else in the rest of the program untouched), the total time drops to about 4 minutes (this means this particular function is the culprit). What can I do to speed up the Python version, or is it just not possible? No lists are manipulated inside this function (there are lists elsewhere in the whole program, but in those places I tried to use numpy arrays as far as possible). I understand that replacing python lists with numpy arrays speeds things up, but there are cases in my program (not in this particular function) where I must build a list iteratively, using append; and those must-have lists are lists of objects (not floats or ints), so numpy would be of little help even if I converted those lists of objects to numpy arrays.
def compute_something(arr):
'''
arr is received as a numpy array of ints and floats (I think python upcasts them to all floats,
doesn’t it?).
Inside this function, elements of arr are accessed using indexing (arr[0], arr[1], etc.), because
each element of the array has its own unique use. It’s not that I need the array as a whole (as in
arr**2 or sum(arr)).
The arr elements are used in several simple arithmetic operations involving nothing costlier than
+, -, *, /, and numpy.log(). There is no other loop inside this function; there are a few if’s though.
Inside this function, use is made of constants imported from other modules (I doubt the
importing, as in AnotherModule.x is expensive).
'''
for x in numpy.arange(float1, float2, float3):
do stuff
return a, b, c # Return a tuple of three floats
Edit:
Thanks for all the comments. Here’s the inside of the function (I made the variable names short for convenience). The ndarray array arr has only 3 elements in it. Can you please suggest any improvement?
def compute_something(arr):
a = Mod.b * arr[1] * arr[2] + Mod.c
max = 0.0
for c in np.arange(a, arr[1] * arr[2] * (Mod.d – Mod.e), Mod.f):
i = c / arr[2]
m1 = Mod.A * np.log( (i / (arr[1] *Mod.d)) + (Mod.d/Mod.e))
m2 = -Mod.B * np.log(1.0 - (i/ (arr[1] *Mod.d)) - (Mod.d /
Mod.e))
V = arr[0] * (Mod.E - Mod.r * i / arr[1] - Mod.r * Mod.d -
m1 – m2)
p = c * V /1000.0
if p > max:
max = p
vmp = V
pen = Mod.COEFF1 * (Mod.COEFF2 - max) if max < Mod.CONST else 0.0
wo = Mod.COEFF3 * arr[1] * arr[0] + Mod.COEFF4 * abs(Mod.R5 - vmp) +
Mod.COEFF6 * arr[2]
w = wo + pen
return vmp, max, w
Python supports profiling of code. (module cProfile). Also there is option to use line_profiler to find most expensive part of code tool here.
So you do not need to guessing which part of code is most expensive.
In this code which you presten the problem is in usage for loop which generates many conversion between types of objects. If you use numpy you can vectorize your calculation.
I try to rewrite your code to vectorize your operation. You do not provide information what is Mod object, but I have hope it will work.
def compute_something(arr):
a = Mod.b * arr[1] * arr[2] + Mod.c
# start calculation on vectors instead of for lop
c_arr = np.arange(a, arr[1] * arr[2] * (Mod.d – Mod.e), Mod.f)
i_arr = c_arr/arr[2]
m1_arr = Mod.A * np.log( (i_arr / (arr[1] *Mod.d)) + (Mod.d/Mod.e))
m2_arr = -Mod.B * np.log(1.0 - (i_arr/ (arr[1] *Mod.d)) - (Mod.d /
Mod.e))
V_arr = arr[0] * (Mod.E - Mod.r * i_arr / arr[1] - Mod.r * Mod.d -
m1_arr – m2_arr)
p = c_arr * V_arr / 1000.0
max_val = p.max() # change name to avoid conflict with builtin function
max_ind = np.nonzero(p == max_val)[0][0]
vmp = V_arr[max_ind]
pen = Mod.COEFF1 * (Mod.COEFF2 - max_val) if max_val < Mod.CONST else 0.0
wo = Mod.COEFF3 * arr[1] * arr[0] + Mod.COEFF4 * abs(Mod.R5 - vmp) +
Mod.COEFF6 * arr[2]
w = wo + pen
return vmp, max_val, w
I would suggest to use range as it is approximately 2 times faster:
def python():
for i in range(100000):
pass
def numpy():
for i in np.arange(100000):
pass
from timeit import timeit
print(timeit(python, number=1000))
print(timeit(numpy, number=1000))
Output:
5.59282787179696
10.027646953771665

Converting MATLAB code to Python: Python types and order of operations

This is a MATLAB function from the author of RainbowCrack:
function ret = calc_success_probability(N, t, m)
arr = zeros(1, t - 1);
arr(1) = m;
for i = 2 : t - 1
arr(i) = N * (1 - (1 - 1 / N) ^ arr(i - 1));
end
exp = 0;
for i = 1 : t - 1
exp = exp + arr(i);
end
ret = 1 - (1 - 1 / N) ^ exp;
It calculates the probability of success in finding a plaintext password given a rainbow table with keyspace N, a large unsigned integer, chain of length t, and number of chains m.
A sample run:
calc_success_probability(80603140212, 2400, 40000000)
Returns 0.6055.
I am having difficulty converting this into Python. In Python 3, there is no max integer anymore, so N isn't an issue. I think in the calculations I have to force everything to a large floating point number, but I'm not sure.
I also don't know the order of operations in MATLAB. I think the code is saying this:
Create array of size [1 .. 10] so ten elements
Initialize every element of that array with zero
In zero-based indexing, I think this would be array[0 .. t-1], it looks like MATLAB uses 1 as the first (0'th) index.
Then second element of array (0-based indexing) initialized to m.
For each element in array, pos=1 (0-based indexing) to t-1:
array[pos] = N * (1 - (1 - 1/N) ** array[pos-1]
Where ** is the power operator. I think power is ^ in MATLAB, so N * (1 - (1-1/N) to the array[pos-1] power is like that above.
Then set an exponent. For each element in array 0 to t-1:
exponent is exponent + 1
return probability = 1 - (1 - 1/N) power of exp;
My Python code looks like this, and doesn't work. I can't figure out why, but it could be that I don't understand MATLAB enough, or Python, both, or I'm reading the math wrong somehow and what's going on in MATLAB is not what I'm expecting, i.e. I have order of operations and/or types wrong to make it work and I'm missing something in those terms...
def calc_success_probability(N, t, m):
comp_arr = []
# array with indices 1 to t-1 in MATLAB, which is otherwise 0 to t-2???
# range with 0, t is 0 to t excluding t, so t here is t-1, t-1 is up
# to including t-2... sounds wrong...
for i in range(0, t-1):
# initialize array
comp_arr.append(0)
print("t = {0:d}, array size is {1:d}".format(t, len(comp_arr)))
# zero'th element chain count
comp_arr[0] = m
for i in range(1, t-1):
comp_arr[i] = N * (1 - (1 - 1 / N)) ** comp_arr[i-1]
final_exp = 0
for i in range(0, t-1):
final_exp = final_exp + comp_arr[i]
probability = (1 - (1 - 1 / N)) ** final_exp
return probability
Watch your brackets! You have translated this:
arr(i) = N * ( 1 - ( 1 - 1 / N ) ^ arr(i - 1) );
to this:
comp_arr[i] = N * ( 1 - ( 1 - 1 / N ) ) ** comp_arr[i-1]
I've lined up everything so you can better see where it goes wrong. You've moved a bracket to the wrong location.
It should be:
comp_arr[i] = N * ( 1 - ( 1 - 1 / N ) ** comp_arr[i-1] )
Similarly,
ret = 1 - (1 - 1 / N) ^ exp;
is not the same as
probability = (1 - (1 - 1 / N)) ** final_exp
This should be
probability = 1 - (1 - 1 / N) ** final_exp

Series Expansion of cos with Python

So, I'm trying to find the value of cos(x), where x=1.2. I feel the script I have written should be fine, however, the value I get out isn't correct. That is; cos(1.2)=0.6988057880877979, for 25 terms, when I should get out: cos(1.2)=0.36235775.
I have created a similar program for calculating sin(1.2) which works fine.
Calculating sin(1.2):
import math as m
x=1.2
k=1
N=25
s=x
sign=1.0
while k<N:
sign=-sign
k=k+2
term=sign*x**k/m.factorial(k)
s=s+term
print('sin(%g) = %g (approximation with %d terms)' % (x,s,N))
Now trying to calculate cos(1.2):
import math as m
x=1.2
k=1
N=25
s=x
sign=1.0
while k<N:
sign=-sign
k=k+1
term=sign*x**k/m.factorial(k)
s=s+term
print(s)
You shouldn't be setting your initial sum to 1.2, and your representation of the expansion
is a bit off - we need to account for the even-ness of the function, so increment k by 2. Also, without modifying your program structure, you'd have to set the initial variables so they are correctly put to their starting values at the beginning of the first loop. Re-ordering your loop control flow a bit, we have
import math as m
x=1.2
k=0
N=25
s=0
sign=1.0
while k<N:
term=sign*x**(k)/m.factorial(k)
s=s+term
k += 2
sign = -sign
print(s)
Gives
0.3623577544766735
I think you're using the wrong series for the cosine, the correct formula would be (I highlighted the important differences with ^):
sum_over_n [(-1)**n * x ** (2 * n) / (math.factorial(2 * n))]
# ^^^^ ^^^^
that means to add n-terms you have something like:
def cosine_by_series(x, terms):
cos = 0
for n in range(terms):
cos += ((-1)**n) * (x ** (2*n)) / (math.factorial(2 * n))
return cos
# or simply:
# return sum(((-1)**n) * (x ** (2*n)) / (math.factorial(2 * n)) for n in range(terms)
which gives:
>>> cosine_by_series(1.2, 30)
0.3623577544766735

Categories

Resources