propagateErrorLN.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import numpy
  2. import scipy.interpolate
  3. import importlib
  4. import convolveLN
  5. importlib.reload(convolveLN)
  6. def generateSample(mu,cv):
  7. return numpy.array([convolveLN.ln(mu,cv) for i in range(10000)])
  8. def getEdges(a):
  9. #convert array of bin centers a to array of bin edges with one more element than a
  10. a1=numpy.zeros(len(a)+1)
  11. a2=numpy.zeros(a1.shape)
  12. a1[1:]=a
  13. a2[:-1]=a
  14. e=0.5*(a1+a2)
  15. e[0]=2*e[1]-e[2]
  16. e[-1]=2*e[-2]-e[-3]
  17. return e
  18. def calculateDistribution(fy,muTarget,mu,cv,dydp):
  19. #calculate distribution of random variable y with a functional dependence
  20. #on random parameters p, each with a log-normal
  21. #distribution with a mean mu(p) and coefficients of variation cv(p)
  22. #and dydp giving partial derivatives of y on p
  23. #muTarget is the target mean of y distribution
  24. #fy is the set of values y where distribution is evaluated at
  25. #Parameters mu, cv and dydp are given as vectors/lists
  26. #parameters qa should be around 1
  27. qa=mu*dydp
  28. print('qa={}'.format(qa))
  29. sign=qa>=0
  30. Ax=numpy.sum(qa[sign])
  31. Ay=numpy.sum(-qa[~sign])
  32. print('A={},{}'.format(Ax,Ay))
  33. if Ax>0:
  34. qa[sign]/=Ax
  35. if Ay>0:
  36. qa[~sign]/=Ay
  37. cvPrime=cv
  38. print('cvPrime={}'.format(cvPrime))
  39. #convolve qa multiplied (1,cv) distributions
  40. sigmaS=numpy.sqrt(numpy.log(1+cvPrime*cvPrime))
  41. print('sigmaS={}'.format(sigmaS))
  42. muS=numpy.zeros(qa.shape)
  43. muS[sign]=numpy.log(qa[sign]/numpy.sqrt(1+cvPrime[sign]*cvPrime[sign]))
  44. muS[~sign]=numpy.log(-qa[~sign]/numpy.sqrt(1+cvPrime[~sign]*cvPrime[~sign]))
  45. print('muS={}'.format(muS))
  46. #do the convolution
  47. nbins=100
  48. umax=5
  49. fw=numpy.linspace(0,umax,nbins)
  50. wx=numpy.array([])
  51. wy=numpy.array([])
  52. if Ax>0:
  53. wx=convolveLN.convolveLN(fw,sigmaS[sign],muS[sign])
  54. if Ay>0:
  55. wy=convolveLN.convolveLN(fw,sigmaS[~sign],muS[~sign])
  56. #for correlate, binning of wx and wy should be equal, but one has scale A1, the other A2
  57. #how do you deal with that?
  58. if Ax>0:
  59. fwx=Ax*fw
  60. if Ay>0:
  61. fwy=Ay*fw
  62. fw=fwx
  63. if not Ay>Ax:
  64. if wy.size>0:
  65. #re-sample wy
  66. wy = scipy.interpolate.CubicSpline(fwy, wy)(fw)
  67. if Ay>Ax:
  68. fw=fwy
  69. #resample wx
  70. if wx.size>0:
  71. wx = scipy.interpolate.CubicSpline(fwx, wx)(fw)
  72. #output is twice the length of w and symmetric around 0
  73. fw1=numpy.concatenate((numpy.flip(-fw[1:]),fw))
  74. w=numpy.zeros(fw1.size)
  75. if wx.size==0:
  76. w[:len(wy)]=numpy.flip(wy)
  77. if wy.size==0:
  78. w[-len(wx):]=wx
  79. if wx.size>0 and wy.size>0:
  80. w=scipy.signal.correlate(wx,wy)
  81. #w is a convolution of input LN distributions with a mean of A
  82. #now shift it to the target meat muTarget
  83. B=muTarget-(Ax-Ay)
  84. print('B={}'.format(B))
  85. #interpolate on w and fill y
  86. f = scipy.interpolate.CubicSpline(fw1, w)
  87. y=numpy.zeros(fy.shape)
  88. for i in range(len(y)):
  89. try:
  90. y[i]=f(fy[i]-B)
  91. except ValueError:
  92. pass
  93. return y/numpy.sum(y)
  94. def generateDistribution(fx,muTarget,mu,cv,dydp, dbg=0):
  95. #sum randomly generated variables to approximate distribution of
  96. #variable y with a functional dependence y(p) on random parameters p,
  97. #each with a log-normal (LN) distribution
  98. #with a mean mu(p) and coefficient of variation cv(p)
  99. #target mean of y is muTarget
  100. #distribution is evaluated at points fy,
  101. #dydp are derivatives of y with respect to parameters p
  102. qa=mu*dydp
  103. A=numpy.sum(qa)
  104. #qa*=A
  105. print('qa={}'.format(qa))
  106. cvPrime=cv
  107. samples=[a*generateSample(1,c) for a,c in zip(qa,cvPrime)]
  108. mean=[numpy.mean(s) for s in samples]
  109. std=[numpy.std(s) for s in samples]
  110. convSample=numpy.zeros(len(samples[0]))
  111. msg='[{}] mean={:.2g} std={:.2g} cv={:.2f}'
  112. for i in range(len(cv)):
  113. convSample+=samples[i]
  114. if dbg:
  115. print(msg.format(i,mean[i],std[i],std[i]/mean[i]))
  116. convSample+=muTarget-A
  117. convM=numpy.mean(convSample)
  118. convS=numpy.std(convSample)
  119. if dbg:
  120. code="x"
  121. print(msg.format(code,convM,convS,convS/convM))
  122. h,be=numpy.histogram(convSample,bins=getEdges(fx))
  123. #h should be len(fx)
  124. return h/numpy.sum(h)