예제 #1
0
    def __init__(self, orig=None, description='unknown'):
        if orig is None:
            pyobs.check_type(description, 'text', str)
            self.description = description
            self.www = [
                pwd.getpwuid(os.getuid())[0],
                os.uname()[1],
                datetime.datetime.now().strftime('%c')
            ]
            self.shape = []
            self.size = 0
            self.mean = []
            self.ename = []
            self.delta = {}
            self.cdata = {}
        else:
            if isinstance(orig, observable):
                self.description = orig.description
                self.www = orig.www
                self.shape = orig.shape
                self.size = numpy.prod(self.shape)
                self.mean = numpy.array(orig.mean)  # or orig.mean.copy()

                self.ename = [e for e in orig.ename]
                self.delta = {}
                for key in orig.delta:
                    self.delta[key] = orig.delta[key].copy()

                self.cdata = {}
                for key in orig.cdata:
                    self.cdata[key] = cdata(orig.cdata[key].cov)
                pyobs.memory.add(self)
            else:
                raise pyobs.PyobsError('Unexpected orig argument')
        pyobs.memory.add(self)
예제 #2
0
 def add_syst_err(self, name, err):
     """
     Add a systematic error to the observable
     
     Parameters:
        name (str): label that uniquely identifies the syst. error
        err (array): array with the same dimensions of the observable
           with the systematic error
     
     Examples:
        >>> data = [0.198638, 0.403983, 1.215960, 1.607684, 0.199049, ... ]
        >>> vec = pyobs.observable(description='vector')
        >>> vec.create('A',data,shape=(4,))
        >>> print(vec)
        0.201(13)    0.399(26)    1.199(24)    1.603(47)
        >>> vec.add_syst_err('syst.err',[0.05,0.05,0,0])
        >>> print(vec)
        0.201(52)    0.399(56)    1.199(24)    1.603(47)
        
     """
     pyobs.check_type(name, 'name', str)
     if name in self.cdata:
         raise pyobs.PyobsError(f'Label {name} already used')
     if numpy.shape(err) != self.shape:
         raise pyobs.PyobsError(
             f'Unexpected error, dimensions do not match {self.shape}')
     cov = numpy.reshape(numpy.array(err)**2, (self.size, ))
     self.cdata[name] = cdata(cov)
     pyobs.memory.update(self)
예제 #3
0
 def create_from_cov(self, cname, value, covariance):
     """
     Create observables based on covariance matrices
     
     Parameters:
        cname (str): label that uniquely identifies the data set
        value (array): central value of the observable; only 1-D arrays are supported
        covariance (array): a 2-D covariance matrix; if `covariance` is a 1-D array of
           same length as `value`, a diagonal covariance matrix is assumed.
     
     Examples:
        >>> mpi = pyobs.observable(description='pion masses, charged and neutral')
        >>> mpi.create_cd('mpi-pdg18',[139.57061,134.9770],[0.00023**2,0.0005**2])
        >>> print(mpi)
        139.57061(23)    134.97700(50)
     """
     if isinstance(value, (int, float, numpy.float64, numpy.float32)):
         self.mean = numpy.reshape(value, (1, ))
         cov = numpy.reshape(covariance, (1, ))
     else:
         self.mean = numpy.array(value)
         cov = numpy.array(covariance)
     self.shape = numpy.shape(self.mean)
     if numpy.ndim(self.shape) != 1:
         raise pyobs.PyobsError(
             f'Unexpected value, only 1-D arrays are supported')
     self.size = numpy.prod(self.shape)
     if cov.shape != (self.size, ) and cov.shape != (self.size, self.size):
         raise pyobs.PyobsError(
             f'Unexpected shape for covariance {cov.shape}')
     pyobs.check_type(cname, 'cname', str)
     self.cdata[cname] = cdata(cov)
     pyobs.memory.update(self)
