function.py 8.1 KB

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