Python: Fourth Order Runge-Kutta Method - python

from math import sin
from numpy import arange
from pylab import plot,xlabel,ylabel,show
def answer():
print('Part a:')
print(low(x,t))
print('First Graph')
print('')
def low(x,t):
return 1/RC * (V_in - V_out)
a = 0.0
b = 10.0
N = 1000
h = (b-a)/N
RC = 0.01
V_out = 0.0
tpoints = arange(a,b,h)
xpoints = []
x = 0.0
for t in tpoints:
xpoints.append(x)
k1 = h*f(x,t)
k2 = h*f(x+0.5*k1,t+0.5*h)
k3 = h*f(x+0.5*k2,t+0.5*h)
k4 = h*f(x+k3,t+h)
x += (k1+2*k2+2*k3+k4)/6
plot(tpoints,xpoints)
xlabel("t")
ylabel("x(t)")
show()
So I have the fourth order runge kutta method coded but the part I'm trying to fit in is where the problem say V_in(t) = 1 if [2t] is even or -1 if [2t] is odd.
Also the I'm not sure if I'm suppose to return this equation:
return 1/RC * (V_in - V_out)
Here is the problem:
Problem 8.1
It would be greatly appreciated if you help me out!

So I have the fourth order runge kutta method coded but the part I'm trying to fit in is where the problem say V_in(t) = 1 if [2t] is even or -1 if [2t] is odd.
You are treating V_in as a constant. The problem says that it's a function. So one solution is to make it a function! It's a very simple function to write:
def dV_out_dt(V_out, t) :
return (V_in(t) - V_out)/RC
def V_in(t) :
if math.floor(2.0*t) % 2 == 0 :
return 1
else :
return -1
You don't need or want that if statement in the definition of V_in(t). A branch inside of a loop is expensive, and this function will be called many times from inside a loop. There's a simple way to avoid that if statement.
def V_in(t) :
return 1 - 2*(math.floor(2.0*t) % 2)
This function is so small that you can fold it into the derivative function:
def dV_out_dt(V_out, t) :
return ((1 - 2*(math.floor(2.0*t) % 2)) - V_out)/RC

The function should look something like this:
def f(x,t):
V_out = x
n = floor(2*t)
V_in = (1==n%2)? -1 : 1
return 1/RC * (V_in - V_out)

Related

Optimizing mathematical formula implementation with concatenations of summations