예제 #4
0
    def __call__(self, yobs, p0=None, min_search=None):
        if len(self.csq) > 1:
            pyobs.check_type(yobs, 'yobs', list)
        else:
            if isinstance(yobs, pyobs.observable):
                yobs = [yobs]
        if len(yobs) != len(self.csq):
            raise pyobs.PyobsError(
                f'Unexpected number of observables for {len(self.csq)} fits')
        if p0 is None:
            p0 = [1.0] * len(self.pdict)
        if min_search is None:
            min_search = lm

        def csq(p0):
            res = 0.0
            for i in range(len(yobs)):
                self.csq[i].set_pars(self.pdict, p0)
                res += self.csq[i](yobs[i].mean)
            return res

        dcsq = lambda x: sum([
            self.csq[i].grad(yobs[i].mean, self.pdict)
            for i in range(len(yobs))
        ])
        ddcsq = lambda x: sum([
            self.csq[i].hess(yobs[i].mean, self.pdict)
            for i in range(len(yobs))
        ])

        t0 = time()
        res = min_search(csq, p0, jac=dcsq, hess=ddcsq)

        # properly create gradients
        H = self.csq[0].Hmat(self.pdict, res.x)
        for i in range(1, len(yobs)):
            H += self.csq[i].Hmat(self.pdict, res.x)
        Hinv = numpy.linalg.inv(H)

        g = []
        for i in range(len(yobs)):
            tmp = self.csq[i].gvec(self.pdict, res.x)
            g.append(pyobs.gradient(Hinv @ tmp))

        if pyobs.is_verbose('mfit.run') or pyobs.is_verbose('mfit'):
            print(f'chisquare = {res.fun}')
            print(f'minimizer iterations = {res.nit}')
            print(f'minimizer status: {res.message}')
            print(f'mfit.run executed in {time()-t0:g} secs')
        return pyobs.derobs(yobs, res.x, g)
예제 #5
0
def error_bias4(x, f):
    pyobs.check_type(x, 'x', pyobs.observable)
    [x0, dx0] = x.error()
    bias4 = numpy.zeros((x.size, ))
    hess = num_hess(x0, f)

    for key in x.delta:
        oid = numpy.array(x.delta[key].mask)
        idx = numpy.ix_(oid, oid)
        d2 = numpy.einsum('abc,bj,cj->aj', hess[:, idx[0], idx[1]],
                          x.delta[key].delta, x.delta[key].delta)
        dd2 = numpy.sum(d2, axis=1)
        bias4 += dd2**2 / x.delta[key].n**4

    return numpy.sqrt(bias4)
예제 #6
0
    def eval(self, xax, pars):
        """
        Evaluates the function on a list of coordinates using the parameters
        obtained from a :math:`\chi^2` minimization.
        
        Parameters:
           xax (array,list of arrays) : the coordinates :math:`x_i^\mu` where 
              the function must be evaluated. For combined fits, a list of 
              arrays must be passed, one for each fit.
           pars (obs) : the observable returned by calling this class
        
        Returns:
           list of obs : observables corresponding to the functions evaluated 
           at the coordinates `xax`.
        
        Examples:
           >>> fit1 = mfit(xax,W,f,df)
           >>> pars = fit1(yobs1)
           >>> print(pars)
           0.925(35)    2.050(19)
           >>> xax = numpy.arange(0,10,0.2)
           >>> yeval = fit1.eval(xax, pars)
        """

        if not type(xax) is list:
            xax = [xax]
        pyobs.check_type(pars, 'pars', pyobs.observable)
        N = len(xax)
        if N != len(self.csq):
            raise pyobs.PyobsError(
                f'Coordinates and Paramters do not match number of internal functions'
            )
        out = []
        for ic in self.csq:
            [m, g] = self.csq[ic].eval(xax[ic], self.pdict, pars.mean)
            out.append(pyobs.derobs([pars], m, [pyobs.gradient(g)]))
        return out
예제 #7
0
def derobs(inps, mean, grads, description=None):
    t0 = time()
    pyobs.check_type(inps, 'inps', list)
    pyobs.check_type(mean, 'mean', numpy.ndarray, int, float, numpy.float32,
                     numpy.float64)
    pyobs.check_type(grads, 'grads', list)
    if len(inps) != len(grads):
        raise pyobs.PyobsError('Incompatible inps and grads')
    if description is None:
        description = ', '.join(set([i.description for i in inps]))
    res = pyobs.observable(description=description)
    res.set_mean(mean)

    allkeys = []
    for i in inps:
        for dn in i.delta:
            if not dn in allkeys:
                allkeys.append(dn)

    for key in allkeys:
        new_idx = []
        new_mask = []
        lat = None
        for i in range(len(inps)):
            if key in inps[i].delta:
                data = inps[i].delta[key]
                h = grads[i].get_mask(data.mask)
                if not h is None:
                    new_mask += h
                    if not new_idx:
                        new_idx = data.idx
                    else:
                        new_idx = merge_idx(new_idx, data.idx)
                    if lat is None:
                        lat = data.lat
                    else:
                        if numpy.any(lat != data.lat):  # pragma: no cover
                            raise pyobs.PyobsError(
                                f'Unexpected lattice size for master fields with same tag'
                            )
        if len(new_mask) > 0:
            res.delta[key] = delta(list(set(new_mask)), new_idx, lat=lat)
            for i in range(len(inps)):
                if key in inps[i].delta:
                    res.delta[key].axpy(grads[i], inps[i].delta[key])

    res.ename = []
    for key in res.delta:
        name = key.split(':')[0]
        if not name in res.ename:
            res.ename.append(name)

    res.cdata = {}
    allkeys = []
    for i in inps:
        for cd in i.cdata:
            if not cd in allkeys:
                allkeys.append(cd)
    for key in allkeys:
        for i in range(len(inps)):
            if key in inps[i].cdata:
                if not key in res.cdata:
                    res.cdata[key] = cdata(numpy.zeros(res.size))
                res.cdata[key].axpy(grads[i], inps[i].cdata[key])

    pyobs.memory.update(res)
    if pyobs.is_verbose('derobs'):
        print(f'derobs executed in {time()-t0:g} secs')
    return res
