import numpy import os import scipy.interpolate import filionQuadrature def ln(mu,cv): #generate log-normal distributed variable with mean mu and coefficient of variation cv m=numpy.log(mu/numpy.sqrt(1+cv*cv)) s=numpy.sqrt(numpy.log(1+cv*cv)) return ln_s(m,s) def ln_s(mu_s,sigma_s): #generate r.v. with log-normal distribution #mu_s and sigma_s are parameters of the normal distribution in the exponent #see ln() for targeted mean and coefficient of variation z=numpy.random.normal() return numpy.exp(mu_s+sigma_s*z) def readCoef(sigma): #read coefficients of gamma distribution set that approximates LN distro #with mu_s=0 and sigma_s=sigma, valid for 0.05<=sigma<=3 #from #Furman E, Hackmann D, Kuznetsov A. #On log-normal convolutions: An analytical–numerical method with #applications to economic capital determination. #Insurance: Mathematics and Economics, 90 (2020) 120-134. #return fa and fb which are alpha and beta for n=20 approximations fdir=os.path.join(os.path.expanduser('~'),'software','src','PBPK', 'lnnCoeficients20','cBase.txt') with open(fdir,'r') as f: lines=f.read().splitlines() fm=[q.split(' ') for q in lines] fm=numpy.array([[float(q) for q in x] for x in fm]) fn=(fm.shape[1]-1)//2 #print(fn) fa=fm[:,1:fn+1] fb=fm[:,fn+1:2*fn+1] fx=fm[:,0] falpha=[] fbeta=[] for i in range(fn): f2a = scipy.interpolate.interp1d(fx, fa[:,i], kind='cubic') try: falpha.append(f2a(sigma)) except ValueError: #interpolation failed if sigmaa>-0.5 was the range tested in paper, using upper limit fa=complex(-0.1,1) #internal structures qh=numpy.linspace(u0,u1,2*n+1) h=(u1-u0)/2/n #curve segmentation qz=c+fa/numpy.absolute(fa)*qh return qz,fa,u0,h def inverseL(fx,qz,qPhi,fa,u0,h,n): #Filion quadratures by parts of complex numbers fI=numpy.zeros(len(fx),dtype=complex) for i in range(len(fx)): x=fx[i] qPhi1=addExpA(qz,qPhi,x) fRc=filionQuadrature.filionQuadrature(numpy.real(qPhi1),x,u0,h,n,'cos') fIc=filionQuadrature.filionQuadrature(numpy.imag(qPhi1),x,u0,h,n,'cos') fRs=filionQuadrature.filionQuadrature(numpy.real(qPhi1),x,u0,h,n,'sin') fIs=filionQuadrature.filionQuadrature(numpy.imag(qPhi1),x,u0,h,n,'sin') fI[i]=complex(fRc-fIs,fIc+fRs) fI1=fa*fI return numpy.imag(fI1) def convolveLN(fx,sigma,mu,sign=numpy.array([])): #convolve a set of LN with parameters sigma and mu as numpy arrays and #return the pdf of convolution evaluated at the set of points fx #zArray #number of grid points 1M suggested in paper, good fit found for few thousand n=2000 qz,fa,u0,h=getZArray(n) #function values at grid points qPhi=numpy.ones(qz.size,dtype=complex) for i in range(len(sigma)): m=mu[i] s=sigma[i] try: if not sign[i]: qPhi*=getComplexConjugatedLTransformAtMinusComplexConjugatedZGrid(s,qz,m) continue except IndexError: pass qPhi*=getLTransformGrid(sigma[i],qz,mu[i]) return inverseL(fx,qz,qPhi,fa,u0,h,n)