I'm trying to implement this following formula in Python. It's basically a long concatenation os summations, where an additional summation is added each time a new 'element' is needed. To simply explain the formula's structure, here's how this formula goes in order from 2 to 5 elements:
2 elements
3 elements
4 elements
5 elements
By the way, here's the g function shown in the formulas:
g function
Now, I foolishly tried coding this formula with my extremely barebones python programming skills. The initial goal was to try this with 15 elements, but given that it contained a lot of nested for loops and factorials, I quickly noticed that I could not really obtain a result from that.
At the end I ended up with this monstrous code, that would finish just after the heat death of the universe:
from ast import Str
import math
pNuevos = [0,2,2,2,2,1,1,1,2,2,2,1,2,2,1,1]
pTotales = [0,10,10,7,8,7,7,7,7,7,10,7,8,7,8,8]
def PTirada (personajes):
tirada = 0.05/personajes
return tirada
def Ppers1 (personajes, intentos):
p1pers = ((math.factorial(intentos-1)) / ((math.factorial(4))*(math.factorial(intentos-5)))) * (PTirada(personajes)**5) * ((1-PTirada(personajes))**(intentos-5))
return p1pers
def Ppers2 (personajes, intentos):
p2pers = 0
for i in range(10,intentos+1):
p2pers = p2pers + ( (math.factorial(intentos-1)) / ((math.factorial(4))*(math.factorial(i-5))*(math.factorial(intentos-i))) ) * (PTirada(personajes)**i) * ((1 - 2*(PTirada(personajes))) **(intentos-i))
p2pers = 2*p2pers
return p2pers
def Activate (z) :
probability1 = 0
probability2 = 0
probability3 = 0
probability4 = 0
probability5 = 0
probability6 = 0
probability7 = 0
probability8 = 0
probability9 = 0
probability10 = 0
probability11 = 0
probability12 = 0
probability13 = 0
probability14 = 0
for i in range (5*pNuevos[1], z-5*pNuevos[2]+1):
for j in range (5*pNuevos[2], z-i-5*pNuevos[3]+1):
for k in range (5*pNuevos[3], z-j-i-5*pNuevos[4]+1):
for l in range (5*pNuevos[4], z-k-j-i-5*pNuevos[5]+1):
for m in range (5*pNuevos[5], z-l-k-j-i-5*pNuevos[6]+1):
for n in range (5*pNuevos[6], z-m-l-k-j-i-5*pNuevos[7]+1):
for o in range (5*pNuevos[7], z-n-m-l-k-j-i-5*pNuevos[8]+1):
for p in range (5*pNuevos[8], z-o-n-m-l-k-j-i-5*pNuevos[9]+1):
for q in range (5*pNuevos[9], z-p-o-n-m-l-k-j-i-5*pNuevos[10]+1):
for r in range (5*pNuevos[10], z-q-p-o-n-m-l-k-j-i-5*pNuevos[11]+1):
for s in range (5*pNuevos[11], z-r-q-p-o-n-m-l-k-j-i-5*pNuevos[12]+1):
for t in range (5*pNuevos[12], z-s-r-q-p-o-n-m-l-k-j-i-5*pNuevos[13]+1):
for u in range (5*pNuevos[13], z-t-s-r-q-p-o-n-m-l-k-j-i-5*pNuevos[14]+1):
for v in range (5*pNuevos[14], z-u-t-s-r-q-p-o-n-m-l-k-j-i-5*pNuevos[15]+1):
probability14 = probability14 + eval("Ppers"+str(pNuevos[14])+"("+str(pTotales[14])+","+str(v)+")") * eval("Ppers"+str(pNuevos[15])+"("+str(pTotales[15])+","+str(z-v-u-t-s-r-q-p-o-n-m-l-k-j-i)+")")
probability13 = probability13 + eval("Ppers"+str(pNuevos[13])+"("+str(pTotales[13])+","+str(u)+")") * probability14
probability12 = probability12 + eval("Ppers"+str(pNuevos[12])+"("+str(pTotales[12])+","+str(t)+")") * probability13
probability11 = probability11 + eval("Ppers"+str(pNuevos[11])+"("+str(pTotales[11])+","+str(s)+")") * probability12
probability10 = probability10 + eval("Ppers"+str(pNuevos[10])+"("+str(pTotales[10])+","+str(r)+")") * probability11
probability9 = probability9 + eval("Ppers"+str(pNuevos[9])+"("+str(pTotales[9])+","+str(q)+")") * probability10
probability8 = probability8 + eval("Ppers"+str(pNuevos[8])+"("+str(pTotales[8])+","+str(p)+")") * probability9
probability7 = probability7 + eval("Ppers"+str(pNuevos[7])+"("+str(pTotales[7])+","+str(o)+")") * probability8
probability6 = probability6 + eval("Ppers"+str(pNuevos[6])+"("+str(pTotales[6])+","+str(n)+")") * probability7
probability5 = probability5 + eval("Ppers"+str(pNuevos[5])+"("+str(pTotales[5])+","+str(m)+")") * probability6
probability4 = probability4 + eval("Ppers"+str(pNuevos[4])+"("+str(pTotales[4])+","+str(l)+")") * probability5
probability3 += eval("Ppers"+str(pNuevos[3]) + "("+str(pTotales[3])+","+str(k)+")") * probability4
probability2 += eval("Ppers"+str(pNuevos[2]) + "("+str(pTotales[2])+","+str(j)+")") * probability3
probability1 += eval("Ppers"+str(pNuevos[1]) + "("+str(pTotales[1])+","+str(i)+")") * probability2
return probability1
print (str(Activate(700)))
Edit: Alright I think it would be helpful to explain a couple things:
-First of all, I was trying to find ways the code could run faster, as I'm aware the nested for loops are a performance hog. I was also hoping there would be a way to optimize so many factorial operations.
-Also, the P(A) function described in the g function represents the probability of an event happening, which is already in the code, in the first function from the top.
There's also the function f in the formula, which is just a simplification of the function g for specific cases.
The function f is the second function in the code, whereas g is the third function in the code.
I will try to find a way to simplify the multiple summations, and thanks for the tip of not using eval()!
I'm sorry again for not specifying the question more, and for that mess of code also.
I would expect to break it down with something like this:
def main():
A = 0.5
m = 10
result = g(A, m)
return
def sigma(k, m):
''' function to deal with the sum loop'''
for k in range(10, m+1):
# the bits in the formula
pass
return
def g(A, m):
''' function to deal with g '''
k=10
return 2 * sigma(k,m)
if __name__=='__main__':
''' This is executed when run from the command line '''
main()
Or alternatively to do similar with classes.
I expect you also need a function for p(A) and one for factorials.