예제 #8
0
    def create(self,
               ename,
               data,
               icnfg=None,
               rname=None,
               shape=(1, ),
               lat=None):
        """
        Create an observable
        
        Parameters:
           ename (str): label of the ensemble
           data (array, list of arrays): the data generated from a single 
              or multiple replica
           icnfg (array of ints or list of arrays of ints, optional): indices 
              of the configurations corresponding to data; if not passed the 
              measurements are assumed to be contiguous
           rname (str or list of str, optional): identifier of the replica; if 
              not passed integers from 0 are automatically assigned
           shape (tuple, optional): shape of the observable, data must be passed accordingly
           lat (list of ints, optional): the size of each dimension of the master-field;
              if passed data is assumed to be obtained from observables measured at different
              sites and `icnfg` is re-interpreted as the index labeling the sites; if `icnfg`
              is not passed data is assumed to be contiguous on all sites.
              
        Note:
           For data and icnfg array can mean either a list or a 1-D numpy.array.
           If the observable has already been created, calling create again will add
           a new replica to the same ensemble.
           
        Examples:
           >>> data = [0.43, 0.42, ... ] # a scalar observable
           >>> a = pyobs.observable(description='test')
           >>> a.create('EnsembleA',data)

           >>> data0 = [0.43,0.42, ... ] # replica 0
           >>> data1 = [0.40,0.41, ... ] # replica 1
           >>> a = pyobs.observable(description='test')
           >>> a.create('EnsembleA',[data0,data1],rname=['r0','r1'])

           >>> data = [0.43, 0.42, 0.44, ... ]
           >>> icnfg= [  10,   11,   13, ... ]
           >>> a = pyobs.observable(description='test')
           >>> a.create('EnsembleA',data,icnfg=icnfg)

           >>> data = [1.0, 2.0, 3.0, 4.0, ... ]
           >>> a = pyobs.observable(description='matrix')
           >>> a.create('EnsembleA',data,shape=(2,2))
       
        Examples:
           >>> data = [0.43, 0.42, 0.44, ... ]
           >>> lat = [64,32,32,32]
           >>> a = pyobs.observable(description='test-mf')
           >>> a.create('EnsembleA',data,lat=lat)
           
           >>> data = [0.43, 0.42, 0.44, ... ]
           >>> idx = [0, 2, 4, 6, ...] # measurements on all even points of time-slice
           >>> lat = [32, 32, 32]
           >>> a = pyobs.observable(description='test-mf')
           >>> a.create('EnsembleA',data,lat=lat,icnfg=idx)           
        """
        t0 = time()
        pyobs.check_type(ename, 'ename', str)
        if ':' in ename:
            raise pyobs.PyobsError(
                f'Column symbol not allowed in ename {ename}')
        pyobs.check_type(shape, 'shape', tuple)
        self.shape = shape
        self.size = numpy.prod(shape)
        mask = range(self.size)
        if not ename in self.ename:
            self.ename.append(ename)

        if isinstance(data[0], (list, numpy.ndarray)):
            R = len(data)
        elif isinstance(data[0], (int, float, numpy.float64, numpy.float32)):
            R = 1
        else:
            raise pyobs.PyobsError(f'Unexpected data type')

        if R == 1:
            pyobs.check_type(data, f'data', list, numpy.ndarray)
            nc = int(len(data) / self.size)
            if rname is None:
                rname = 0
            else:
                pyobs.check_not_type(rname, 'rname', list)
            if icnfg is None:
                icnfg = range(nc)
            else:
                pyobs.check_type(icnfg, 'icnfg', list, range)
                pyobs.check_type(icnfg[0], 'icnfg[:]', int, numpy.int32,
                                 numpy.int64)
                if len(icnfg) * self.size != len(data):
                    raise pyobs.PyobsError(
                        f'Incompatible icnfg and data, for shape={shape}')
            if numpy.size(self.mean) != 0:
                N0 = sum([self.delta[key].n for key in self.delta])
                mean0 = numpy.reshape(self.mean, (self.size, ))
                mean1 = numpy.mean(numpy.reshape(data, (nc, self.size)), 0)
                self.mean = (N0 * mean0 + nc * mean1) / (N0 + nc)
                shift = nc * (mean0 - mean1) / (N0 + nc)
                for key in self.delta:
                    self.delta[key].delta += shift[:, None]
            else:
                self.mean = numpy.mean(numpy.reshape(data, (nc, self.size)), 0)

            key = f'{ename}:{rname}'
            self.delta[key] = delta(mask, icnfg, data, self.mean, lat)
        else:
            if numpy.size(self.mean) != 0:
                raise pyobs.PyobsError(
                    'Only a single replica can be added to existing observables'
                )
            for ir in range(R):
                pyobs.check_type(data[ir], f'data[{ir}]', list, numpy.ndarray)
            self.mean = numpy.zeros((self.size, ))
            nt = 0
            for dd in data:
                nc = int(len(dd) / self.size)
                self.mean += numpy.sum(numpy.reshape(dd, (nc, self.size)), 0)
                nt += nc
            self.mean *= 1.0 / float(nt)
            if rname is None:
                rname = range(R)
            else:
                pyobs.check_type(rname, 'rname', list)
                if len(rname) != R:
                    raise pyobs.PyobsError('Incompatible rname and data')
            if not icnfg is None:
                pyobs.check_type(icnfg, 'icnfg', list)

            if icnfg is None:
                icnfg = []
                for ir in range(len(data)):
                    nc = int(len(data[ir]) / self.size)
                    icnfg.append(range(nc))
            else:
                for ir in range(len(data)):
                    if len(icnfg[ir]) * self.size != len(data[ir]):
                        raise pyobs.PyobsError(
                            f'Incompatible icnfg[{ir}] and data[{ir}], for shape={shape}'
                        )
            for ir in range(len(data)):
                key = f'{ename}:{rname[ir]}'
                self.delta[key] = delta(mask, icnfg[ir], data[ir], self.mean,
                                        lat)
        self.mean = numpy.reshape(self.mean, self.shape)
        pyobs.memory.update(self)
        if pyobs.is_verbose('create'):
            print(f'create executed in {time()-t0:g} secs')
