Related
Im trying to calculate the hill coefficient of two logistic functions, f(x) and g(x), their composition, c(x), and input it in a datatable. Additionally, I am inputting the functions themselves, their composition and the product of hill coefficients of f(x) and g(x). In order to calculate the hill coefficients I am using a numeric bisection method as well.
Here's my code:
# Imports
import numpy as np
from numpy import log as ln
from matplotlib import pyplot as plt
from random import randint
import sympy as sym
import pandas as pd
import math
# initializing data
data = {'row': [],
'f(x)': [],
'g(x)': [],
'f(g(x))': [],
'H_f': [],
'H_g': [],
'H_fg': [],
'Product of H_f and H_g': [],
'Does this prove hypothesis?': []
}
df = pd.DataFrame(data)
#True Randomization
def logrand(b):
ra = randint(1,b)
ra = float(ra/100)
ra = 10**ra
ra = round(ra)
ra = int(ra)
return ra
# Two Hill functions
num1 = 10
number = 1
for _ in range(num1):
# Params
u = 10
c1 = logrand(300)
r1 = randint(1,u)
k1 = logrand(300)
c2 = logrand(300)
r2 = randint(1,u)
k2 = logrand(300)
# function layout
funcf = '{}/({}+e^(-{}x))'.format(c1, k1, r1)
funcg = '{}/({}+e^(-{}x))'.format(c2, k2, r2)
funcc = '{}/({}+e^(-{}({}/({}+e^(-{}x)))))'.format(c1, k1, r1, c2, k2, r2)
# figure layout
plt.rcParams["figure.figsize"] = [7.50, 3.50]
plt.rcParams["figure.autolayout"] = True
# Hill Function for f(x) and g(x) and f(g(x))
def f(x):
return c1 / (k1 + np.exp((-1*r1) * x))
def g(x):
return c2 / (k2 + np.exp((-1*r2) * x))
def comp(x):
return c1 / (k1 + np.exp((-1*r1) * (c2 / (k2 + np.exp((-1*r2) * x)))))
# EC finder
def BisectionEC10(fa, a, b):
c = 1
x = np.linspace(a, b, 1000)
ystar = 0.10 * (fa(x).max() - fa(x).min())
while abs(fa(c) - ystar) > 0.000000001:
c = (a + b) / 2
if fa(c) - ystar < 0:
a = c
elif fa(c) - ystar > 0:
b = c
# print('The EC10 of the function is: ',"{0:.15f}".format(c))
# print('Output of the function when evaluated at the EC10: ',fa(c))
return c
def BisectionEC90(fa, a, b):
c = 1
x = np.linspace(a, b, 1000)
ystar = 0.90 * (fa(x).max() - fa(x).min())
while abs(fa(c) - ystar) > 0.000000001:
c = (a + b) / 2
if fa(c) - ystar < 0:
a = c
elif fa(c) - ystar > 0:
b = c
# print('The EC90 of the function is: ',"{0:.15f}".format(c))
# print('Output of the function when evaluated at the EC90: ',fa(c))
return c
# EC90 and EC10 for f(x), g(x) and f(g(x))
up = 20
lo = 0
# x = np.linspace[lo,up,1000]
x = 1
# x = sym.symbols('x')
EC90_1 = BisectionEC90(f, lo, up)
EC10_1 = BisectionEC10(f, lo, up)
EC90_2 = BisectionEC90(g, lo, up)
EC10_2 = BisectionEC10(g, lo, up)
EC90_3 = BisectionEC90(comp, lo, up)
EC10_3 = BisectionEC10(comp, lo, up)
# Hill Coefficient for f(x) and g(x)
H_1 = ln(81) / (ln(EC90_1 / EC10_1))
H_1 = round(H_1,4)
H_2 = ln(81) / (ln(EC90_2 / EC10_2))
H_2 = round(H_2,4)
H_3 = ln(81) / (ln(EC90_3 / EC10_3))
H_3 = round(H_3,4)
prod = float(H_1.real) * float(H_2.real)
if prod >= float(H_3.real):
answer = 'yes'
else:
answer = 'no'
prod = round(prod,4)
# adding all data to dataframe 2
data2 = {'row': [number],
'f(x)': [funcf],
'g(x)': [funcg],
'f(g(x))': [funcc],
'H_f': [H_1],
'H_g': [H_2],
'H_fg': [H_3],
'Product of H_f and H_g': [prod],
'Does this prove hypothesis?': [answer]
}
number = number + 1
df = df.append(data2, ignore_index=True)
#final dataframe
print(df)
df.to_csv(r'/Users/*****/Desktop/Research/twoarctanfuncs.csv', index = False)
Now the problem that im stuck in is that the code just doesn't run, or that it has an infinite loop and I can't figure out where the problem is happening.
This is the error message when I force stop it:
Traceback (most recent call last):
File "/Users/*****/Documents/Python/NumericBisectionMethod/venv/twologisticfuncs.py", line 112, in <module>
EC90_1 = BisectionEC90(f, lo, up)
File "/Users/*****/Documents/Python/NumericBisectionMethod/venv/twologisticfuncs.py", line 95, in BisectionEC90
if fa(c) - ystar < 0:
File "/Users/*****/Documents/Python/NumericBisectionMethod/venv/twologisticfuncs.py", line 60, in f
return c1 / (k1 + np.exp((-1*r1) * x))
KeyboardInterrupt
Thanks for any help!
It looks like your binary search (bisection) isn't starting with a large enough span.
I quickly tried your code, and then added the statement print (a, b, c) inside the while loop of BisectionEC90. The output quickly converged to 0 0.0 0.0 and stayed there.
I replaced that print statement with print (abs(fa(c)), ystar) and the output converged to 0.025157232704402517 0.00014330069262001276.
So it makes sense that the code never exits the loop while abs(fa(c) - ystar) > 0.000000001:
EDIT: it would be a good idea to add some error checking into the while loop of your Bisection functions to prevent this type of infinite loop. Maybe something like this:
if b - a < 0.000000001:
raise Exception("Failed to find solution; a={}, b={}".format(a,b))
If you use a print statement or debugger, you will be able to detect the issue. You are stuck in a while loop, which means your condition is never satisfied. There could be multiple reasons, and to find out the exact issue, you would have to debug it. Although the most logical conclusion is that while abs(fa(c) - ystar) > 0.000000001 is not being satisfied as mentioned by #Mr. Snrub
I would also recommend using the if conditions to break the loop in such cases.
As mentioned above, the function below works, however its very slow. I am very interested in using faster/optimised numpy (or other) vectorized alternatives. I have not posted the entire script here due to it being too large.
My specific question is - are there suitable numpy (or other) functions that I can use to 1) reduce run time and 2) reduce code volume of this function, specifically the for loop?
Edit: mass, temp, U and dpdh are functions that carry out simple algebraic calculations and return constants
def my_system(t, y, n, hIn, min, mAlumina, cpAlumina, sa, V):
dydt = np.zeros(3 * n) #setting up zeros array for solution (solving for [H0,Ts0,m0,H1,Ts1,m1,H2,Ts2,m2,..Hn,Tsn,mn])
# y = [h_0, Ts_0, m_0, ... h_n, Ts_n, m_n]
# y[0] = hin
# y[1] = Ts0
# y[2] = minL
i=0
## Using thermo
T = temp(y[i],P) #initial T
m = mass(y[i],P) #initial m
#initial values
dydt[i] = (min * (hIn - y[i]) + (U(hIn,P,min) * sa * (y[i + 1] - T))) / m # dH/dt (eq. 2)
dydt[i + 1] = -(U(hIn,P,min) * sa * (y[i + 1] - T)) / (mAlumina * cpAlumina) # dTs/dt from eq.3
dmdt = dydt[i] * dpdh(y[i], P) * V # dm/dt (holdup variation) eq. 4b
dydt[i + 2] = min - dmdt # mass flow out (eq.4a)
for i in range(3, 3 * n, 3): #starting at index 3, and incrementing by 3 because we are solving for 'triplets' [h,Ts,m] in each loop
## Using thermo
T = temp(y[i],P)
m = mass(y[i],P)
# [h, TS, mdot]
dydt[i] = (dydt[i-1] * (y[i - 3] - y[i]) + (U(y[i-3], P, dydt[i-1]) * sa * (y[i + 1] - T))) / m # dH/dt (eq.2), dydt[i-1] is the mass of the previous tank
dydt[i + 1] = -(U(y[i-3], P, dydt[i-1]) * sa * (y[i + 1] - T)) / (mAlumina * cpAlumina) # dTs/dt eq. (3)
dmdt = dydt[i] * dpdh(y[i], P) * V # Equation 4b
dydt[i + 2] = dydt[i-1] - dmdt # Equation 4a
return dydt
The functions mass, temp, U, and dpdh used inside the my_system function all take numbers as input, perform some simple algebraic operation and return a number (no need to optimise these I am just providing them for further context)
def temp(H,P):
# returns temperature given enthalpy (after processing function)
T = flasher.flash(H=H, P=P, zs=zs, retry=True).T
return T
def mass(H, P):
# returns mass holdup in mol
m = flasher.flash(H=H, P=P, zs=zs, retry=True).rho()*V
return m
def dpdh(H, P):
res = flasher.flash(H=H, P=P, zs=zs, retry=True)
if res.phase_count == 1:
if res.phase == 'L':
drho_dTf = res.liquid0.drho_dT()
else:
drho_dTf = res.gas.drho_dT()
else:
drho_dTf = res.bulk._equilibrium_derivative(of='rho', wrt='T', const='P')
dpdh = drho_dTf/res.dH_dT_P()
return dpdh
def U(H,P,m):
# Given T, P, m
air = Mixture(['nitrogen', 'oxygen'], Vfgs=[0.79, 0.21], H=H, P=P)
mu = air.mu*1000/mWAir #mol/m.s
cp = air.Cpm #J/mol.K
kg = air.k #W/m.K
g0 = m/areaBed #mol/m2.s
a = sa*n/vTotal #m^2/m^3 #QUESTIONABLE
psi = 1
beta = 10
pr = (mu*cp)/kg
re = (6*g0)/(a*mu*psi)
hfs = ((2.19*(re**1/3)) + (0.78*(re**0.619)))*(pr**1/3)*(kg)/diameterParticle
h = 1/((1/hfs) + ((diameterParticle/beta)/kAlumina))
return h
Reference Image:
enter image description here
For improving the speed, you can see Numba, which is useable if you use NumPy a lot but not every code can be used with Numba. Apart from that, the formulation of the equation system is confusing. You are solving 3 equations and adding the result to a single dydt list by 3 elements each. You can simply create three lists, solve each equation and add them to their respective list. For this, you need to re-write my_system as:
import numpy as np
def my_system(t, RHS, hIn, Ts0, minL, mAlumina, cpAlumina, sa, V):
# get initial boundary condition values
y1 = RHS[0]
y2 = RHS[1]
y3 = RHS[2]
## Using thermo
T = # calculate T
m = # calculate m
# [h, TS, mdot] solve dy1dt for h, dy2dt for TS and dy3dt for mdot
dy1dt = # dH/dt (eq.2), y1 corresponds to initial or previous value of dy1dt
dy2dt = # dTs/dt eq. (3), y2 corresponds to initial or previous value of dy2dt
dmdt = # Equation 4b
dy3dt = # Equation 4a, y3 corresponds to initial or previous value of dy3dt
# Left-hand side of ODE
LHS = np.zeros([3,])
LHS[0] = dy1dt
LHS[1] = dy2dt
LHS[2] = dy3dt
return LHS
In this function, you can pass RHS as a list with initial values ([dy1dt, dy2dt, dy3dt]) which will be unpacked as y1, y2, and y3 respectively and use them for respective differential equations. The solved equations (next values) will be saved to dy1dt, dy2dt, and dy3dt which will be returned as a list LHS.
Now you can solve this using scipy.integrate.odeint. Therefore, you can leave the for loop structure and solve the equations by using this method as follows:
hIn = #some val
Ts0 = #some val
minL = #some val
mAlumina = #some vaL
cpAlumina = #some val
sa = #some val
V = #some val
P = #some val
## Using thermo
T = temp(hIn,P) #initial T
m = mass(hIn,P) #initial m
#initial values
y01 = # calculate dH/dt (eq. 2)
y02 = # calculate dTs/dt from eq.3
dmdt = # calculate dm/dt (holdup variation) eq. 4b
y03 = # calculatemass flow out (eq.4a)
n = # time till where you want to solve the equation system
y0 = [y01, y02, y03]
step_size = 1
t = np.linspace(0, n, int(n/step_size)) # use that start time to which initial values corresponds
res = odeint(my_sytem, y0, t, args=(hIn, Ts0, minL, mAlumina, cpAlumina, sa, V,), tfirst=True)
print(res[:,0]) # print results for dH/dt
print(res[:,1]) # print results for dTs/dt
print(res[:,2]) # print results for Equation 4a
Here, I have passed all the initial values as y0 and chosen a step size of 1 which you can change as per your need.
Am trying to write code for an equation that includes complex number, and put it into a function for simpsons rule.
import numpy as np
import cmath as cmp
import matplotlib.pyplot as plt
wavelength = 0.000001 #meters
Apw = 0.00002 # meters Apw taken as apeture width
z = 0.02 # meters
N = 100
permittivity = 0.00000000000885418783
c = 299792458 # m/s
k = (2*cmp.pi)/wavelength
j = 0 + 1j
n = 100
x = np.linspace(-0.005, 0.005, n, 1.1)
def simps (N, k, Apw, x):
S = 0
h = Apw / N
for i in range(0, N + 1):
for xprime in range(0, N+1):
xprime = Apw*xprime/N
f = cmp.exp(((j*k)/(2*z))*(x-xprime)**2)
if (i != 0) and (i != n):
f *= (2 + (2 * (i % 2)))
S = h/3 * np.sum(f[0:-1:2] + 4*f[1::2] + f[2::2])
return S
x = np.linspace(-0.005, 0.005, n, 1.1)
I = np.zeros([n])
for i in range(0,n):
E_0 = simps(N, k, Apw, x[i])[0]
I[i] = permittivity*c*(E_0 * cmp.conj(E_0)).real
Where j is a complex number j = 0 + 1j
I don't really know if what I am doing is anywhere near correct, the lines that are causing the error is line 46 and 36
In your code, f is a complex number. As the error message says, you can subscript (use square braces on) a complex number. For this reason, the expressions f[0:-1:2], f[1::2] and f[2::2] are illegal operations that are producing this error. It seems that maybe you think that f is a list of complex values, rather than a single value?
If you do know that f is a single complex value, then the only operations on it that I can think of are to extract the real and imaginary parts with f.real and f.imag.
I am new to python and in learning stages. I wanted to implement Particle Swarm Optimization(PSO) algorithm which I did by taking help from on-line materials and python tutorials. In PSO, a simple calculus problem is inferred i-e 100 * ((y - (x2))2) + ((1 - (x2))2). This problem is defined in a fitness function.
def fitness(x, y):
return 100 * ((y - (x**2))**2) + ((1 - (x**2))**2)
Now, I want to replace this simple calculus problem by simple first order Ordinary Differential Equation(ODE) by without changing existing function parameters (x,y) and want to return the value of dy_dx,y0 and t for further process.
# Define a function which calculates the derivative
def dy_dx(y, x):
return x - y
t = np.linspace(0,5,100)
y0 = 1.0 # the initial condition
ys = odeint(dy_dx, y0, t)`
In python odeint function is used for ODE which requires three essential parameters i-e func/model, y0( Initial condition on y (can be a vector) and t(A sequence of time points for which to solve for y) Example of odeint parameters.
I don't want to change its parameters because it will be difficult for me to make changes in algorithm.
For simplicity I pasted the full code below and my question is open to anyone if wants to modify the code with further parameters in General Best, Personal Best and r[i].
import numpy as np
from scipy.integrate import odeint
import random as rand
from scipy.integrate import odeint
from numpy import array
import matplotlib.pyplot as plt
def main():
#Variables
n = 40
num_variables = 2
a = np.empty((num_variables, n))
v = np.empty((num_variables, n))
Pbest = np.empty((num_variables, n))
Gbest = np.empty((1, 2))
r = np.empty((n))
for i in range(0, num_variables):
for j in range(0, n):
Pbest[i][j] = rand.randint(-20, 20)
a[i][j] = Pbest[i][j]
v[i][j] = 0
for i in range(0, n):
r[i] = fitness(a[0][i], a[1][i])
#Sort elements of Pbest
Order(Pbest, r, n)
Gbest[0][0] = Pbest[0][0]
Gbest[0][1] = Pbest[1][0]
generation = 0
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(True)
while(generation < 1000):
for i in range(n):
#Get Personal Best
if(fitness(a[0][i], a[1][i]) < fitness(Pbest[0][i], Pbest[1][i])):
Pbest[0][i] = a[0][i]
Pbest[1][i] = a[1][i]
#Get General Best
if(fitness(Pbest[0][i], Pbest[1][i]) < fitness(Gbest[0][0], Gbest[0][1])):
Gbest[0][0] = Pbest[0][i]
Gbest[0][1] = Pbest[1][i]
#Calculate Velocity
Vector_Velocidad(n, a, Pbest, Gbest, v)
generation = generation + 1
print 'Generacion: ' + str(generation) + ' - - - Gbest: ' +str(Gbest)
line1 = ax.plot(a[0], a[1], 'r+')
line2 = ax.plot(Gbest[0][0], Gbest[0][1], 'g*')
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
fig.canvas.draw()
ax.clear()
ax.grid(True)
print 'Gbest: '
print Gbest
def Vector_Velocidad(n, a, Pbest, Gbest, v):
for i in range(n):
#Velocity in X
v[0][i] = 0.7 * v[0][i] + (Pbest[0][i] - a[0][i]) * rand.random() * 1.47 + (Gbest[0][0] - a[0][i]) * rand.random() * 1.47
a[0][i] = a[0][i] + v[0][i]
v[1][i] = 0.7 * v[1][i] + (Pbest[1][i] - a[1][i]) * rand.random() * 1.47 + (Gbest[0][1] - a[1][i]) * rand.random() * 1.47
a[1][i] = a[1][i] + v[1][i]
def fitness(x, y):
return 100 * ((y - (x**2))**2) + ((1 - (x**2))**2)
def Order(Pbest, r, n):
for i in range(1, n):
for j in range(0, n - 1):
if r[j] > r[j + 1]:
#Order the fitness
tempRes = r[j]
r[j] = r[j + 1]
r[j + 1] = tempRes
#Order las X, Y
tempX = Pbest[0][j]
Pbest[0][j] = Pbest[0][j + 1]
Pbest[0][j + 1] = tempX
tempY = Pbest[1][j]
Pbest[1][j] = Pbest[1][j + 1]
Pbest[1][j + 1] = tempY
if '__main__' == main():
main()
I have created some very basic implementations of the mentioned models. However, although graphs seem to look right, the numbers don't add up to a constant. That is for the sum of susceptible/infected/recovered people in each compartment should add up to N (which is total number of people), but it doesn't, for some reason it adds up to some bizarre decimal numbers, and I really don't know how to fix it, after looking at it for 3 days now.
The SI Model:
import matplotlib.pyplot as plt
N = 1000000
S = N - 1
I = 1
beta = 0.6
sus = [] # infected compartment
inf = [] # susceptible compartment
prob = [] # probability of infection at time t
def infection(S, I, N):
t = 0
while (t < 100):
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
p = beta * (I / N)
sus.append(S)
inf.append(I)
prob.append(p)
t = t + 1
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SI model')
figure.add_subplot(211)
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=[inf_line, sus_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) # use scientific notation
ax = figure.add_subplot(212)
prob_line = plt.plot(prob, label='p(t)')
plt.legend(handles=prob_line)
type(ax) # matplotlib.axes._subplots.AxesSubplot
# manipulate
vals = ax.get_yticks()
ax.set_yticklabels(['{:3.2f}%'.format(x*100) for x in vals])
plt.xlabel('T')
plt.ylabel('p')
plt.show()
SIS Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
beta = 0.3
gamma = 0.1
sus = \[\]
inf = \[\]
def infection(S, I, N):
for t in range (0, 1000):
S = S - (beta*S*I/N) + gamma * I
I = I + (beta*S*I/N) - gamma * I
sus.append(S)
inf.append(I)
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SIS model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=\[inf_line, sus_line\])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
SIR Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
R = 0
beta = 0.5
mu = 0.1
sus = []
inf = []
rec = []
def infection(S, I, R, N):
for t in range (1, 100):
S = S -(beta * S * I)/N
I = I + ((beta * S * I)/N) - R
R = mu * I
sus.append(S)
inf.append(I)
rec.append(R)
infection(S, I, R, N)
figure = plt.figure()
figure.canvas.set_window_title('SIR model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
rec_line, = plt.plot(rec, label='R(t)')
plt.legend(handles=[inf_line, sus_line, rec_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
I'll look only at the SI model.
Your two key variables are S and I. (You may have reversed the meanings of these two variables, though that does not affect what I write here.) You initialize them so their sum is N which is the constant 1000000.
You update your two key variables in the lines
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
You apparently intend to add to I and subtract from S the same value, so the sum of S and I is unchanged. However, you actually first change S then use that new value to change I, so the values added and subtracted are not actually the same, and the sum of the variables has not remained constant.
You can fix this by using Python's ability to update multiple variables in one line. Replace those two lines with
S, I = S - beta * ((S * I / N)), I + beta * ((S * I) / N)
This calculates both of the new values before updating the variables, so the same value actually added and subtracted from the two variables. (There are other ways to get the same effect, such as temporary variables for the updated values, or one temporary variable to store the amount to add and subtract, but since you use Python you may as well use its capabilities.)
When I now run the program, I get these graphs:
which I think is what you want.
So the solution above worked for the SIS model as well.
As for the SIR model I had to solve differential equations using odeint, here is a simple solution to the SIR model:
import matplotlib.pylab as plt
from scipy.integrate import odeint
import numpy as np
N = 1000
S = N - 1
I = 1
R = 0
beta = 0.6 # infection rate
gamma = 0.2 # recovery rate
# differential equatinons
def diff(sir, t):
# sir[0] - S, sir[1] - I, sir[2] - R
dsdt = - (beta * sir[0] * sir[1])/N
didt = (beta * sir[0] * sir[1])/N - gamma * sir[1]
drdt = gamma * sir[1]
print (dsdt + didt + drdt)
dsirdt = [dsdt, didt, drdt]
return dsirdt
# initial conditions
sir0 = (S, I, R)
# time points
t = np.linspace(0, 100)
# solve ODE
# the parameters are, the equations, initial conditions,
# and time steps (between 0 and 100)
sir = odeint(diff, sir0, t)
plt.plot(t, sir[:, 0], label='S(t)')
plt.plot(t, sir[:, 1], label='I(t)')
plt.plot(t, sir[:, 2], label='R(t)')
plt.legend()
plt.xlabel('T')
plt.ylabel('N')
# use scientific notation
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.show()