https://github.com/mirnylab/pdSim
Tip revision: f08bd75aabf7213e253baf26d219c374a745c8d4 authored by Christopher McFarland on 04 February 2019, 22:56:03 UTC
refract
refract
Tip revision: f08bd75
kt.pyx
from numpy cimport *
from numpy import empty, zeros, ones, corrcoef, tile, concatenate, fromiter, uint32, uint8, double, array, arange, dot, finfo, seterr, ndindex, r_, repeat, copy, loadtxt, outer, random, vstack, hstack, zeros_like, ones_like, empty_like, vectorize, transpose, pi, linspace, e, ceil, nan
import numpy as np
seterr(over='ignore', invalid='ignore', divide='ignore')
from scipy.special import gammainc, gamma, erf
import functools
cdef extern from "c/exponential.h":
double exponential()
void mt_init()
mt_init() # see final.h
cdef extern from "math.h":
double log( double x)
double sqrt( double x)
double exp( double x)
double pow(double x, double y)
def getUdUp(u=1e-8, **otherParams):
Tdf = 2*700
Tpf = 2*5e6
TdTp = otherParams.pop('TdTp', Tdf/Tpf)
Td = sqrt(TdTp*Tpf*Tdf)
Tp = sqrt(Tdf*Tpf/TdTp)
return dict(Ud=u*Td, Up=u*Tp, **otherParams)
class first:
Ud = 1e-8*700
Up = 1e-8*5e6
sd = 0.1
sp = 1e-3
N_0 = 1e3
t_max = 10000
verbose = False
def p(self, txt):
if self.verbose: print txt
def __init__(self, **params):
self.__dict__.update(params)
@property
def f(self): return self.Ud*self.sd/(1 + self.sd)
@property
def dN(self): return self.sd
@property
def vp(self): return self.sp*self.Up
@property
def N_eq(self):
assert self.vp > 0, 'vp: {x.vp:g}, Up: {x.Up:g}, sp: {x.sp:g}, N: {x.N_0}'.format(x = self )
assert self.f > 0, ' f: {x.f:g} , Up: {x.Up:g}, sp: {x.sp:g}, N: {x.N_0}'.format(x = self )
assert self.dN > 0, 'dN: {x.dN:g}, Up: {x.Up:g}, sp: {x.sp:g}, N: {x.N_0}'.format(x = self )
return self.vp/(self.f*log(1 + self.dN))
def __repr__(self):
return """Ud = {x.Ud:g}
Up = {x.Up:g}
sd = {x.sd:g}
sp = {x.sp:g}
N_0 = {x.N_0:g}
t_max = {x.t_max:g}
N_eq = {x.N_eq:g}
""".format(x=self)
def velocity(model, x): return x*model.N_eq*model.vp*(x - 1)
def potential(model, x): return model.N_eq*model.vp*x*x*(0.5 - x/3)
def pC(model, x=None):
D = 2/log(1+model.dN)
if x is None:
x = model.N_0/model.N_eq
return 1 - gammainc(D, D/x)
def clonalInterference(model):
""" CI -> d<d>/dt : sp = 0, Ud ~ sd"""
from scipy.optimize import fsolve
cdef double sd = model.sd
def f(V):
A = V/model.Ud; B = log(A);
return model.N - (exp(V/2/sd*((B-1)**2+1)-0.5*(log(sd**3/(A*B))- B if V>=sd else 0) ) if V>sd/B else 0.5*A/sd)
return fsolve(f, sd)
@functools.lru_cache(maxsize=None)
def fAnddN(double Up, double sd, double sp, double N):
from scipy.stats import poisson
cdef:
double L = Up/sp
int i
int i_max = int(ceil(log(1+sd)/log(1+sp)))
int k = (poisson.cdf(arange(L), L) < 0.5*pow(N,-0.5)).sum()
ndarray[int64_t, ndim=1] X = arange(i_max)
ndarray[double_t, ndim=1] W = (1+sd)*(1+sp)**-X
ndarray[double_t, ndim=1] f = poisson.pmf(X+k, L)/(1-poisson.cdf(k-1, L))
ndarray[double_t, ndim=1] fP = f*(W-1)/W
ndarray[double_t, ndim=1] dNp = empty(i_max)
Lambda = fP.sum()
assert Lambda < sd/(1+sd), 'wtf!'
dNp[i_max - 1] = i_max - 1
for i in range(i_max-2, -1, -1): dNp[i] = (i + sp*dNp[i+1])/(1 + sp)
V = (1 + sd)*(1 + sp)**-dNp
assert (V <= 1+sd).all()
return Lambda, fP.dot(V)/Lambda - 1
from scipy.integrate import simps
def moran(double Up, double sp, double N, int r1=150, int r2=600):
cdef double x0 = exp(-Up/sp), spp =sp/(e-1)
a, x1, x2 = N*spp/(2*x0), linspace(x0/r1, x0, r1), linspace(x0, 1, r2)
erfi = r_[erf(1j*sqrt(a)*(x1-x0-x1[0])).imag,0]
I, G1, G2 = sqrt(pi/(4*a))*exp(-a*x0**2)*(erfi[1:]-erfi[0]), np.exp(a*x1*(x1-2*x0)), np.exp(a*x2*(x2-2*x0))
Tclick = max(1/sp*(1-e*spp/Up), 0)
Tclick += N/2*(simps(r_[1,I/(x1*G1)],dx=x1[0])+I[-1]*simps(1./(x2*G2),dx=x2[1]-x2[0]))
return Up*1/Tclick
def moran(double Up, double sp, double N, int r1=150, int r2=600):
cdef double x0 = exp(-Up/sp), spp =sp/(e-1)
Tclick = exp(0.5*spp*N*x0)/spp
return Up*1/Tclick
#def moran(double Up, double sp, double N, double alpha=1/(e-1)):
# cdef:
# double No = N*exp(-Up/sp)
# double Nosa = No*sp*alpha
# return sqrt(Nosa*alpha*alpha/pi)*exp(-Nosa)
def wave(double Up, double sp, double N, double v_0=1e-12):
from scipy.optimize import brentq
x2 = sp*log(N)
if Up < x2:
No = N*exp(-Up/sp)
pi_p = sp/((1 + sp)**No - 1) #if sp > 5e-5 else 1/(No*(1+2*sp))
#y1 = Up*N*sp/((1 + sp)**N - 1)
#y2 = wave(x2, sp, N)*Up/x2
assert No*Up*pi_p > 0
return No*Up*pi_p #y2
cdef:
double iL = sp/Up
double Nd2spSqrtiL = N/2*sp*sqrt(iL)
double fiveSixthsiL = 5*iL/6
def f(double v):
cdef double A = log(e*Up/v), C = v/Up
return 1 - 0.5*C*(A*A + 1) - iL*log(Nd2spSqrtiL*sqrt(v*C*C/(Up - v))*A/(1 - C*A + fiveSixthsiL))
cdef double vp = brentq(f, v_0, Up - v_0)
assert vp > 0
return min(vp, Up)
@functools.lru_cache(maxsize=None)
def Vp(double Up, double sp, double N):
cdef double L = Up/sp
if L > 1:
return wave(Up, sp, N)
N_0 = N*exp(-L)
return moran(Up, sp, N) if sp*N_0 > 1 else (1-L)*moran(Up, sp, N) + L*wave(Up, sp, N)
class second(first):
@property
def f(self):
print('New f')
return fAnddN(self.Up, self.sd, self.sp, self.N_0)[0]*self.Ud
@property
def dN(self): return fAnddN(self.Up, self.sd, self.sp, self.N_0)[1]
@property
def vp(self):
print('New vp')
return self.sp*Vp(self.Up, self.sp, self.N_0)
@functools.lru_cache(maxsize=None)
def simplified_fanddN(double sd, double sp, double Up, double N_0):
from scipy.stats import poisson
cdef:
double Lambda = Up/sp
ndarray[int64_t, ndim=1] I = arange( int(sd/sp) + 1 )
int k = (poisson.cdf(arange(int(Lambda)), Lambda) < 0.5*pow(N_0, -0.5) ).sum()
ndarray[double_t, ndim=1] f = poisson.pmf(I + k, Lambda)
ndarray[double_t, ndim=1] W = sd - sp*I
return dot(f, W/(1+W)), dot(f, W)/f.sum()
class third(first):
@property
def f(self): return self.Ud*simplified_fanddN(self.sd, self.sp, self.Up, self.N_0)[0]
@property
def dN(self): return simplified_fanddN(self.sd, self.sp, self.Up, self.N_0)[1]
@property
def vp(self):
from scipy.stats import poisson
cdef:
double Up = self.Up
double sp = self.sp
double sd = self.sd
double Lambda = Up/sp
ndarray[int64_t, ndim=1] I = arange( int(sd/sp) + 1 )
int k = (poisson.cdf(arange(int(Lambda)), Lambda) < 0.5*pow(self.N_0, -0.5) ).sum()
double No = self.N_0*poisson.pmf(k, Lambda) #exp(-Lambda)
double pi_p = sp/((1 + sp)**No - 1) if sp > 5e-5 else 1/(No*(1+2*sp))
return Up*sp*No*pi_p if No > 1e-1 else Up*sp
def tC(model, x, n_low=0.35, n_high=7):
from scipy.integrate import quadrature
D = 2/model.dN
N_eq = model.N_eq
vp = model.vp
p = model.pC(x)
gD = gamma(D)
def I1(x):
p = model.pC(x)
de_mN = (D/x)**D*exp(-D/x)/gD
return (1 - p)*p/(de_mN*N_eq*N_eq)
def I2(x):
p = model.pC(x)
de_mN = (D/x)**D*exp(-D/x)/gD
return p*p/(de_mN*N_eq*N_eq)
if x < n_high:
C = D*N_eq/vp
if x < n_low:
return C*quadrature(I1, n_low, n_high)[0] + log(n_low/x)/vp + x/(D*vp)
else:
return C*(quadrature(I1, x, n_high)[0] + (1-p)/p*(gamma(D-1)*(1 - gammainc(D - 1, D/n_low))/(D*N_eq*gD) + quadrature(I2, n_low, x)[0])) + 1/(n_high*vp)
else:
return 1/(vp*x)
def hGillespie(model, long trials=10000000, double x_max=20):
"""Hybrid gillespie model"""
cdef:
double theta = 1 + model.dN
double tau_max = model.t_max*model.vp # tau = t*vp
double dimmensionless_if = model.vp/model.f/model.N_0 # In units of
double x, Q
long n_cancers = 0
long i
ndarray[double_t, ndim=1] Taus = empty(trials, dtype=double)
for i in range(trials):
x = 1
Taus[n_cancers] = 0
while x < x_max:
Q = 1 - exponential()*dimmensionless_if/x
x *= Q*theta
Taus[n_cancers] -= log(Q)
if Taus[n_cancers] > tau_max or x < 0:
break
else:
n_cancers += 1
return {'P_cancer':double(n_cancers)/trials, 'generations':Taus[0:n_cancers]/model.vp}
#def hGompGillespie(model, long trials=10000000, double y_max=20):
# Solution to waiting-time jump is transcendental equation, with Exponential Integral function in it!
# """hybrid gillespie using gomp-ex death rate"""
#
# cdef:
# double vp = model.vp
# double theta = 1 + model.dN
# double jumpRate = model.f*model.N_0/vp
# double em1dJump = (e - 1)/jumpRate
# double omega_d
# double sigma_d
# double eSigma_d
# double t
#
# for i in range(trials):
# y = 1
# sigma_d = 0
# omega_d = -sigma_d*vp
# t = 0
# eSigma_d = exp(sigma_d)
B2 = array([5.72640947, -4.84536097])
B3 = array([5.77629529, -4.94655988, 0.04817712])
B4 = array([10.82164987, -16.63749275, 8.31660758, -1.75101626])
B5 = array([10.78285714, -16.57518638, 8.32469893, -1.79888812, 0.01717027])
B6 = array([12.38219463, -22.35712125, 15.97711017, -6.31622209, 1.13227449, -0.07230064])
def empiricalPcancer10(y, f=lambda y, B: 1/(1+exp((B*pow(y, arange(len(B)))).sum() )), B=B6):
"""assumes sd=0.1 """
return f(y, B)
#Pcancer = {0.1: 0.415*N_eq,
#0.2:67,
#0.05: 0.94*N_eq =
#0.96*N_eq =
#0.98*N_eq =
#}
# while y < y_max:
# dt = log(em1dJump*omega_d*exponential() + 1)/omega_d
# y *= eTheta
# t = log(omega_d*em1/(jumpRate*eSigma_d)*exponential() + exp(omega_d*t))/omega_d
# sigma_d += theta
# eSigma_d = nan
# y *= eSigma
#def ndnp(double Ud=1e-8, double sd=0.1, double sp=0.001, double K=1e3, double Td=700, double Tp=5000000, double Genome_size = 149948690/4, long trials=50000000, double Nmax=2):
#
#random.poisson(T*u*(Genome_size-2*Tp))
# cdef double Ud = 2*u*Td, Up = 2*u*Tp
# out = lambdaG(Ud, Up, sd, sp, No)
# cdef double vp = sp*Vp(sp, Up, No), L = out[0], dN = out[1]+1, N, Q, T, m_hitchhikers = (sd - out[1])/sp
# cdef ndarray[uint32_t, ndim=2] Trial = empty((Iter,3),uint32)
# cdef uint32_t i, j = 0, d
# for i in range(Iter):
# N, T, d = No, 0, 0
# while N > 0 and T < Time:
# Q = 1 - exponential()*vp/(N*L)
# N *= Q*dN
# d += 1
# T -= log(Q)/vp
# if N > No*Top:
# j+=1
# break
# print j
# return Trial[:j,:]