123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- import numpy
- import functools
- import math
- def fval(t,W):
- if callable(W):
- return W(t)
- return W
- def L(t,t0,W):
- ft0=fval(t,t0)
- fW=fval(t,W)
- x=(t-ft0)/fW
- if x<-18:
- return 0
- if x>18:
- return 1
- return 1/(1+numpy.exp(-x))
- def dL(t,t0,W):
- return L(t,t0,W)*(1-L(t,t0,W))
- def dLdW(t,t0,W):
- fW=fval(t,W)
- ft0=fval(t,t0)
- return dL(t,t0,W)*(t-ft0)/fW/fW
- def dLdt0(t,t0,W):
- fW=fval(t,W)
- return dL(t,t0,W)/fW
- def logisticPar(par):
- t0=par['t0']['value']
- W=par['W']['value']
- f=functools.partial(L,t0=t0,W=W)
-
- #D1[x] could be a function !!!
- Dt0=par['t0']['derivatives']
- dfdt0=functools.partial(dLdt0,t0=t0,W=W)
- dt0=generate(dfdt0,Dt0)
- #D2[x] could be a function !!!
- DW=par['W']['derivatives']
- dfdW=functools.partial(dLdW,t0=t0,W=W)
- dW=generate(dfdW,DW)
- #merge
- return Object(f,[dt0,dW])
- def deltaW(t,t1,t2):
- ft1=fval(t,t1)
- ft2=fval(t,t2)
- return ft2-ft1
- def deltaT(t,t1):
- ft1=fval(t,t1)
- return t-t1
- def fq(t,t1,t2):
- return deltaT(t,t1)/deltaW(t,t1,t2)
- def f1(t,C,t1,t2,L1,L2):
- fC=fval(t,C)
- return fC*fq(t,t1,t2)*L1(t)*(1-L2(t))
- def f2(t,C,L1,L2):
- fC=fval(t,C)
- return fC*L1(t)*L2(t)
- def lg(t,C,t1,t2,eps,L1,L2):
- fC=fval(t,C)
- return eps*fC+f1(t,C,t1,t2,L1,L2)+f2(t,C,L1,L2)
- def dlgdt1(t,C,t1,t2,L1,L2):
- fC=fval(t,C)
- return -fC*L1(t)*(1-L2(t))*(1+fq(t,t1,t2))/deltaW(t,t1,t2)
- def dlgdt2(t,C,t1,t2,L1,L2):
- return -f1(t,C,t1,t2,L1,L2)/deltaW(t,t1,t2)
- def dlgdL1(t,C,t1,t2,L1,L2):
- fC=fval(t,C)
- fL2=L2(t)
- return fC*(fq(t,t1,t2)*(1-fL2)+fL2)
- def dlgdL2(t,C,t1,t2,L1,L2):
- fC=fval(t,C)
- return fC*L1(t)*(1-fq(t,t1,t2))
- def dlgdC(t,C,t1,t2,L1,L2):
- fL2=L2(t)
- return L1(t)*(fq(t,t1,t2)*(1-fL2)+fL2)
- def lgFixedSlope(t,alpha,t1,t2,eps,L1,L2):
- fA=fval(t,alpha)
- ft1=fval(t,t1)
- dW=deltaW(t,t1,t2)
- return fA*(dW*eps+L1(t)*((t-ft1)*(1-L2(t))+dW*L2(t)))
- def dlgFixedSlopedt1(t,alpha,L1,L2):
- fA=fval(t,alpha)
- return -fA*L1(t)
- def dlgFixedSlopedt2(t,alpha,L1,L2):
- fA=fval(t,alpha)
- return -fA*L1(t)*L2(t)
- def dlgFixedSlopedL1(t,alpha,t1,t2,L1,L2):
- return lgFixedSlope(t,alpha,t1,t2,L1,L2)/L1(t)
- def dlgFixedSlopedL2(t,alpha,t1,t2,L1,L2):
- fA=fval(t,alpha)
- ft2=fval(t,t2)
- return fA*L1(t)*(ft2-t)
- def dlgFixedSlopedalpha(t,alpha,t1,t2,L1,L2):
- fA=fval(t,alpha)
- return lgFixedSlope(t,alpha,t1,t2,L1,L2)/fA
- def linearGrowth(par):
- #make sure we never return zero or negative
- C=par['C']['value']
- t1=par['t1']['value']
- t2=par['t2']['value']
- eps=1e-8
- pL1=logisticPar({'t0':par['t1'],'W':par['W1']})
- pL2=logisticPar({'t0':par['t2'],'W':par['W2']})
- L1=pL1['value']
- L2=pL2['value']
- f=functools.partial(lg,C=C,t1=t1,t2=t2,eps=eps,L1=L1,L2=L2)
- Dt1=par['t1']['derivatives']
- dfdt1=functools.partial(dlgdt1,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
- dt1=generate(dfdt1,Dt1)
-
- Dt2=par['t2']['derivatives']
- dfdt2=functools.partial(dlgdt2,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
- dt2=generate(dfdt2,Dt2)
- DL1=pL1['derivatives']
- dfdL1=functools.partial(dlgdL1,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
- dL1=generate(dfdL1,DL1)
- DL2=pL2['derivatives']
- dfdL2=functools.partial(dlgdL2,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
- dL2=generate(dfdL2,DL2)
- DC=par['C']['derivatives']
- dfdC=functools.partial(dlgdC,C=C,t1=t1,t2=t2,L1=L1,L2=L2)
- dC=generate(dfdC,DC)
- return Object(f,[dt1,dt2,dL1,dL2,dC])
- def linearGrowthFixedSlope(par):
- #make sure we never return zero or negative
- alpha=par['alpha']['value']
- t1=par['t1']['value']
- t2=par['t2']['value']
- eps=1e-8
- pL1=logisticPar({'t0':par['t1'],'W':par['W1']})
- pL2=logisticPar({'t0':par['t2'],'W':par['W2']})
- L1=pL1['value']
- L2=pL2['value']
- f=functools.partial(lgFixedSlope,alpha=alpha,t1=t1,t2=t2,eps=eps,L1=L1,L2=L2)
- Dt1=par['t1']['derivatives']
- dfdt1=functools.partial(dlgFixedSlopedt1,alpha=alpha,L1=L1,L2=L2)
- dt1=generate(dfdt2,Dt1)
-
- Dt2=par['t2']['derivatives']
- dfdt2=functools.partial(dlgFixedSlopedt2,alpha=alpha,L1=L1,L2=L2)
- dt2=generate(dfdt2,Dt2)
- DL1=pL1['derivatives']
- dfdL1=functools.partial(dlgFixedSlopedL1,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
- dL1=generate(dfdL1,DL1)
- DL2=pL2['derivatives']
- dfdL2=functools.partial(dlgFixedSlopedL2,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
- dL2=generate(dfdL2,DL2)
- DA=par['alpha']['derivatives']
- dfdA=functools.partial(dlgFixedSlopedalpha,alpha=alpha,t1=t1,t2=t2,L1=L1,L2=L2)
- dA=generate(dfdA,DA)
- return Object(f,[dt1,dt2,dL1,dL2,dA])
- def feps(t,A,tau):
- fA=fval(t,A)
- fTau=fval(t,tau)
- return fA*math.exp(-t/fTau)
- def dfepsdA(t,A,tau):
- fTau=fval(t,tau)
- return math.exp(-t/fTau)
- def dfepsdTau(t,A,tau):
- fTau=fval(t,tau)
- fA=fval(t,A)
- if fTau==0:
- return 0
- return fA*math.exp(-t/fTau)*(t/fTau/fTau)
- def exp(par):
- A=par['A']['value']
- tau=par['tau']['value']
- f=functools.partial(feps,A=A,tau=tau)
- DA=par['A']['derivatives']
- dfdA=functools.partial(dfepsdA,A=A,tau=tau)
- dA=generate(dfdA,DA)
- DTau=par['tau']['derivatives']
- dfdTau=functools.partial(dfepsdTau,A=A,tau=tau)
- dTau=generate(dfdTau,DTau)
-
- return Object(f,[dA,dTau])
- def Object(v,dArray):
- o={'value':v,'function':True}
- allKeys=[]
- for x in dArray:
- allKeys.extend(x.keys())
- allKeys=list(set(allKeys))
- o['derivatives']={x:sumDict(dArray,x) for x in allKeys}
- return o
- def derivedObject(f,ders):
- o={'value':f}
- DD=[]
- for d in ders:
- df=d['df']
- D=d['D']
- DD.append({x:df*D[x] for x in D})
- allKeys=[]
- for x in DD:
- allKeys.extend(x.keys())
- allKeys=list(set(allKeys))
- o['derivatives']={x:sumValues(DD,x) for x in allKeys}
- return o
-
- def sumDict(dArray,x):
- #print('sumFunctions for {}'.format(x))
- fs=[]
- for a in dArray:
- #print('\tChecking {}'.format(a))
- try:
- #check if a[x] is a function with a dummy parameter
- fs.append(a[x])
- #print('\tAdding [{}]'.format(v))
- except KeyError:
- #print('\t{} not in table'.format(x))
- continue
- return sumArray(fs)
- def sumArray(fs,w=1):
- return lambda t,fs=fs,w=w: w*sum([to(f)(t) for f in fs])
- def sumValues(dArray,x):
- s=0
- for a in dArray:
- try:
- s+=a[x]
- except KeyError:
- continue
- return s
- def isFunction(vObject):
- if callable(vObject['value']):
- return True
- return False
- def to(a):
- if callable(a):
- return a
- return lambda t,a=a:a
-
- def contains(arr):
- for a in arr:
- if callable(a):
- return True
- return False
- def multiply(t,a,b):
- #abstract actual type of a and b
- fa=fval(t,a)
- fb=fval(t,b)
- return fa*fb
- def generate(df,D):
- return {x:functools.partial(multiply,a=D[x],b=df) for x in D}
- def product(pA,pB):
- #return a product of two values with derivatives
- #print('{}*{}'.format(d['a'],d['b']))
- a=pA['value']
- DA=pA['derivatives']
- b=pB['value']
- DB=pB['derivatives']
- if any(['function' in pA,'function' in pB]):
- fa=to(a)
- fb=to(b)
- f=lambda t,a=fa,b=fb,:a(t)*b(t)
- dfdA=lambda t,b=fb: b(t)
- dfdB=lambda t,a=fa: a(t)
- dA=generate(dfdA,DA)
- dB=generate(dfdB,DB)
- return Object(f,[dA,dB])
- else:
- return derivedObject(a*b,[{'df':b,'D':DA},{'df':a,'D':DB}])
- def power(pA,pN):
- #return pA to the power of pN, numpy.power(pA,pN), with derivatives
- a=pA['value']
- DA=pA['derivatives']
- n=pN['value']
- DN=pN['derivatives']
- if any(['function' in pA,'function' in pN]):
- fa=to(a)
- fn=to(n)
- f=lambda t,a=fa,n=fn:numpy.power(a(t),n(t))
- dfdA=lambda t,n=fn,f=f,a=fa:n(t)*f(t)/a(t)
- dfdN=lambda t,a=fa,f=f:numpy.log(a(t))*f(t)
- dA=generate(dfdA,DA)
- dN=generate(dfdN,DN)
- return Object(f,[dA,dN])
- else:
- f=numpy.power(a,n)
- return derivedObject(f,[{'df':n*f/a,'D':DA},{'df':f*numpy.log(a),'D':DN}])
- def ratio(pA,pB):
- #return ratio pA/pB with all derivatives
- #print('{}/{}'.format(d['a'],d['b']))
- a=pA['value']
- DA=pA['derivatives']
- b=pB['value']
- DB=pB['derivatives']
- if any(['function' in pA,'function' in pB]):
- fa=to(a)
- fb=to(b)
- f=lambda t,a=fa,b=fb,:a(t)/b(t)
- dfdA=lambda t,f=f,a=fa: f(t)/a(t)
- dfdB=lambda t,f=f,b=fb: -f(t)/b(t)
- dA=generate(dfdA,DA)
- dB=generate(dfdB,DB)
- return Object(f,[dA,dB])
- else:
- return derivedObject(a/b,[{'df':1/b,'D':DA},{'df':-a/b/b,'D':DB}])
- def add(pA,pB):
- #return sum of pA and pB with derivatives
- #print('{}+{}'.format(d['a'],d['b']))
- a=pA['value']
- DA=pA['derivatives']
- b=pB['value']
- DB=pB['derivatives']
- #even more generic -> df/dp=[df/dA*dA/dp+df/dB*dB/dp]
- if any(['function' in pA,'function' in pB]):
- fa=to(a)
- fb=to(b)
- f=lambda t,a=fa,b=fb,:a(t)+b(t)
- dfdA=lambda t: 1
- dfdB=lambda t: 1
- dA=generate(dfdA,DA)
- dB=generate(dfdB,DB)
- return Object(f,[dA,dB])
- else:
- return derivedObject(a+b,[{'df':1,'D':DA},{'df':1,'D':DB}])
|