function.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. import numpy
  2. import functools
  3. import math
  4. def fval(t,W):
  5. if callable(W):
  6. return W(t)
  7. return W
  8. def L(t,t0,W):
  9. ft0=fval(t,t0)
  10. fW=fval(t,W)
  11. x=(t-ft0)/fW
  12. if x<-18:
  13. return 0
  14. if x>18:
  15. return 1
  16. return 1/(1+numpy.exp(-x))
  17. def dL(t,t0,W):
  18. return L(t,t0,W)*(1-L(t,t0,W))
  19. def dLdW(t,t0,W):
  20. fW=fval(t,W)
  21. ft0=fval(t,t0)
  22. return dL(t,t0,W)*(t-ft0)/fW/fW
  23. def dLdt0(t,t0,W):
  24. fW=fval(t,W)
  25. return dL(t,t0,W)/fW
  26. def logisticPar(par):
  27. t0=par['t0']['value']
  28. W=par['W']['value']
  29. f=functools.partial(L,t0=t0,W=W)
  30. #D1[x] could be a function !!!
  31. Dt0=par['t0']['derivatives']
  32. dfdt0=functools.partial(dLdt0,t0=t0,W=W)
  33. dt0=generate(dfdt0,Dt0)
  34. #D2[x] could be a function !!!
  35. DW=par['W']['derivatives']
  36. dfdW=functools.partial(dLdW,t0=t0,W=W)
  37. dW=generate(dfdW,DW)
  38. #merge
  39. return Object(f,[dt0,dW])
  40. def deltaW(t,t1,t2):
  41. ft1=fval(t,t1)
  42. ft2=fval(t,t2)
  43. return ft2-ft1
  44. def deltaT(t,t1):
  45. ft1=fval(t,t1)
  46. return t-t1
  47. def fq(t,t1,t2):
  48. return deltaT(t,t1)/deltaW(t,t1,t2)
  49. def f1(t,C,t1,t2,L1,L2):
  50. fC=fval(t,C)
  51. return fC*fq(t,t1,t2)*L1(t)*(1-L2(t))
  52. def f2(t,C,L1,L2):
  53. fC=fval(t,C)
  54. return fC*L1(t)*L2(t)
  55. def lg(t,C,t1,t2,eps,L1,L2):
  56. fC=fval(t,C)
  57. return eps*fC+f1(t,C,t1,t2,L1,L2)+f2(t,C,L1,L2)
  58. def dlgdt1(t,C,t1,t2,L1,L2):
  59. fC=fval(t,C)
  60. return -fC*L1(t)*(1-L2(t))*(1+fq(t,t1,t2))/deltaW(t,t1,t2)
  61. def dlgdt2(t,C,t1,t2,L1,L2):
  62. return -f1(t,C,t1,t2,L1,L2)/deltaW(t,t1,t2)
  63. def dlgdL1(t,C,t1,t2,L1,L2):
  64. fC=fval(t,C)
  65. fL2=L2(t)
  66. return fC*(fq(t,t1,t2)*(1-fL2)+fL2)
  67. def dlgdL2(t,C,t1,t2,L1,L2):
  68. fC=fval(t,C)
  69. return fC*L1(t)*(1-fq(t,t1,t2))
  70. def dlgdC(t,C,t1,t2,L1,L2):
  71. fL2=L2(t)
  72. return L1(t)*(fq(t,t1,t2)*(1-fL2)+fL2)
  73. def lgFixedSlope(t,alpha,t1,t2,eps,L1,L2):
  74. fA=fval(t,alpha)
  75. ft1=fval(t,t1)
  76. dW=deltaW(t,t1,t2)
  77. return fA*(dW*eps+L1(t)*((t-ft1)*(1-L2(t))+dW*L2(t)))
  78. def dlgFixedSlopedt1(t,alpha,L1,L2):
  79. fA=fval(t,alpha)
  80. return -fA*L1(t)
  81. def dlgFixedSlopedt2(t,alpha,L1,L2):
  82. fA=fval(t,alpha)
  83. return -fA*L1(t)*L2(t)
  84. def dlgFixedSlopedL1(t,alpha,t1,t2,L1,L2):
  85. return lgFixedSlope(t,alpha,t1,t2,L1,L2)/L1(t)
  86. def dlgFixedSlopedL2(t,alpha,t1,t2,L1,L2):
  87. fA=fval(t,alpha)
  88. ft2=fval(t,t2)
  89. return fA*L1(t)*(ft2-t)
  90. def dlgFixedSlopedalpha(t,alpha,t1,t2,L1,L2):
  91. fA=fval(t,alpha)
  92. return lgFixedSlope(t,alpha,t1,t2,L1,L2)/fA
  93. def linearGrowth(par):
  94. #make sure we never return zero or negative
  95. C=par['C']['value']
  96. t1=par['t1']['value']
  97. t2=par['t2']['value']
  98. eps=1e-8
  99. pL1=logisticPar({'t0':par['t1'],'W':par['W1']})
  100. pL2=logisticPar({'t0':par['t2'],'W':par['W2']})
  101. L1=pL1['value']
  102. L2=pL2['value']
  103. f=functools.partial(lg,C=C,t1=t1,t2=t2,eps=eps,L1=L1,L2=L2)
  104. Dt1=par['t1']['derivatives']
  105. dfdt1=functools.partial(dlgdt1,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
  106. dt1=generate(dfdt1,Dt1)
  107. Dt2=par['t2']['derivatives']
  108. dfdt2=functools.partial(dlgdt2,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
  109. dt2=generate(dfdt2,Dt2)
  110. DL1=pL1['derivatives']
  111. dfdL1=functools.partial(dlgdL1,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
  112. dL1=generate(dfdL1,DL1)
  113. DL2=pL2['derivatives']
  114. dfdL2=functools.partial(dlgdL2,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
  115. dL2=generate(dfdL2,DL2)
  116. DC=par['C']['derivatives']
  117. dfdC=functools.partial(dlgdC,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
  118. dC=generate(dfdC,DC)
  119. return Object(f,[dt1,dt2,dL1,dL2,dC])
  120. def linearGrowthFixedSlope(par):
  121. #make sure we never return zero or negative
  122. alpha=par['alpha']['value']
  123. t1=par['t1']['value']
  124. t2=par['t2']['value']
  125. eps=1e-8
  126. pL1=logisticPar({'t0':par['t1'],'W':par['W1']})
  127. pL2=logisticPar({'t0':par['t2'],'W':par['W2']})
  128. L1=pL1['value']
  129. L2=pL2['value']
  130. f=functools.partial(lgFixedSlope,alpha=alpha,t1=t1,t2=t2,eps=eps,L1=L1,L2=L2)
  131. Dt1=par['t1']['derivatives']
  132. dfdt1=functools.partial(dlgFixedSlopedt1,alpha=alpha,L1=L1,L2=L2)
  133. dt1=generate(dfdt2,Dt1)
  134. Dt2=par['t2']['derivatives']
  135. dfdt2=functools.partial(dlgFixedSlopedt2,alpha=alpha,L1=L1,L2=L2)
  136. dt2=generate(dfdt2,Dt2)
  137. DL1=pL1['derivatives']
  138. dfdL1=functools.partial(dlgFixedSlopedL1,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
  139. dL1=generate(dfdL1,DL1)
  140. DL2=pL2['derivatives']
  141. dfdL2=functools.partial(dlgFixedSlopedL2,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
  142. dL2=generate(dfdL2,DL2)
  143. DA=par['alpha']['derivatives']
  144. dfdA=functools.partial(dlgFixedSlopedalpha,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
  145. dA=generate(dfdA,DA)
  146. return Object(f,[dt1,dt2,dL1,dL2,dA])
  147. def feps(t,A,tau):
  148. fA=fval(t,A)
  149. fTau=fval(t,tau)
  150. return fA*math.exp(-t/fTau)
  151. def dfepsdA(t,A,tau):
  152. fTau=fval(t,tau)
  153. return math.exp(-t/fTau)
  154. def dfepsdTau(t,A,tau):
  155. fTau=fval(t,tau)
  156. fA=fval(t,A)
  157. if fTau==0:
  158. return 0
  159. return fA*math.exp(-t/fTau)*(t/fTau/fTau)
  160. def exp(par):
  161. A=par['A']['value']
  162. tau=par['tau']['value']
  163. f=functools.partial(feps,A=A,tau=tau)
  164. DA=par['A']['derivatives']
  165. dfdA=functools.partial(dfepsdA,A=A,tau=tau)
  166. dA=generate(dfdA,DA)
  167. DTau=par['tau']['derivatives']
  168. dfdTau=functools.partial(dfepsdTau,A=A,tau=tau)
  169. dTau=generate(dfdTau,DTau)
  170. return Object(f,[dA,dTau])
  171. def Object(v,dArray):
  172. o={'value':v,'function':True}
  173. allKeys=[]
  174. for x in dArray:
  175. allKeys.extend(x.keys())
  176. allKeys=list(set(allKeys))
  177. o['derivatives']={x:sumDict(dArray,x) for x in allKeys}
  178. return o
  179. def derivedObject(f,ders):
  180. o={'value':f}
  181. DD=[]
  182. for d in ders:
  183. df=d['df']
  184. D=d['D']
  185. DD.append({x:df*D[x] for x in D})
  186. allKeys=[]
  187. for x in DD:
  188. allKeys.extend(x.keys())
  189. allKeys=list(set(allKeys))
  190. o['derivatives']={x:sumValues(DD,x) for x in allKeys}
  191. return o
  192. def sumDict(dArray,x):
  193. #print('sumFunctions for {}'.format(x))
  194. fs=[]
  195. for a in dArray:
  196. #print('\tChecking {}'.format(a))
  197. try:
  198. #check if a[x] is a function with a dummy parameter
  199. fs.append(a[x])
  200. #print('\tAdding [{}]'.format(v))
  201. except KeyError:
  202. #print('\t{} not in table'.format(x))
  203. continue
  204. return sumArray(fs)
  205. def sumArray(fs,w=1):
  206. return lambda t,fs=fs,w=w: w*sum([to(f)(t) for f in fs])
  207. def sumValues(dArray,x):
  208. s=0
  209. for a in dArray:
  210. try:
  211. s+=a[x]
  212. except KeyError:
  213. continue
  214. return s
  215. def isFunction(vObject):
  216. if callable(vObject['value']):
  217. return True
  218. return False
  219. def to(a):
  220. if callable(a):
  221. return a
  222. return lambda t,a=a:a
  223. def contains(arr):
  224. for a in arr:
  225. if callable(a):
  226. return True
  227. return False
  228. def multiply(t,a,b):
  229. #abstract actual type of a and b
  230. fa=fval(t,a)
  231. fb=fval(t,b)
  232. return fa*fb
  233. def generate(df,D):
  234. return {x:functools.partial(multiply,a=D[x],b=df) for x in D}
  235. def product(pA,pB):
  236. #return a product of two values with derivatives
  237. #print('{}*{}'.format(d['a'],d['b']))
  238. a=pA['value']
  239. DA=pA['derivatives']
  240. b=pB['value']
  241. DB=pB['derivatives']
  242. if any(['function' in pA,'function' in pB]):
  243. fa=to(a)
  244. fb=to(b)
  245. f=lambda t,a=fa,b=fb,:a(t)*b(t)
  246. dfdA=lambda t,b=fb: b(t)
  247. dfdB=lambda t,a=fa: a(t)
  248. dA=generate(dfdA,DA)
  249. dB=generate(dfdB,DB)
  250. return Object(f,[dA,dB])
  251. else:
  252. return derivedObject(a*b,[{'df':b,'D':DA},{'df':a,'D':DB}])
  253. def power(pA,pN):
  254. #return pA to the power of pN, numpy.power(pA,pN), with derivatives
  255. a=pA['value']
  256. DA=pA['derivatives']
  257. n=pN['value']
  258. DN=pN['derivatives']
  259. if any(['function' in pA,'function' in pN]):
  260. fa=to(a)
  261. fn=to(n)
  262. f=lambda t,a=fa,n=fn:numpy.power(a(t),n(t))
  263. dfdA=lambda t,n=fn,f=f,a=fa:n(t)*f(t)/a(t)
  264. dfdN=lambda t,a=fa,f=f:numpy.log(a(t))*f(t)
  265. dA=generate(dfdA,DA)
  266. dN=generate(dfdN,DN)
  267. return Object(f,[dA,dN])
  268. else:
  269. f=numpy.power(a,n)
  270. return derivedObject(f,[{'df':n*f/a,'D':DA},{'df':f*numpy.log(a),'D':DN}])
  271. def ratio(pA,pB):
  272. #return ratio pA/pB with all derivatives
  273. #print('{}/{}'.format(d['a'],d['b']))
  274. a=pA['value']
  275. DA=pA['derivatives']
  276. b=pB['value']
  277. DB=pB['derivatives']
  278. if any(['function' in pA,'function' in pB]):
  279. fa=to(a)
  280. fb=to(b)
  281. f=lambda t,a=fa,b=fb,:a(t)/b(t)
  282. dfdA=lambda t,f=f,a=fa: f(t)/a(t)
  283. dfdB=lambda t,f=f,b=fb: -f(t)/b(t)
  284. dA=generate(dfdA,DA)
  285. dB=generate(dfdB,DB)
  286. return Object(f,[dA,dB])
  287. else:
  288. return derivedObject(a/b,[{'df':1/b,'D':DA},{'df':-a/b/b,'D':DB}])
  289. def add(pA,pB):
  290. #return sum of pA and pB with derivatives
  291. #print('{}+{}'.format(d['a'],d['b']))
  292. a=pA['value']
  293. DA=pA['derivatives']
  294. b=pB['value']
  295. DB=pB['derivatives']
  296. #even more generic -> df/dp=[df/dA*dA/dp+df/dB*dB/dp]
  297. if any(['function' in pA,'function' in pB]):
  298. fa=to(a)
  299. fb=to(b)
  300. f=lambda t,a=fa,b=fb,:a(t)+b(t)
  301. dfdA=lambda t: 1
  302. dfdB=lambda t: 1
  303. dA=generate(dfdA,DA)
  304. dB=generate(dfdB,DB)
  305. return Object(f,[dA,dB])
  306. else:
  307. return derivedObject(a+b,[{'df':1,'D':DA},{'df':1,'D':DB}])