(Python) Markov, Chebyshev, Chernoff upper bound functions

I'm stuck with one task on my learning path.
For the binomial distribution X∼Bp,n with mean μ=np and variance σ**2=np(1−p), we would like to upper bound the probability P(X≥c⋅μ) for c≥1.
Three bounds introduced:
Formulas
The task is to write three functions respectively for each of the inequalities. They must take n , p and c as inputs and return the upper bounds for P(X≥c⋅np) given by the above Markov, Chebyshev, and Chernoff inequalities as outputs.
And there is an example of IO:
Code:
print Markov(100.,0.2,1.5)
print Chebyshev(100.,0.2,1.5)
print Chernoff(100.,0.2,1.5)
Output
0.6666666666666666
0.16
0.1353352832366127
I'm completely stuck. I just can't figure out how to plug in all that math into functions (or how to think algorithmically here). If someone could help me out, that would be of great help!
p.s. and all libs are not allowed by task conditions except math.exp
Ok, let's look at what's given:
Input and derived values:
n = 100
p = 0.2
c = 1.5
m = n*p = 100 * 0.2 = 20
s2 = n*p*(1-p) = 16
s = sqrt(s2) = sqrt(16) = 4
You have multiple inequalities of the form P(X>=a*m) and you need to provide bounds for the term P(X>=c*m), so you need to think how a relates to c in all cases.
Markov inequality: P(X>=a*m) <= 1/a
You're asked to implement Markov(n,p,c) that will return the upper bound for P(X>=c*m). Since from
P(X>=a*m)
= P(X>=c*m)
it's clear that a == c, you get 1/a = 1/c. Well, that's just
def Markov(n, p, c):
return 1.0/c
>>> Markov(100,0.2,1.5)
0.6666666666666666
That was easy, wasn't it?
Chernoff inequality states that P(X>=(1+d)*m) <= exp(-d**2/(2+d)*m)
First, let's verify that if
P(X>=(1+d)*m)
= P(X>=c *m)
then
1+d = c
d = c-1
This gives us everything we need to calculate the uper bound:
def Chernoff(n, p, c):
d = c-1
m = n*p
return math.exp(-d**2/(2+d)*m)
>>> Chernoff(100,0.2,1.5)
0.1353352832366127
Chebyshev inequality bounds P(X>=m+k*s) by 1/k**2
So again, if
P(X>=c*m)
= P(X>=m+k*s)
then
c*m = m+k*s
m*(c-1) = k*s
k = m*(c-1)/s
Then it's straight forward to implement
def Chebyshev(n, p, c):
m = n*p
s = math.sqrt(n*p*(1-p))
k = m*(c-1)/s
return 1/k**2
>>> Chebyshev(100,0.2,1.5)
0.16

Converting recursive algorithm to Iterative

I have done the Recursive function in Python that works:
def Rec(n):
if (n<=5):
return 2*n
elif (n>=6):
return Rec(n-6)+2*Rec(n-4)+4*Rec(n-2)
print (Rec(50))
But I can't think of an iterative one
I am sure I will need to use a loop and possibly have 4 variables to store the previous values, imitating a stack.
For your particular question, assuming you have an input n, the following code should calculate the function iteratively in python.
val = []
for i in range(6):
val.append(2*i)
for i in range(6,n+1):
val.append( val[i-6] + 2*val[i-4] + 4*val[i-2] )
print(val[n])
I get this answer:
$ python test.py
Rec(50) = 9142785252232708
Kist(50) = 9142785252232708
Using the code below. The idea is that your function needs a "window" of previous values - Kn-6, Kn-4, Kn-2 - and that window can be "slid" along as you compute new values.
So, for some value like "14", you would have a window of K8, K9, ... K13. Just compute using those values, then drop K8 since you'll never use it again, and append K14 so you can use it in computing K15..20.
def Rec(n):
if (n<=5):
return 2*n
elif (n>=6):
return Rec(n-6)+2*Rec(n-4)+4*Rec(n-2)
def Kist(n):
if n <= 5:
return 2 * n
KN = [2*n for n in range(6)]
for i in range(6, n+1):
kn = KN[-6] + 2 * KN[-4] + 4 * KN[-2]
KN.append(kn)
KN = KN[-6:]
return KN[-1]
print("Rec(50) =", Rec(50))
print("Kist(50) =", Kist(50))

