I apologise if there is already an answer to my question, I've searched stack overflow for a while but found nothing that I could use.
I'm learning how to create classes at the moment and I've constructed classes for the explicit Runge-Kutta methods 1-4. The names of the classes are 'RK_1', 'RK_2', 'RK_3' and 'RK_4'. In order to test my code, I decided to solve the Legendre differential equation, which I also created a class for called 'Legendre'.
Now I wanted to solve the problem, so I wrote a function that uses a particular RK scheme and solves the Legendre problem. I wanted to do this for each one of my RK schemes, so I wrote the same function 4 times i.e
def solve_Legendre_1(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_1(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_2(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_2(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_3(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_3(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
def solve_Legendre_4(p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_4(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
However, I realised there must be an easier way to do this. So I thought I might be able to use a loop and str.format() to change the name of the function and get it to take in its corresponding RK scheme, something like
for j in range(4):
def solve_Legendre_%s(p,Tmax,init,dt=0.001) % (j+1):
f = Legendre(p)
solver = RK_%s(init,f) % (j+1)
while solver.now() < Tmax:
solver(dt)
return solver.state()
but obviously this won't work. Does anyone know how I should approach this?
Thanks for your help.
You can simply pass the RK_n() function in as a parameter to avoid duplicating the other function:
def solve_Legendre(p,Tmax,init,dt=0.001, RK=RK_1):
f = Legendre(p)
solver = RK(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
and if you want you can bind that last parameter in advance:
import functools
solve_Legendre_1 = functools.partial(solve_Legendre, RK=RK_1)
solve_Legendre_2 = functools.partial(solve_Legendre, RK=RK_2)
...
You can use arguments not only to give "usual" things, like numbers, lists or strings to a function, you can also use functions themselves as parameters:
>>> def test1():
print(1)
>>> def test2():
print(2)
>>> def solve_with_func(funcname):
funcname()
>>> solve_with_func(test1)
1
>>> solve_with_func(test2)
2
This way you can use the same logic in solve_with_func and simply swap out the function that is executed.
This can of course be extended to lists of functions:
>>> def execute_all(funclist):
for f in funclist:
f()
>>> execute_all([test1, test2])
1
2
Your functions differ only by RK_x function, which you can simply pass by additional variable. This will minimize code redundancy:
def solve_Legendre(RK_func, p,Tmax,init,dt=0.001):
f = Legendre(p)
solver = RK_func(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
Now to your question - you can query globals for this. globals() function will return map with every object defined at global scope keyed by identifier. So you could write (using my previous function):
for j in range(4):
globals()['solve_Legendre_%d' % (j + 1)] = lambda *args, **kw_args: solve_Legendre(globals()['RK_%d' % (j + 1)], *args, **kw_args)
You should add a parameter to you function. So you have one function to solve all your schemes :
def solve_Legendre(p,Tmax,init,dt=0.001, RK):
f = Legendre(p)
solver = RK(init,f)
while solver.now() < Tmax:
solver(dt)
return solver.state()
You can set a default value for RK if you want :
def solve_Legendre(p,Tmax,init,dt=0.001, RK=RK_1):
Related
How to create a functinon in Python such that for example:
n = int(input("number of knots: "))
xsolmed=[]
for i in range(n+1):
xsolmed.append(-1+(2*i/n))
def x(x):
return x
lni=[]
formula=1
for i in range(n+1):
for j in range(n+1):
if i==j:
pass
formula = (x(x)-xsolmed[i])/(xsolmed[j]-xsolmed[i])*formula
I think I need it to return the function such that the formula variable is a function by x in its own right and so later i can call upon it in the fashion
formula(10)=output
Set the results of a function call equal to your desired variable.
def f(x):
"A function that changes nothing"
return x
a = f(5) # same as a = 5
To avoid confusion, I recommend that you don't give functions the same name as their arguments, (i.e., don't do def x(x): ...).
If you want formula to be a function, then declare it as one, after which the correct syntax would be output = formula(10).
formula(10) is instance of a function and hence only has a value not a variable name to assign to.
A good way to write above code will be:
n = int(input("number of knots: "))
xsolmed=[]
for i in range(n+1):
xsolmed.append(-1+(2*i/n))
def y(x):
return x
def formula_calc(X):
formula=1
for i in range(n+1):
for j in range(n+1):
if i==j:
pass
formula = (X-xsolmed[i])/(xsolmed[j]-xsolmed[i])*formula
return formula
# now formula is a function of x. X can itself be a function.
print(formula(y(7))
# this will print value of formula at 7 as x(7) is 7.
So I'm making an integrator program with the simpson 1/3 method and I want the user to give me a function and interval of integration, and then return the result. I figure that I can use exec to make dynamic code, so I use it to create the function. This is my code:
from math import *
class CreaFormula:
def __init__(self,formula):
self.fun = "def f(x):\n return %s" % formula
class Integrador:
def __init__(self,f):
#integration interval
a = 0
b = 1
n = 600
h = (b-a)/n
#method
self.s = f(a) + f(b)
for i in range(1,n):
if i%2 == 0:
self.s = self.s + 2*f(a+i*h)
else:
self.s = self.s + 4*f(a+i*h)
self.s = self.s*(h/3)
vr = -cos(1) + e
self.er = abs((vr -self.s) /vr)
formula = input()
MiFo = CreaFormula(formula)
f1 = MiFo.fun
exec(f1)
MyInt = Integrador(f)
a = MyInt.s
b = MyInt.er
print(a)
So basically I want to put everything that is at the end of the code inside a class, so I can call it later by another code, but if I do that it shows an error that f is not defined, because it is created with exec. So is there a way to not use exec but still create a function from user's input?
Thanks in advance
If the question is just how to construct a function object that can evaluate a user-supplied expression, you'll probably have an easier time using eval than exec:
def create_function_from_formula(formula):
def user_function(x):
return eval(formula, globals(), {'x': x})
return user_function
Of course, even with eval, if someone provides a malicious formula, it can do anything, up to and including executing any other program included on the computer. So only do this if you trust the person providing the formula to essentially take over your computer. In particular, you should never do this if formula can come from a user who is not physically logged in to your computer already.
I think my question should be more clearly understood by this short code:
fs = []
for k in range(0, 10):
def f(x):
return x + 2*k
fs.append(f)
fs[0](1)
# expecting 1 but 19(=1+2*9)
How do I instead make f return what I want? Please note that f cannot receive k as an argument.
(What I'm actually trying to do is prepare multiple constraint functions that are fed to scipy.optimize.minimize)
The typical way to fix this is to do something like:
def f(x, k=k):
return x + 2*k
For the most part, this shouldn't affect your "f cannot receive k as an argument" condition because it isn't a required argument.
A related, but different approach would be to define f out of the loop.
def f(k, x):
return x + 2*k
Then in the loop use functools.partial.
import functools
fs = []
for k in range(10):
fs.append(functools.partial(f, k))
In this approach, your function won't accept a value for k even if you try to pass one.
Basically the problem is that the variable k, in this case, continually changes as the loop iterates. This means that all things which are pointing to the variable "k" are pointing to the same value at all times.
There are a couple of ways to solve this. This is perhaps the most common.
def f(x, k=k):
# This sets k as a locally bound variable which is evaluated
# at the time the function is created.
return x + 2*k
The detriment is that this solution will allow a later function to call the newly created functions with a different value of k. This means you could call f("cat","dog") and get "catdogdog" as a return. While this is not the end of the world, it certainly isn't intended.
However, you could also do something like this:
def f_maker(k):
# Create a new function whose variable "k" does not exist in outside scope.
def f(x):
return x + 2*k
return f
fs = []
for k in range(0, 10):
fs.append(f_maker(k))
fs[0](1)
i would like to perform a calculation using python, where the current value (i) of the equation is based on the previous value of the equation (i-1), which is really easy to do in a spreadsheet but i would rather learn to code it
i have noticed that there is loads of information on finding the previous value from a list, but i don't have a list i need to create it! my equation is shown below.
h=(2*b)-h[i-1]
can anyone give me tell me a method to do this ?
i tried this sort of thing, but that will not work as when i try to do the equation i'm calling a value i haven't created yet, if i set h=0 then i get an error that i am out of index range
i = 1
for i in range(1, len(b)):
h=[]
h=(2*b)-h[i-1]
x+=1
h = [b[0]]
for val in b[1:]:
h.append(2 * val - h[-1]) # As you add to h, you keep up with its tail
for large b list (brr, one-letter identifier), to avoid creating large slice
from itertools import islice # For big list it will keep code less wasteful
for val in islice(b, 1, None):
....
As pointed out by #pad, you simply need to handle the base case of receiving the first sample.
However, your equation makes no use of i other than to retrieve the previous result. It's looking more like a running filter than something which needs to maintain a list of past values (with an array which might never stop growing).
If that is the case, and you only ever want the most recent value,then you might want to go with a generator instead.
def gen():
def eqn(b):
eqn.h = 2*b - eqn.h
return eqn.h
eqn.h = 0
return eqn
And then use thus
>>> f = gen()
>>> f(2)
4
>>> f(3)
2
>>> f(2)
0
>>>
The same effect could be acheived with a true generator using yield and send.
First of, do you need all the intermediate values? That is, do you want a list h from 0 to i? Or do you just want h[i]?
If you just need the i-th value you could us recursion:
def get_h(i):
if i>0:
return (2*b) - get_h(i-1)
else:
return h_0
But be aware that this will not work for large i, as it will exceed the maximum recursion depth. (Thanks for pointing this out kdopen) In that case a simple for-loop or a generator is better.
Even better is to use a (mathematically) closed form of the equation (for your example that is possible, it might not be in other cases):
def get_h(i):
if i%2 == 0:
return h_0
else:
return (2*b)-h_0
In both cases h_0 is the initial value that you start out with.
h = []
for i in range(len(b)):
if i>0:
h.append(2*b - h[i-1])
else:
# handle i=0 case here
You are successively applying a function (equation) to the result of a previous application of that function - the process needs a seed to start it. Your result looks like this [seed, f(seed), f(f(seed)), f(f(f(seed)), ...]. This concept is function composition. You can create a generalized function that will do this for any sequence of functions, in Python functions are first class objects and can be passed around just like any other object. If you need to preserve the intermediate results use a generator.
def composition(functions, x):
""" yields f(x), f(f(x)), f(f(f(x)) ....
for each f in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
yield x
Your specs require a seed and a constant,
seed = 0
b = 10
The equation/function,
def f(x, b = b):
return 2*b - x
f is applied b times.
functions = [f]*b
Usage
print list(composition(functions, seed))
If the intermediate results are not needed composition can be redefined as
def composition(functions, x):
""" Returns f(x), g(f(x)), h(g(f(x)) ....
for each function in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
return x
print composition(functions, seed)
Or more generally, with no limitations on call signature:
def compose(funcs):
'''Return a callable composed of successive application of functions
funcs is an iterable producing callables
for [f, g, h] returns f(g(h(*args, **kwargs)))
'''
def outer(f, g):
def inner(*args, **kwargs):
return f(g(*args, **kwargs))
return inner
return reduce(outer, funcs)
def plus2(x):
return x + 2
def times2(x):
return x * 2
def mod16(x):
return x % 16
funcs = (mod16, plus2, times2)
eq = compose(funcs) # mod16(plus2(times2(x)))
print eq(15)
While the process definition appears to be recursive, I resisted the temptation so I could stay out of maximum recursion depth hades.
I got curious, searched SO for function composition and, of course, there are numerous relavent Q&A's.
...while still leaving it executable within the function.
The idea behind this is I want to create a summation function. Here's what I have so far:
def summation(n, bound, operation):
if operation is None and upper != 'inf':
g = 0
for num in range(n, limit + 1):
g += num
return g
else:
pass
But summations are most often about infinite convergent series (for which I use 'inf'), with operations applied to each term. Ideally, I'd like to be able to write print summation(0, 'inf', 1 / factorial(n)) and get the mathematical constant e, or def W(x): return summation(1, 'inf', ((-n) ** (n - 1)) / factorial(n)) to get the Lambert W function.
All that comes to my mind is passing the appropriate arithmetic as a string and then using the exec statement to execute it. But I don't think that would accomplish the whole thing, and it's obviously dangerous to use exec with possibly user-entered code.
In Python, functions are first-class, which is to say they can be used and passed around like any other values, so you can take a function:
def example(f):
return f(1) + f(2)
To run it, you could define a function like this:
def square(n):
return n * n
And then pass it to your other function:
example(square) # = square(1) + square(2) = 1 + 4 = 5
You can also use lambda to avoid having to define a new function if it's a simple expression:
example(lambda n: n * n)