예제 #9
0
def diff(f, x, dx):
    """
    Utility function to compute the gradient and hessian of a function using symbolic calculus
    
    Parameters:
       f (string): the reference function
       x (string): variables that are not differentiated; different variables
           must be separated by a comma
       dx (string): variables that are differentiated; different variables
           must be separated by a comma
    
    Returns:
       lambda : (scalar) function `f`
       lambda : (vector) function of the gradient of `f`
       lambda : (matrix) function of the hessian of `f`
    
    Notes: 
       The symbolic manipulation is based on the library `sympy` and the user 
       must follow the `sympy` syntax when passing the argument `f`. The analytic
       form of the gradient and hessian can be printed by activating the 'diff'
       verbose flag.
       
    Examples:
       >>> res = diff('a+b*x','x','a,b') # differentiate wrt to a and b
       a + b*x
       [1, x]
       [[0, 0], [0, 0]]
       >>> for i in range(3):
       >>>     print(res[i](0.4,1.,2.))
       1.8
       [1, 0.4]
       [[0, 0], [0, 0]]
    """
    pyobs.check_type(f, 'f', str)
    pyobs.check_type(x, 'x', str)
    pyobs.check_type(dx, 'dx', str)

    sym = {}
    for y in dx.rsplit(','):
        sym[y] = sympy.Symbol(y)

    expr = parse_expr(f, local_dict=sym)
    allvars = f'{x},{dx}'
    func = sympy.lambdify(allvars, expr, 'numpy')
    dexpr = []
    ddexpr = []
    for y in sym:
        dexpr.append(sympy.diff(expr, sym[y]))
        tmp = []
        for z in sym:
            tmp.append(sympy.diff(dexpr[-1], sym[z]))
        ddexpr.append(tmp)

    if pyobs.is_verbose('diff') or pyobs.is_verbose('symbolic.diff'):
        display(expr)
        display(dexpr)
        display(ddexpr)

    dfunc = sympy.lambdify(allvars, dexpr, 'numpy')
    ddfunc = sympy.lambdify(allvars, ddexpr, 'numpy')
    return [func, dfunc, ddfunc]