Markov Chain montecarlo integration and infinite while loop - python

I'm implementing a Markov Chain Montecarlo with metropolis and barkes alphas for numerical integration. I've created a class called MCMCIntegrator(). I've loaded it with some attributes, one of then is the pdf of the function (a lambda) we're trying to integrate called g.
import numpy as np
import scipy.stats as st
class MCMCIntegrator:
def __init__(self):
self.g = lambda x: st.gamma.pdf(x, 0, 1, scale=1 / 1.23452676)*np.abs(np.cos(1.123454156))
self.size = 10000
self.std = 0.6
self.real_int = 0.06496359
There are other methods in this class, the size is the size of the sample that the class must generate, std is the standard deviation of the Normal Kernel, which you will see in a few seconds. The real_int is the value of the integral from 1 to 2 of the function we're integrating. I've generated it with a R script. Now, to the problem.
def _chain(self, method=None):
"""
Markov chain heat-up with burn-in
:param method: Metrpolis or barker alpha
:return: np.array containing the sample
"""
old = 0
sample = np.zeros(int(self.size * 1.5))
i = 0
if method:
def alpha(a, b): return min(1, self.g(b) / self.g(a))
else:
def alpha(a, b): return self.g(b) / (self.g(a) + self.g(b))
while i != len(sample):
new = st.norm(loc=old, scale=self.std).rvs()
new = abs(new)
al = alpha(old, new)
u = st.uniform.rvs()
if al > u:
sample[i] = new
old = new
i += 1
return np.array(sample)
Below this method is an integrate() method that calculates the proportion of numbers in the [1, 2] interval:
def integrate(self, method=None):
"""
Integration step
"""
sample = self._chain(method=method)
# discarding 30% of the sample for the burn-in
ind = int(len(sample)*0.3)
sample = sample[ind:]
setattr(self, "sample", sample)
sample = [1 if 1 < v < 2 else 0 for v in sample]
return np.mean(sample)
This is the main function:
def main():
print("-- RESULTS --".center(20), end='\n')
mcmc = MCMCIntegrator()
print(f"\t{mcmc.integrate()}", end='\n')
print(f"\t{np.abs(mcmc.integrate() - mcmc.real_int) / mcmc.real_int}")
if __name__ == "__main__":
main()
I'm stuck in an infinity while loop and I have no idea why this is happening.