Double Asterisk

I'm new to Python and really stumped on this. I'm reading from a book and the code works fine; I just don't get it!
T[i+1] = m*v[i+1]ˆ**/L
What's with the double asterisk part of this code? It's even followed by a forward slash. The variable L is initialized with the value 1.0 However, it looks like someone slumped over the keyboard, but the code works fine. Is this a math expression or something more? I would appreciate the help understanding this. Thanks!
full code:
from pylab import *
g = 9.8 # m/sˆ2
dt = 0.01 # s
time = 10.0 # s
v0 = 2.0 # s
D = 0.05 #
L = 1.0 # m
m = 0.5 # kg
# Numerical initialization
n = int(round(time/dt))
t = zeros(n,float)
s = zeros(n,float)
v = zeros(n,float)
T = zeros(n,float)
# Initial conditions
v[0] = v0
s[0] = 0.0
# Simulation loop
i = 0
while (i<n AND T[i]>=0.0):
t[i+1] = t[i] + dt
a = -D/m*v[i]*abs(v[i])-g*sin(s[i]/L)
v[i+1] = v[i] + a*dt
s[i+1] = s[i] + v[i+1]*dt
T[i+1] = m*v[i+1]ˆ**/L + m*g*cos(s[i+1]/L)
i = i + 1
This code is from the book "Elementary Mechanics Using Python: A Modern Course Combining Analytical and Numerical Techniques".
According to the formula on the page 255:
So the Python line should be:
T[i+1] = m*v[i+1]**2/L + m*g*cos(s[i+1]/L)
What's with the double asterisk part of this code?
The answer to your core questions (at least as it exists of this writing) is the double asterisk (star) is power -- "raise to the power". So, i**3 would be "cube i".
My (cross check) source: https://stackoverflow.com/a/1044866/18196

Fit a distribution to a Counter in scipy

I have a collections.Counter object with a count of the occurrences of different values like this:
1:193260
2:51794
3:19112
4:9250
5:6486
How can I fit a probability distribution to this data in scipy? scipy.stats.expon.fit() seems to want a list of numbers. It seems wasteful to create a list with 193260 [1]s, 51794 [2]s, etc. Is there a more elegant or efficient way?
It looks like scipy.stats.expon.fit is basically a small wrapper over scipy.optimize.minimize, where it first creates a function to compute neg-log-likelihood, and then uses scipy.optimize.minimize to fit the pdf parameters.
So, I think what you need to do here is write your own function that computes the neg-log-likelihood of the counter object, and then call scipy.optimize.minimize yourself.
More specifically, scipy defines the expon 'scale' parameter here
http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.expon.html
So, the pdf is:
pdf(x) = 1 / scale * exp ( - x / scale)
So, taking the logarithm of both sides we get:
log_pdf(x) = - log(scale) - x / scale
Therefore, the negative-log-likelikehood of everything in your counter object would be:
def neg_log_likelihood(scale):
total = 0.0
for x, count in counter.iteritems():
total += (math.log(scale) + x / scale) * count
return total
Here is a program to try this out.
import scipy.stats
import scipy.optimize
import math
import collections
def fit1(counter):
def neg_log_likelihood(scale):
total = 0.0
for x, count in counter.iteritems():
total += (math.log(scale) + x / scale) * count
return total
optimize_result = scipy.optimize.minimize(neg_log_likelihood, [1.0])
if not optimize_result.success:
raise Exception(optimize_result.message)
return optimize_result.x[0]
def fit2(counter):
data = []
# Create an array where each key is repeated as many times
# as the value of the counter.
for x, count in counter.iteritems():
data += [x] * count
fit_result = scipy.stats.expon.fit(data, floc = 0)
return fit_result[-1]
def test():
c = collections.Counter()
c[1] = 193260
c[2] = 51794
c[3] = 19112
c[4] = 9250
c[5] = 6486
print "fit1 'scale' is %f " % fit1(c)
print "fit2 'scale' is %f " % fit2(c)
test()
Here is the output:
fit1 'scale' is 1.513437
fit2 'scale' is 1.513438

Categories