For given function (with one parameter, parameter type and return value type are equal) generate another function that applies original fuction multiple times.
Example:
from math import sin
f1 = fn(lambda x: "sin(%s)" % x, 5)
f2 = fn(lambda x: sin(x), 5)
print("%s = %f" % (f1("1"), f2(1)))
>>sin(sin(sin(sin(sin(1))))) = 0.587181
print("%s = %f" % (f1("2"), f2(2)))
>>sin(sin(sin(sin(sin(2))))) = 0.606464
print(fn(lambda x: sin(x), 0)(1000))
>> 1000
Function signature: def fn (f, n)
Using 'fn' (see first part of the task) generate function that calculates golden ratio approximation.
final formula:
1 + 1 / (1 + 1 / (1 + 1 / (...)))
some results:
golden_ratio(0) = 1
golden_ratio(1) = 2
golden_ratio(2) = 1.5
golden_ratio(100) = 1.6180...
Function signature: def golden_ratio (n), where 'n' is number of invocations (and number of terms in continued fraction)
That's what I did, the golden ratio calculates everything well. But with the first part of the task, something is wrong, as in the example ...
first function fn which will apply a similar function a given number of times
second function - golden ratio which should use fn to calculate
code:
n = 100
def golden_ratio(f,n):
def wrap(*arg):
for _ in range(n+1):
arg = f(*arg)
return arg
return wrap
def fib(q, w):
q, w = w, q + w
return q, w
f3 = golden_ratio(fib, n)
q, w = f3(0, 1)
# gold section
print(w / q)
it is executed, but not quite correctly, as in the example
Try to use this:
def fn(f, n):
r = 1/2
for i in range(n):
r = f(r)
return r
f = lambda x: 1+(1/x)
fn(f, 20) # answer: 1.6180339789779863
If you want a general function which can apply any type of function.
from math import sin
def fn(f, n):
def wrap(*arg):
for _ in range(n):
if isinstance(arg, tuple):
arg = f(*arg)
else:
arg = f(arg)
return arg
return wrap
f1 = fn(sin, 5)
f1(1)
def fib(q, w):
q, w = w, q + w
return q, w
f2 = fn(fib, 100)
q, w = f2(0, 1)
print(w/q)
Golden ratio
def golden_ratio(n):
f = fn(fib, n+1)
q, w = f(0, 1)
return w/q
print(golden_ratio(0))
print(golden_ratio(1))
print(golden_ratio(2))
print(golden_ratio(100))
Output
1.0
2.0
1.5
1.618033988749895
Use Lambda
def golden_ratio(n):
f = fn(lambda x, y: (y, x+y), n+1)
q, w = f(0, 1)
return w/q
Or
def golden_ratio(n):
f = fn(lambda x: 1 + (1/x) if x != 0 else 1, n+1)
return f(0)
Related
So im tasked with using the 4th order Runge Kutta Meathod to solve the 2nd order differential equation of a damped occilator.
my function for the runge-kutta meathod looks as such
def RungeKutta(f,y0,x):
y=np.zeros((len(x),len(y0)))
y[0,:]=np.array(y0)
h=x[1]-x[0]
for i in range(0,len(x)-1):
k1=h*np.array(f(y[i,:],x[i]))
k2=h*np.array(f(y[i,:]+k1/2,x[i]+h/2))
k3=h*np.array(f(y[i,:]+k2/2,x[i]+h/2))
k4=h*np.array(f(y[i,:]+k3,x[i]+h))
y[i+1,:]=y[i,:]+k1/6+k2/3+k3/3+k4/6
return y
the rungeKutta function works fine, and I have tested it with a list of example inputs so that doesnt seem to be the problem
im given
question parameters
and have to make a class to solve the problem
class harmonicOscillator:
def __init__(self,m,c,k):
if((m>0) and ((type(m) == int) or (type(m) == float))):
self.m = m
else:
raise ValueError
if((c>0) and ((type(c) == int) or (type(c) == float))):
self.c = c
else:
raise ValueError
if((k>0) and ((type(k) == int) or (type(k) == float))):
self.k = k
else:
raise ValueError
def period(self):
self.T = 2 * np.pi * (self.m / self.k)**(0.5)
return(self.T)
def solve(self, func, y0):
m = self.m
c = self.c
k = self.k
T = self.T
t = np.linspace(0,10*T,1000)
but im unsure where to really progress. ive tried turning the 2nd order differential equation into a lambda function like such
F = lambda X,t: [X[1], (-c) * X[1] + (-k) * X[0] + func(t)]
and then passing that into my RungeKutta function
result = RungeKutta(F, y0, t, func)
return(result)
but im not really well versed in lambda functions and am clearly going wrong somewhere.
an example input that it should pass would be something like this
####### example inputs #######
m=1
c=0.5
k=2
a = harmonicOscillator(m,c,k)
a.period()
x0 = [0,0]
tHO,xHO= a.solve(lambda t: omega0**2,x0)
would really appreciate some help. the requirments for the questions are that I have to use the above rungeKutta function, but im just kind of lost at this point
thanks.
I think there may be some confusion over the external forcing term and the Runge Kutta derivative helper function F. The F in RK4 returns the derivative dX/dt of the system of first order differential equations X. The forcing term in a damped oscillator is unfortunately also called F but it is a function of t.
One of your issues is that the arity (number of parameters) of your RungeKutta() function and your call to that function do not match: you tried to do RungeKutta(F, y0, t, func), but the RungeKutta() function only takes arguments (f, y0, x) in that order.
In other words, the f parameter in your current RungeKutta() function should encapsulate the forcing function F(t).
You can do this with helpers:
# A constant function in your case, but this can be any function of `t`
def applied_force(t):
# Note, you did not provide a value for `omega0`
return omega0 ** 2
def rk_derivative_factory(c, k, F):
return lambda X, t: np.array([X[1], -c * X[1] - k * X[0] + F(t)])
The rk_derivative_factory() is a function which takes a damping coefficient, a spring constant, and a forcing function F(t), and returns a function which takes a system X and a time step t as arguments (because this is what is demanded of you by the implementation of RungeKutta()).
Then,
omega0 = 0.234
m, c, k = 1, 0.25, 2
oscillator = HarmonicOscillator(m, c, k)
f = rk_derivative_factory(oscillator, applied_force)
x_osc = oscillator.solve(f, [1, 0])
Where solve() is defined like so:
def solve(self, func, y0):
t = np.linspace(0,10 * self.period(), 1000)
return RungeKutta(f, y0, t)
As an aside, I strongly recommend being more consistent about your variable names. You named the initial state of your oscillator x0, and were passing it to RungeKutta() as the argument for the parameter y0, and the x parameter of RungeKutta() represents time... Gets pretty confusing.
Full solution
Lastly, your implementation of RK4 wasn't quite correct, so I've fixed that and made some other slight improvements.
Note that one thing you might want to consider is making the HarmonicOscillator.solve() function take a solver. Then you could play around with different integrators.
import numpy as np
def RungeKutta(f, y0, x):
y = np.zeros((len(x), len(y0)))
y[0, :] = np.array(y0)
h = x[1] - x[0]
for i in range(0, len(x) - 1):
# Many slight changes below
k1 = np.array(f(y[i, :], x[i]))
k2 = np.array(f(y[i, :] + h * k1 / 2, x[i] + h / 2))
k3 = np.array(f(y[i, :] + h * k2 / 2, x[i] + h / 2))
k4 = np.array(f(y[i, :] + h * k3, x[i] + h))
y[i + 1, :] = y[i, :] + (h / 6) * (k1 + 2 * k2 + 2 * k3 + k4)
return y
# A constant function in your case, but this can be any function of `t`
def applied_force(t):
# Note, you did not provide a value for `omega0`
return omega0 ** 2
def rk_derivative_factory(osc, F):
return lambda X, t: np.array([X[1], (F(t) - osc.c * X[1] - osc.k * X[0]) / osc.m])
class HarmonicOscillator:
def __init__(self, m, c, k):
if (type(m) in (int, float)) and (m > 0):
self.m = m
else:
raise ValueError("Parameter 'm' must be a positive number")
if (type(c) in (int, float)) and (c > 0):
self.c = c
else:
raise ValueError("Parameter 'c' must be a positive number")
if (type(k) in (int, float)) and (k > 0):
self.k = k
else:
raise ValueError("Parameter 'k' must be a positive number")
self.T = 2 * np.pi * (self.m / self.k)**(0.5)
def period(self):
return self.T
def solve(self, func, y0):
t = np.linspace(0, 10 * self.period(), 1000)
return RungeKutta(func, y0, t)
Demo:
import plotly.graph_objects as go
omega0 = 0.234
m, c, k = 1, 0.25, 2
oscillator = HarmonicOscillator(m, c, k)
f = rk_derivative_factory(oscillator, applied_force)
x_osc = oscillator.solve(f, [1, 0])
x, dx = x_osc.T
t = np.linspace(0, 10 * oscillator.period(), 1000)
fig = go.Figure(go.Scatter(x=t, y=x, name="x(t)"))
fig.add_trace(go.Scatter(x=t, y=dx, name="x'(t)"))
Output:
I am writing a program that operates out of a main() function. I am unable to change the main function (this is part of a class). How would I go about carrying over a variable from one function to the next, without changing the main()?
def read_data(fname) :
file = fname
x = []
y = []
with open(file) as f:
for line in f:
xi, yi = [float(x) for x in line.split(",")]
x.append(xi)
y.append(yi)
return x, y
def compute_m_and_b(x, y) :
sx, sy, sx2, sxy, sy2 = 0, 0, 0, 0, 0
for i in range(len(x)):
sx += x[i]
sy += y[i]
sx2 += (x[i] ** 2)
sy2 += (y[i] ** 2)
sxy += (x[i] * y[i])
m = (sxy * len(x) - sx * sy) / (sx2 * len(x) - sx**2)
b = (sy - m * sx) / len(x)
return m, b
def compute_fx_residual(x, y, m, b) :
fx = []
for xi in x:
fx.append(m * xi + b)
residual = []
for i in range(len(y)):
residual.append(y[i] - fx[i])
return fx, residual
def compute_sum_of_squared_residuals(residual) :
least_squares_r = 0
for i in range(len(y)) :
least_squares_r += (residual[i]) ** 2
return least_squares_r
def compute_total_sum_of_squares(y) :
sum_squares = 0
ymean = sum(y) / len(y)
for i in range(len(y)) :
sum_squares += (yi - ymean) ** 2
return sum_squares
as you can see, I am restricted to only pulling the variables listed in the parentheses of the def functions. This leads to variables calculated in prior functions being undefined. How can I import them without needing to change the main()?
Please let me know if I should be more clear. I can provide more examples, but I wanted to maintain some brevity.
EDIT: here is the main function:
def main():
fname = input("Enter Input Filename: ")
x, y = regress.read_data(fname)
print()
print("Input File: ", fname)
print("Data points: ", len(x))
#compute coefficients m and b
m, b = regress.compute_m_and_b(x, y)
#compute fx and residual
fx, residual = regress.compute_fx_residual(x, y, m, b)
#compute sum of squared residuals
least_squares_r = regress.compute_sum_of_squared_residuals(residual)
#compute sum of squares
sum_squares = regress.compute_total_sum_of_squares(y)
#compute coefficeint of determination
coeff_of_d = regress.compute_coeff_of_determination(least_squares_r, sum_squares)
regress.print_least_squares(x, y, m, b, fx, residual, least_squares_r, sum_squares, coeff_of_d)
#compute pearson coefficient
pearson_r = regress.compute_pearson_coefficient(x, y)
regress.print_pearson(pearson_r)
return
main()
You haven't provided the main function so it's unclear how you're currently using it.
Looks to me like you can just get the variable for each consecutive function and pass them into the next:
fname = "some/path/to/file.txt"
x, y = read_data(fname)
m, b = compute_m_and_b(x, y, m, b)
fx, residual = compute_fx_residual(x, y, m, b)
least_squares_r = compute_sum_of_squared_residuals(residual)
sum_squares = compute_total_sum_of_squares(y)
trigonometric function by Newton method is giving wrong results
def func(x):
return radians(math.sin(x)+log(x)+1)
def derivFunc(x):
return radians((1/x) + math.cos(x))
#sin(x)+log(x)+1 --is the function i want to apply method on
**# Function to find the root**
def newtonRaphson(x):
h = func(x) / derivFunc(x)
while abs(h) >= 0.0001:
h = func(x) / derivFunc(x)
# x(i+1) = x(i) - f(x) / f'(x)
x = x - h
print("The value of the root is : ",
"%.4f" % x)
x0 = 0.03
newtonRaphson(x0)
The problem is the radians, which multiplies a part of an expression by pi/180.0. Remove all of its mentions, and it should be fine.
EDIT
The below works absolutely fine now:
import math
def func(x):
return math.sin(x)+math.log10(x)+1
def derivFunc(x):
return (1/x) + math.cos(x)
#sin(x)+log(x)+1 --is the function i want to apply method on
# Function to find the root
def newtonRaphson(x):
h = func(x) / derivFunc(x)
while abs(h) >= 0.0001:
h = func(x) / derivFunc(x)
# x(i+1) = x(i) - f(x) / f'(x)
x = x - h
print("The value of the root is : ",
"%.4f" % x)
x0 = 0.03
newtonRaphson(x0)
Here is the homework assignment I'm trying to solve:
A further improvement of the approximate integration method from the last question is to divide the area under the f(x) curve into n equally-spaced trapezoids.
Based on this idea, the following formula can be derived for approximating the integral:
!(https://www.dropbox.com/s/q84mx8r5ml1q7n1/Screenshot%202017-10-01%2016.09.32.png?dl=0)!
where h is the width of the trapezoids, h=(b−a)/n, and xi=a+ih,i∈0,...,n, are the coordinates of the sides of the trapezoids. The figure above visualizes the idea of the trapezoidal rule.
Implement this formula in a Python function trapezint( f,a,b,n ). You may need to check and see if b > a, otherwise you may need to swap the variables.
For instance, the result of trapezint( math.sin,0,0.5*math.pi,10 ) should be 0.9979 (with some numerical error). The result of trapezint( abs,-1,1,10 ) should be 2.0
This is my code but It doesn't seem to return the right values.
For print ((trapezint( math.sin,0,0.5*math.pi,10)))
I get 0.012286334153465965, when I am suppose to get 0.9979
For print (trapezint(abs, -1, 1, 10))
I get 0.18000000000000002, when I am suppose to get 1.0.
import math
def trapezint(f,a,b,n):
g = 0
if b>a:
h = (b-a)/float(n)
for i in range (0,n):
k = 0.5*h*(f(a+i*h) + f(a + (i+1)*h))
g = g + k
return g
else:
a,b=b,a
h = (b-a)/float(n)
for i in range(0,n):
k = 0.5*h*(f(a + i*h) + f(a + (i + 1)*h))
g = g + k
return g
print ((trapezint( math.sin,0,0.5*math.pi,10)))
print (trapezint(abs, -1, 1, 10))
Essentially, your return g statement was indented, when it should not have been.
Also, I removed your duplicated code, so it would adhere to "DRY" "Don't Repeat Yourself" principle, which prevents errors, and keeps code simplified and more readable.
import math
def trapezint(f, a, b, n):
g = 0
if b > a:
h = (b-a)/float(n)
else:
h = (a-b)/float(n)
for i in range (0, n):
k = 0.5 * h * ( f(a + i*h) + f(a + (i+1)*h) )
g = g + k
return g
print ( trapezint( math.sin, 0, 0.5*math.pi, 10) )
print ( trapezint(abs, -1, 1, 10) )
0.9979429863543573
1.0000000000000002
This variation reduces the complexity of branches and reduces number of operations. The summation in last step is reduced to single operation on an array.
from math import pi, sin
def trapezoid(f, a, b, n):
if b < a:
a,b = b, a
h = (b - a)/float(n)
g = [(0.5 * h * (f(a + (i * h)) + f(a + ((i + 1) * h)))) for i in range(0, n)]
return sum(g)
assert trapezoid(sin, 0, 0.5*pi, 10) == 0.9979429863543573
assert trapezoid(abs, -1, 1, 10) == 1.0000000000000002
Fsolve in Scipy seems to be the right candidate for this, I just need help passing equations dynamically. I appreciate any thoughts in advance.
By dynamic I mean number of equations differ from one run to another for example one situation i have :
alpha*x + (1-alpha)*x*y - y = 0
beta*x + (1- beta)*x*z - z = 0
A*x + B*y + C*z = D
and another situation i have:
alpha*x + (1-alpha)*x*y - y = 0
beta*x + (1- beta)*x*z - z = 0
gama*x + (1 -gama)*x*w - w =0
A*x + B*y + C*z + D*w = E
alpha, beta, A, B, C, D and E are all constants. x, y, z, w are variables.
I haven't used Fsolve myself, but according to its documentation it takes a callable function.
Something like this handles multiple functions with unknown number of variables. Bear in mind that the args must be ordered correctly here, but each function simply takes a list.
def f1(argList):
x = argList[0]
return x**2
def f2(argList):
x = argList[0]
y = argList[1]
return (x+y)**2
def f3(argList):
x = argList[0]
return x/3
fs = [f1,f2,f3]
args = [3,5]
for f in fs:
print f(args)
For Fsolve, you could try something like this (untested):
def func1(argList, constList):
x = argList[0]
y = argList[1]
alpha = constList[0]
return alpha*x + (1-alpha)*x*y - y
def func2(argList, constList):
x = argList[0]
y = argList[1]
z = argList[2]
beta = constList[1]
return beta*x + (1- beta)*x*z - z
def func3(argList, constList):
x = argList[0]
w = argList[1] ## or, if you want to pass the exact same list to each function, make w argList[4]
gamma = constList[2]
return gama*x + (1 -gama)*x*w - w
def func4(argList, constList):
return A*x + B*y + C*z + D*w -E ## note that I moved E to the left hand side
functions = []
functions.append((func1, argList1, constList1, args01))
# args here can be tailored to fit your function structure
# Just make sure to align it with the way you call your function:
# args = [argList, constLit]
# args0 can be null.
functions.append((func1, argList2, constList2, args02))
functions.append((func1, argList3, constList3, args03))
functions.append((func1, argList4, constList4, args04))
for func,argList, constList, args0 in functions: ## argList is the (Vector) variable you're solving for.
Fsolve(func = func, x0 = ..., args = constList, ...)