Couple things... You are hung up in the chain method because the alpha computation is returning NaN, because g() is returning NaN. Take a look at the print statements I inserted into your code and run it...
tips:
Make g() a class function just like chain.
Test g() on some test values, something is clearly amiss
Don't conditionally define a function like alpha. Wayyy confusing and error prone/tough to troubleshoot. Just pass alpha what it needs and you also can make it a class function alpha(a, b, method=None)
Take a look at where you are updating i in the `_chain' function.... You realize you are risking a long looping process because you only update i conditionally!
You are set up for disaster with your use of numpy array. You may have a bunch of trailing zeros after your actual data because you are over-writing the large zeros list. You do NOT need numpy array here, just use a python empty list and append new values to it, either zero or one...based on whatever.
Add a couple print statements in when you are troubleshooting (or unit test your functions). Try my adds to your function below... it is what I used to figure out what was going on
def _chain(self, method=None, verbose=True):
"""
Markov chain heat-up with burn-in
:param method: Metrpolis or barker alpha
:return: np.array containing the sample
"""
old = 0
sample = np.zeros(int(self.size * 1.5))
i = 0
if method:
def alpha(a, b): return min(1, self.g(b) / self.g(a))
else:
def alpha(a, b):
if verbose: print(f'g(a): {self.g(a)}, g(b): {self.g(b)}')
return self.g(b) / (self.g(a) + self.g(b))
while i != len(sample):
new = st.norm(loc=old, scale=self.std).rvs()
new = abs(new)
al = alpha(old, new)
u = st.uniform.rvs()
if verbose: print(f'old: {old:.3f} new: {new:.3f} alpha: {al:.3f} u: {u:.3f}')
if al > u:
sample[i] = new
old = new
i += 1 # do you really want to conditionally update i?
sys.exit(-1) # to breakout of infinite loop...
return np.array(sample)

Related

Revised simplex method enters endless loop

I'm trying to implement a revised simplex method (RSM) algorithm using Python and numpy. I'm stuck with its either working on maximization only (correct on tiny matrices like 2x4, 5x5 etc and wrong on larger ones) or entering endless loop in most cases of minimization. The code below demonstrates my attempt to implement minimization:
def __init__(self, A: np.ndarray, b: np.ndarray, c: np.ndarray):
base_size = A.shape[0] # Number of basis vectors
I = np.eye(base_size) # Identity matrix, an initial basis
self.extended_matrix = np.concatenate((A, I), axis=1) # Extended matrix of the system
self.free_size = A.shape[1] # Number of free vectors
self.b = b # Right parts of the constraints
self.base_inv = I # Initial inverse basis matrix
self.c = np.concatenate((c, np.zeros(base_size))) # Objective function quotients including those related to the basis variables
self.c_i = [i for i, coeff in enumerate(self.c)] # Indices for all variables
#property
def c_f_indices(self):
"""
Indices of the free variables.
"""
return self.c_i[:self.free_size]
#property
def c_T_B(self):
"""
Objective function quotients related to the basis variables.
"""
c_B_indices = self.c_i[self.free_size:] # Basis variables indices.
return self.c[c_B_indices]
#property
def c_T_f(self):
"""
Objective function quotients related to the free variables.
"""
return self.c[self.c_f_indices]
#property
def free(self):
"""
Free vectors.
"""
return self.extended_matrix[:, self.c_f_indices]
#property
def y_T(self):
"""
Lagrange multipliers.
"""
return self.c_T_B # self.base_inv
#property
def deltas(self):
"""
Net evaluations.
"""
return (self.y_T # self.free) - self.c_T_f
def _swap(self, exits: int, enters: int) -> None:
"""
In-/excluding respective vectors into/from the basis.
"""
self.c_i[enters], self.c_i[exits + self.free_size] = self.c_i[exits + self.free_size], self.c_i[enters]
def optimize(self):
while any([delta > 0 for delta in self.deltas]): # < 0 in case of maximization
x_B = self.base_inv # self.b # Current basis variables
enters_base = np.argmax(self.deltas) # Vector to enter the basis; argmin in case of maximization
# Finding the vector to leave the basis:
alpha = self.base_inv # self.free[:, enters_base]
try:
exits_base = np.argmin([b/a if a > 0 else np.inf for b, a in zip(x_B, alpha)])
assert alpha[exits_base] != 0
except (ValueError, AssertionError):
raise Exception("No solutions")
# Finding the E_r matrix, which current inverse basis will be left-multiplied with in order to achieve the new inverse basis:
zetas = [-alpha[j] / alpha[exits_base] if j != exits_base else 1/alpha[exits_base] for j, a_j in enumerate(alpha)]
E = np.eye(self.free.shape[0])
E[:, exits_base] = zetas
self.base_inv = E # self.base_inv
# In-/excluding respective vectors into/from the basis:
self._swap(exits_base, enters_base)
return self.c_T_B # self.base_inv # self.b # Final objective function value
I've also tried to sort c_f_indices but still get endless loop. A similar RSM implementation also yields wrong results on larger matrices (like 16x8, for example) and works fine on tiny ones.
Where is the root of the problem?
As already mentioned by Erwin Kalvelagen, the normal Dantzig pivot rule can lead to cycles and stalling, where there is no improvement in the objective value for long periods of time.
In general, this phenomena is known as degeneracy of the LP. Limited numerical accuracy and roundoffs errors can contribute to the degeneracy of an issue. This is why it is typically more prevalent in large LP's.
There's a few ways to combat this problem. As Erwin mentioned, pertubation is most often used. However, if you're doing this as a learning project, I would suggest using a solution where you use more refined pivoting rule such as Zadeh's Rule or Cunningham's Rule, where you simply keep a a table or variable to ensure you pick a different variable to enter the basis once you've cycled.

Making a class process A 2 by 2 Matrix and having issues return it through __str__

so I have to process a 2 by 2 matrix through a class and return print the output with str. I can't really create new functions and I'm pretty sure the math is good with the matrices, I'm just having some output issues. I marked the area, specifically really the output that I cannot modify, but I can modify the class to support it.
Here's my code.
# This code aims to take a 2 by 2 matrix and add, subtract, and multiply it by another matrix, as well as inverse and power it.
# ----------------------------------------------------------------------------------------------------------------------
# This is how we'll use math.nan and only math.nan
import math
# Your classes should go here
class Matrix2x2: # Just initializing as needed.
def __init__(self,a,b,c,d):
self.a = a
self.b = b
self.c = c
self.d = d
def __add__(self,second):
return(Matrix2x2(self.a+second.a,self.b+second.b,self.c+second.c,self.d+second.d))
def __sub__(self, second): # Just subtracting instead of adding
return(Matrix2x2(self.a - second.a,self.b-second.b,self.c-second.c,self.d-second.d))
def __mul__(self, second): # Multiplying them based on the according spot and some addition.
return(Matrix2x2(self.a*second.a+self.b*second.c,self.a*second.b+self.b*second.d,self.c*second.a+self.d*second.c,self.c*second.b+self.d*second.d))
def __pow__(self, power): # Process varies based on what we work with.
StoredMatrix = Matrix2x2(self.a, self.b, self.c, self.d) # The variables just save information and make the math more clean.
determinant = 1/((self.a*self.d)-(self.b*self.c)) # Used to simplify inversing and determine if there is an inverse.
InverseMatrix = Matrix2x2(self.d*determinant,-self.b*determinant,-self.c*determinant, self.a*determinant)
if power > 0:
count = 1
while count < power: # The original matrix is repeatedly multiplied and stored until it matches the power value.
count+=1
StoredMatrix *= Matrix2x2(self.a, self.b, self.c, self.d)
return StoredMatrix
elif power < 0:
count = 0
while count < power:
count+=1
InverseMatrix *= Matrix2x2(self.d*determinant,-self.b*determinant,-self.c*determinant,self.a*determinant)
return InverseMatrix
if determinant == 0 or power == 0: # This means that there is no inverse, or the power value is 0 and invalid.
return(Matrix2x2(math.nan, math.nan, math.nan, math.nan))
def __str__(self):
return print('[',str(self.a) ,str(self.b) ,']\n' ,'\b[' ,str(self.c) ,str(self.d),']')
# Do NOT use any pre-built packages to perform the below operations, each should
# be coded using regular mathematics operation (+,-,*,/), no numpy or math functions other
# than math.nan
# Code below cannot be modified
A = Matrix2x2(1,2,3,4)
B = Matrix2x2(4,3,2,1)
print('Addition: A+B')
print(A,"+\n",B,"=\n",A+B,sep="")
input(),print('Subtraction: A-B')
print(A,"-\n",B,"=\n",A-B,sep="")
input(),print('Multiplication: A*B')
print(A,"*\n",B,"=\n",A*B,sep="")
input(),print('Multiplication: B*A')
print(B,"*\n",A,"=\n",B*A,sep="")
input(),print('Powers: A^3 ')
print(A,"^3","\n=\n",A**3,sep="")
input(),print('Inverse: A^-1 ')
print(A,"^-1","\n=\n",A**(-1),sep="")
input(),print('Inverse with powers: A^-3 = (A^-1)^3')
print(A,"^-3","\n=\n",A**(-3),sep="")
# code above cannot be modified
# Just for testing, below.
print(A.__add__(B))
print(A.__sub__(B))
print(A.__mul__(B))
print(A.__pow__(3))
print(A.__pow__(-1))
print(A.__pow__(0))
print(A.__pow(-3))
I usually get an error due a NoneType with the add function. This doesn't allow me to see what errors I'd get. I tried just converting them to strings individually using str() and got the same error. I don't think it's the math.nan's either.
Here is an example:
Addition: A+B
[ 1 2 ]
[ 3 4 ]
Traceback (most recent call last):
File "ThisWasPurposelyCensored", line 51, in <module>
print(A,"+\n",B,"=\n",A+B,sep="")
TypeError: __str__ returned non-string (type NoneType)
Process finished with exit code 1
Anyways, how do I avoid that NoneType issue or get this to be compatible with str, without interfering with the math and required input too much? I'll provide anymore information you might need to help me solve this.
Rewrite your __str__ method to be like this
def __str__(self):
return '[ {} {} ]\n[ {} {} ]'.format(self.a, self.b, self.c, self.d)
A bit shorter
def __str__(self):
return '[ {x.a} {x.b} ]\n[ {x.c} {x.d} ]'.format(x=self)

Guidance needed with 'class' task (beginner)

It looks daunting, but please bear with me, it's not as difficult as it looks. I have a code here about beam deflection. It's just some maths and numbers at this point. Only the last part requires attention.
class beam(object):
"""This class is models the deflection of a simply supported beam under
multiple point loads, following Euler-Bernoulli theory and the principle of
superposition.
"""
def __init__(self, E, I, L):
"""The class costructor.
"""
self.E = 8.0E9 # Young's modulus of the beam in N/m^2
self.I = 1.333E-4 # Second moment of area of the beam in m^4
self.L = 5.0 # Length of the beam in m
self.Loads = [(0.0, 0.0)] # the list of loads applied to the beam
self.name = "beam"
def setLoads(self, Loads):
'''This function allows multiple point loads to be applied to the beam
using a list of tuples of the form (load, position)
'''
self.Loads = Loads
The "def __ init __" and "def setLoads" were given, so the above doesn't need changing. I inputted values for self.E, I and L since I thought I needed them there, but those numbers can be replaced back to the letters that they were before.
def beamDeflection(self, Load, x):
"""Just a simple calculation, really.
"""
E = 8.09 * (10 ** 9)
I = 1.333 * (10 ** -4)
L = 5
a = 2.5
b = a + (x - a)
(P1, a) = Load
if 0 <= x <= 2.5:
beamDeflection = ((P1*b*x)/(6*L*E*I))*((L**2)-(x**2)-(b**2))
else:
if 2.5 < x <= 5:
beamDeflection = ((P1*b)/(6*L*E*I)) / (((L/b)*((x-a)**3)) -
(x**3) + (x*((L**2) -
(b**2))))
return beamDeflection
The above "beamDeflection" is the simple code I typed up that just calculates deflection in a beam using a formula that's already been given. Essentially, if a weight is put on the left side of the beam, it'll calculate a number out and the same for the other side.
def getTotalDeflection(self, x):
"""The function getTotalDeflection(self, x) should go through each load tuple in the
list.Loads and calculate the beam deflection at point x (Hint: the function you just
created could be handy here!). getTotalDeflection should return the total deflection at x,
which is the sum over each of the individual deflections.
"""
My understanding is that I need to a "for" loop to go through each load tuple while involving the self.load. I'm not sure on how to go about combining both of those things together. If anyone can help me out, I'd really, really appreciate it.
What you are looking for is probably this (else please clarify):
def getTotalDeflection(self, x):
return sum(self.beamDeflection(loadval, x) for loadval in self.Loads)
I think this is what you want:
def getTotalDeflection(self, x):
total = 0
# For every load in `Loads`
for load in self.Loads:
# Compute the deflection and add it to the total
total += self.beamDeflection(load, x):
return total

Pythonic way to manage arbitrary amount of variables, used for equation solving.

This is a bit difficult to explain without a direct example. So let's put the very simplistic ideal-gas law as example. For an ideal gas under normal circumstances the following equation holds:
PV = RT
This means that if we know 3 of the 4 variables (pressure, volume, specific gas constant and temperature) we can solve for the other one.
How would I put this inside an object? I want to have an object where I can just insert 3 of the variables, and then it calculates the 4th. I wonder if this can be achieved through properties?
My current best guess is to insert it like:
class gasProperties(object):
__init__(self, P=None, V=None, R=None, T=None)
self.setFlowParams(P, V, R, T)
def setFlowParams(self, P=None, V=None, R=None, T=None)
if P is None:
self._P = R*T/V
self._V = V
self._R = R
self._T = T
elif V is None:
self._V = R*T/P
self._P = P
self._R = R
self._T = T
#etc
Though this is quite cumbersome, and error prone (I have to add checks to see that exactly one of the parameters is set to "None").
Is there a better, cleaner way?
I see this "problem" happening quite often, in all kinds of various ways, and especially once the number of variables grows (adding density, reynolds number, viscosity to the mix) the number of different if-statements grows quickly. (IE if I have 8 variables and any 5 make the system unique I would need 8 nCr 5 = 56 if statements).
Using sympy, you can create a class for each of your equations. Create the symbols of the equation with ω, π = sp.symbols('ω π') etc., the equation itself and then use function f() to do the rest:
import sympy as sp
# Create all symbols.
P, V, n, R, T = sp.symbols('P V n R T')
# Create all equations
IDEAL_GAS_EQUATION = P*V - n*R*T
def f(x, values_dct, eq_lst):
"""
Solves equations in eq_lst for x, substitutes values from values_dct,
and returns value of x.
:param x: Sympy symbol
:param values_dct: Dict with sympy symbols as keys, and numbers as values.
"""
lst = []
lst += eq_lst
for i, j in values_dct.items():
lst.append(sp.Eq(i, j))
try:
return sp.solve(lst)[0][x]
except IndexError:
print('This equation has no solutions.')
To try this out... :
vals = {P: 2, n: 3, R: 1, T:4}
r = f(V, values_dct=vals, eq_lst=[IDEAL_GAS_EQUATION, ])
print(r) # Prints 6
If you do not provide enough parameters through values_dct you ll get a result like 3*T/2, checking its type() you get <class 'sympy.core.mul.Mul'>.
If you do provide all parameters you get as a result 6 and its type is <class 'sympy.core.numbers.Integer'>, so you can raise exceptions, or whatever you need. You could also, convert it to an int with int() (it would raise an error if instead of 6 you had 3*T/2 so you can test it that way too).
Alternatively, you can simply check if None values in values_dct are more than 1.
To combine multiple equations, for example PV=nRT and P=2m, you can create the extra symbol m like the previous symbols and assign 2m to the new equation name MY_EQ_2, then insert it in the eq_lst of the function:
m = sp.symbols('m')
MY_EQ_2 = P - 2 * m
vals = {n: 3, R: 1, T:4}
r = f(V, values_dct=vals, eq_lst=[IDEAL_GAS_EQUATION, MY_EQ_2])
print(r) # Prints 6/m
A basic solution using sympy, and kwargs to check what information the user has provided:
from sympy.solvers import solve
from sympy import Symbol
def solve_gas_properties(**kwargs):
properties = []
missing = None
for letter in 'PVRT':
if letter in kwargs:
properties.append(kwargs[letter])
elif missing is not None:
raise ValueError("Expected 3 out of 4 arguments.")
else:
missing = Symbol(letter)
properties.append(missing)
if missing is None:
raise ValueError("Expected 3 out of 4 arguments.")
P, V, R, T = properties
return solve(P * V - R * T, missing)
print solve_gas_properties(P=3, V=2, R=1) # returns [6], the solution for T
This could then be converted into a class method, drawing on class properties instead of keyword arguments, if you want to store and manipulate the different values in the system.
The above can also be rewritten as:
def gas_properties(**kwargs):
missing = [Symbol(letter) for letter in 'PVRT' if letter not in kwargs]
if len(missing) != 1:
raise ValueError("Expected 3 out of 4 arguments.")
missing = missing[0]
P, V, R, T = [kwargs.get(letter, missing) for letter in 'PVRT']
return solve(P * V - R * T, missing)
One solution could be the use of a dictionary to store variable names and their values. This allows you to easily add other variables at any time. Also, you can check that exactly one variable has value "None" by counting the number of "None" items in your dictionary.
My approach would be fairly simple:
class GasProperties(object):
def __init__(self, P=None, V=None, R=None, T=None):
self.setFlowParams(P, V, R, T)
def setFlowParams(self, P=None, V=None, R=None, T=None):
if sum(1 for arg in (P, V, R, T) if arg is None) != 1:
raise ValueError("Expected 3 out of 4 arguments.")
self._P = P
self._V = V
self._R = R
self._T = T
#property
def P(self):
return self._P is self._P is not None else self._R*self._T/self._V
You similarly define properties for V, R and T.
This approach allows you to set up object's attributes:
def setFlowParams(self, P=None, V=None, R=None, T=None):
params = self.setFlowParams.func_code.co_varnames[1:5]
if sum([locals()[param] is None for param in params]) > 1:
raise ValueError("3 arguments required")
for param in params:
setattr(self, '_'+param, locals()[param])
In addition, you need to define getters for attributes with formulas. Like this:
#property
def P(self):
if self._P is None:
self._P = self._R*self._T/self._V
return self._P
Or calculate all values in setFlowParams.
Numerical methods
You might want to do this without sympy, as and exercise for example, with numerical root finding. The beauty of this method is that it works for a extremely wide range of equations, even ones sympy would have trouble with. Everybody i know was taught this in uni on bachelor maths course*, unfortunately not many can apply this in practice.
So first we get the rootfinder you can find code examples on wikipedia and on the net at large this is fairly well known stuff. Many math packages have these built in see for example scipy.optimize for good root finders. I'm going to use the secant method for ease of implementation (in this case i don't really need iterations but ill use generic versions anyway if you happen to want to use some other formulas).
"""Equation solving with numeric root finding using vanilla python 2.7"""
def secant_rootfind(f, a, incr=0.1, accuracy=1e-15):
""" secant root finding method """
b=a+incr;
while abs(f(b)) > accuracy :
a, b = ( b, b - f(b) * (b - a)/(f(b) - f(a)) )
class gasProperties(object):
def __init__(self, P=None,V=None,n=None,T=None):
self.vars = [P, V, n, 8.314, T]
unknowns = 0
for i,v in enumerate(self.vars):
if v is None :
self._unknown_=i
unknowns += 1
if unknowns > 1:
raise ValueError("too many unknowns")
def equation(self, a):
self.vars[self._unknown_] = a
P, V, n, R, T = self.vars
return P*V - n*R*T # = 0
def __str__(self):
return str((
"P = %f\nV = %f\nn = %f\n"+
"R = %f\nT = %f ")%tuple(self.vars))
def solve(self):
secant_rootfind(self.equation, 0.2)
print str(self)
if __name__=="__main__": # run tests
gasProperties(P=1013.25, V=1., T=273.15).solve()
print "--- test2---"
gasProperties( V=1,n = 0.446175, T=273.15).solve()
The benefit of root finding is that even if your formula wouldn't be so easy it would still work, so any number of formulas could be done with no more code than writing formulation. This is generally a very useful skill to have. SYMPY is good but symbolic math is not always easily solvable
The root solver is easily extendable to vector and multi equation cases, even matrix solving. The ready made scipy functions built for optimization allready do this by default.
Here is some more resources:
Some numerical methods in python
* most were introduced at minimum to Newton–Raphson method

calculating current value based on previous value

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.

Categories