| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 | import numpyimport scipy.optimizeimport functoolsimport fitModelclass initialValueGenerator:      def __init__(self):      self.map={'gaus':initialValueGenerator.drawGauss,      'loggaus':initialValueGenerator.drawLogGauss,      'poisson':initialValueGenerator.drawPoisson,      'flat':initialValueGenerator.drawFlat,      'const':initialValueGenerator.drawConst}   def add(self,gtype='gaus',parameters=[0,1],bounds=[-numpy.inf,numpy.inf]):      v={'type':gtype,'parameters':parameters,'bounds':bounds}      try:         self.vals.append(v)      except AttributeError:         self.vals=[v]   def getN(self):      return len(self.vals)      def drawGauss(p):      return numpy.random.normal(p[0],p[1])   def drawLogGauss(p):      return numpy.power(10,initialValueGenerator.drawGauss(p))   def drawPoisson(p):      return numpy.random.poisson(p[0])         def drawFlat(p):      return p[0]+numpy.random.random()*(p[1]-p[0])   def drawConst(p):      return p[0]   def draw(self,typ,p,lb,ub):      f=self.map[typ]      while True:         x=f(p)         if x<lb:            continue         if x>ub:            continue         return x       def generate(self):      n=len(self.vals)      par=numpy.zeros(n)      lb=numpy.zeros(n)      ub=numpy.zeros(n)      for i in range(n):         x=self.vals[i]         p=x['parameters']         lb[i]=x['bounds'][0]         ub[i]=x['bounds'][1]         par[i]=self.draw(x['type'],p,lb[i],ub[i])      return par, (lb,ub)   def getBoundsScalar(self):      gbounds=[]      for x in self.vals:         gbounds.append(x['bounds'])      return gboundsdef generateIVF():    ig=initialValueGenerator()    ig.add('poisson',[200],[0,numpy.inf])    ig.add('loggaus',[1,0.5],[0,numpy.inf])    ig.add('loggaus',[-1,1],[0,numpy.inf])    ig.add('const',[6,1],[0,numpy.inf])    ig.add('poisson',[200],[0,numpy.inf])    ig.add('loggaus',[-2,1],[0,0.1])    return igdef generateIVFFinite():    ig=initialValueGenerator()    #A    ig.add('poisson',[200],[0,10000])    #tau    ig.add('loggaus',[1,0.5],[0,40])    #alpha    ig.add('loggaus',[-1,1],[0,5])    #dt    ig.add('const',[6,1],[6,50])    #B    ig.add('poisson',[200],[0,10000])    #gamma    ig.add('loggaus',[-2,1],[0,0.1])    return igdef generateCModel():    #generate approx candidate    ig=initialValueGenerator()    ig.add('loggaus',[-3,1],[0,0.1])    ig.add('flat',[0,1],[0,1])    ig.add('loggaus',[-7,2],[0,0.1])    ig.add('gaus',[10,5],[0,30])    return igdef generateCModelFinite():    #generate approx candidate    ig=initialValueGenerator()    #k1    ig.add('loggaus',[-3,1],[0,0.1])    #BVF    ig.add('flat',[0,1],[0,1])    #k2    ig.add('loggaus',[-7,2],[0,0.1])    #dt    ig.add('gaus',[10,2],[5,50])    return igdef generateGauss(fit,bounds,n=1):#generate n realizations of a gaussian multivariate distribution with average (vector) mu#and (co)variance (matrix) cov#output is a (r,n) matrix where r is size of vector mu and n are realizations   sig=scipy.linalg.sqrtm(fit.cov)   r=fit.mu.shape[0]   out=numpy.ones((r,n))   i=0   while True:      s=numpy.matmul(sig,numpy.random.normal(size=r))      s+=fit.mu      if in_bounds(s,bounds[0],bounds[1]):         out[:,i]=s         i+=1      if i==n:         break   return out   #return numpy.outer(fit.mu,numpy.ones((1,n)))+numpy.matmul(sig,s)  def in_bounds(x0,lb,ub):   r=x0.shape[0]   for i in range(r):      if x0[i]<lb[i]:         return False      if x0[i]>ub[i]:         return False   return Truedef getFit(samples,threshold=numpy.inf,verbose=0):    class fit:pass    chi2=samples[0,:]    #mScore=numpy.min(chi2)        fit.mu=numpy.mean(samples[1:,chi2<threshold],axis=1)    fit.cov=numpy.cov(samples[1:,chi2<threshold])    if verbose>0:        print(fit.mu)        print(fit.cov)    return fit#fit input functiondef fitIVF(t, centers,nfit=10,nbatch=20):   #t,dt=loadData.loadTime(r,xsetup)   #centers=loadCenters(r,xsetup)   #find center with maximum content/uptake    m=numpy.unravel_index(numpy.argmax(centers),centers.shape)[0]   #treat it as ivf sample   ivf=centers[m]   #create a partial specialization to be used in optimization   w=numpy.ones(t.shape)   fun=functools.partial(fitModel.fDiff,fitModel.fIVF,t,ivf,w)     jac=functools.partial(fitModel.jacIVF,t)      #generate approx candidate   ig=generateIVF()   n=ig.getN()   samples=numpy.zeros((n+1,nfit))   for j in range(nfit):      fMin=1e30      for i in range(nbatch):         par,bounds=ig.generate()         result=scipy.optimize.least_squares(fun=fun,x0=par,bounds=bounds,jac=jac)         #result=scipy.optimize.least_squares(fun=fun,x0=par,bounds=bounds)         #fit is invariant on 1/p[1]==p[2], so          xm=[x for x in result.x]         xm[1]=numpy.max([result.x[1],1/result.x[2]])         xm[2]=numpy.max([1/result.x[1],result.x[2]])         if result.cost<fMin:            fMin=result.cost            x1=xm      samples[1:,j]=x1      samples[0,j]=fMin      return m,samples#global version with annealingdef fitIVFGlobal(t, centers,nfit=10, qLambda=0):    #find center with maximmum point    m=numpy.unravel_index(numpy.argmax(centers),centers.shape)[0]    #treat it as ivf sample    ivf=centers[m]    #this is the (relative) weight of each time point in fit    w=numpy.ones(t.shape)    #create a partial specialization to be used in optimizations    #funcion, a difference between model prediction fIVF and measured values ivf at points t, using point weights w    funScalar=functools.partial(fitModel.fDiffScalar,fitModel.fIVF,t,ivf,w)      #add regulizer on A    funScalarRegularized=functools.partial(fitModel.fDiffScalarRegularized,funScalar,fitModel.fIVFRegA,qLambda)    #Jacobi    jac=functools.partial(fitModel.jacIVF,t)    #convert it to scalar for global minimization    jacScalar=functools.partial(fitModel.jacScalar,fitModel.fIVF,t,ivf,w,jac)    #add regularization on A    jacScalarRegularized=functools.partial(fitModel.jacScalarRegularized,jacScalar,fitModel.jacIVFRegA,qLambda)    ig=generateIVFFinite()    boundsScalar=ig.getBoundsScalar()    #par,bounds=ig.generate()    n=ig.getN()    samples=numpy.zeros((n+1,nfit))        minSetup=dict(method='L-BFGS-B',jac=jacScalar)    if qLambda>0:       minSetup['jac']=jacScalarRegularized    for j in range(nfit):        if qLambda>0:           result=scipy.optimize.dual_annealing(func=funScalarRegularized,bounds=boundsScalar,minimizer_kwargs=minSetup)        else:           result=scipy.optimize.dual_annealing(func=funScalar,bounds=boundsScalar,minimizer_kwargs=minSetup)        xm=[qx for qx in result.x]        xm[1]=numpy.max([result.x[1],1/result.x[2]])        xm[2]=numpy.max([1/result.x[1],result.x[2]])        if result.x[2]!=xm[2]:           pass            #print(f'[{j}] switch')        samples[1:,j]=xm        samples[0,j]=result.fun        #print(f'[{j}] {result.fun}')    return m,samples#fit compartmentdef fitCompartment(ivfFit, t, qf ,nfit=10,nbatch=20, useJac=False):      igIVF=generateIVF()   pars,boundsIVF=igIVF.generate()   ivfSamples=generateGauss(ivfFit,boundsIVF,nfit)   ig=generateCModel()   nC=ig.getN()   boundsScalar=ig.getBoundsScalar()   samples=numpy.zeros((1+nC+nIVF,nfit))   for j in range(nfit):      #create a partial specialization to be used in optimization      ivfPar=ivfSamples[:,j]      w=numpy.ones(t.shape)      fc1=functools.partial(fitModel.fCompartment,ivfPar)      fun=functools.partial(fitModel.fDiff,fc1,t,qf,w)        jac=functools.partial(fitModel.jacDep,ivfPar,t)      #minimize requires scalar function      funScalar=functools.partial(fitData.fDiffScalar,fc1,t,qf,w)        jacScalar=functools.partial(fitData.jacScalar,fc1,t,qf,w,jac)         fMin=1e30      for i in range(nbatch):         #generate approx candidate         par,bounds=ig.generate()                  if useJac:            result=scipy.optimize.least_squares(fun=fun,x0=par,bounds=bounds,jac=jac)            #scalar, just for reference            #result=scipy.optimize.minimize(fun=funScalar,x0=par,bounds=boundsScalar,            #                                           jac=jacScalar,method='L-BFGS-B')         else:            result=scipy.optimize.least_squares(fun=fun,x0=par,bounds=bounds)         if result.cost<fMin:            fMin=result.cost            x1=result.x            samples[0,j]=fMin      samples[1:nC+1,j]=x1      samples[(1+nC):,j]=ivfPar              return samplesdef fitCompartmentGlobal(ivfFit, t, qf ,nfit=10,nbatch=20, useJac=False,qLambda=0):    #nclass=setup['nclass'][0]    #t,dt=loadData.loadTime(r,setup)    #centers=loadData.loadCenters(r,setup,ir)    #qf=centers[m]    igIVF=generateIVF()    xs,boundsIVF=igIVF.generate()    ivfSamples=generateGauss(ivfFit,boundsIVF,nfit)    #samples=numpy.zeros((4+1,nfit))    nIVF=igIVF.getN()    ig=generateCModelFinite()    nC=ig.getN()    samples=numpy.zeros((1+nC+nIVF,nfit))       boundsScalar=ig.getBoundsScalar()    #optimize has a hard time dealing with small values, so scale the target up and later scale the parameters (approximately correct)    scale=1    fi=qf.sum()    if fi<0.1:        scale=10/fi        print('scale={}'.format(scale))    #print(qf.sum()*scale)    qf*=scale    for j in range(nfit):        ivfPar=ivfSamples[:,j]        w=numpy.ones(t.shape)        fc1=functools.partial(fitModel.fCompartment,ivfPar)        funScalar=functools.partial(fitModel.fDiffScalar,fc1,t,qf,w)          funScalarRegularized=functools.partial(fitModel.fDiffScalarRegularized,funScalar,fitModel.fCRegBVF,qLambda)        jac=functools.partial(fitModel.jacDep,ivfPar,t)        jacScalar=functools.partial(fitModel.jacScalar,fc1,t,qf,w,jac)        jacScalarRegularized=functools.partial(fitModel.jacScalarRegularized,jacScalar,fitModel.jacDepRegBVF,qLambda)        #minSetup=dict(method='L-BFGS-B',jac=jacScalar)        minSetup=dict(method='L-BFGS-B',jac=jacScalarRegularized)                result=scipy.optimize.dual_annealing(func=funScalarRegularized,bounds=boundsScalar,minimizer_kwargs=minSetup)        print(f'[{j}/{nfit}]')                samples[0,j]=result.fun/scale        qx=result.x        qx[0]/=scale        qx[1]/=scale        print('{} {} {}'.format(scale,result.fun/scale,qx))        samples[1:nC+1,j]=qx        samples[(1+nC):,j]=ivfPar                return samples
 |