Esempio n. 1
0
 def __init_cache(self):
     """ensure model has a mystic.cache"""
     model = self.__model__
     mvl = getattr(
         self, 'ny', getattr(model, 'ny', getattr(
             model, '__axis__', None))) is not None  # True if multivalued
     name = getattr(model, '__name__', None)  #XXX: do better?
     if not hasattr(model, '__cache__') or not hasattr(
             model, '__inverse__'):
         import mystic.cache as mc
         model = mc.cached(archive=name, multivalued=mvl)(model)
     self.__model__ = model
     if name is not None:
         self.__model__.__name__ = name
     return
Esempio n. 2
0
    def __call__(self, axis=None, **kwds):
        """apply the reducer to the sampled statistical quantity

    Input:
        axis: int, the index of y on which to find bound (all, by default)

    Additional Input:
        reducer: function to reduce a list to a single value (e.g. mean, max)
        dist: a mystic.tools.Distribution instance (or list of Distributions)
        npts: number of sample points [default = 10000]
        clip: if True, clip at bounds, else resample [default = False]

    Returns:
        sampled statistical quantity, for the specified axis, reduced to a float
        """
        #XXX: return what? "energy and solution?" reduced?
        reducer = kwds.pop('reducer', None)
        if kwds.get('npts', None) is None: kwds.pop('npts', None)
        s = random_samples(self.lb, self.ub, **kwds).T
        fobj = cached(archive=dict_archive())(self.objective)  #XXX: bad idea?
        self._pts = fobj.__cache__(
        )  #XXX: also bad idea? include from *_bounds?
        objective = lambda rv: fobj(self.constraint(rv), axis=axis)
        import multiprocess.dummy as mp  #FIXME: process pickle/recursion Error
        pool = mp.Pool()  # len(s)
        map = pool.map  #TODO: don't hardwire map
        s = map(objective, s)  #NOTE: s = [(...),(...)] or [...]
        pool.close()
        pool.join()
        if axis is None and self.axes is not None:  # apply per axis
            if reducer is None:
                return tuple(sum(si) / len(si) for si in zip(*s))
            #XXX: better tuple(reducer(s, axis=0).tolist()) if numpy ufunc?
            return tuple(reducer(si) for si in zip(*s))
        s = tuple(s)
        return sum(s) / len(s) if reducer is None else reducer(s)
Esempio n. 3
0
#!/usr/bin/env python
#
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
# Copyright (c) 2020-2022 The Uncertainty Quantification Foundation.
# License: 3-clause BSD.  The full license text is available at:
#  - https://github.com/uqfoundation/mystic/blob/master/LICENSE

import mystic.cache as mc
import mystic.models as mm

# basic interaction with an archive
d = mc.archive.read('test', type=mc.archive.dict_archive)
assert len(d) == 0

mc.archive.write(d, dict(a=1, b=2, c=3))
assert len(d) == 3

# basic pattern to cache an objective
d = mc.archive.read('rosen', type=mc.archive.dict_archive)
model = mc.cached(archive=d)(mm.rosen)
model([1, 2, 1])
model([1, 1, 1])
c = model.__cache__()
assert len(c) == 2

model.__inverse__([1, 2, 3]) == -model([1, 2, 3])
assert len(c) == 3
Esempio n. 4
0
def sample(model, bounds, pts=None, **kwds):
    """sample model within bounds, writing to an archive and returning data

    Inputs:
        model: a cached model function, of form y = model(x, axis=None)
        bounds: list of tuples of (lower,upper), bounds on each 'x'
        pts: int, number of points sampled by the sampler

    Additional Inputs:
        sampler: the mystic.sampler type [default: LatticeSampler]
        solver: the mystic.solver type [default: NelderMeadSimplexSolver]
        dist: a distribution type (or float amplitude) [default: None]
        map: a map instance [default: builtins.map]
        ny: int, number of model outputs, len(y) [default: None]
        axis: int, index of output on which to search [default: None]

    Returns:
        the mystic.math.legacydata.dataset of sampled data

    NOTE:
        additional keywords (evalmon, stepmon, maxiter, maxfun,
        saveiter, state, termination, constraints, penalty, reducer)
        are available for use. See mystic.ensemble for more details.

    NOTE:
        dist can be used to add randomness to the sampler, and can
        accept a numpy distribution type such as numpy.random.normal,
        or a mystic distribution type built with mystic.math.Distribution.
        if dist=N, where N is an int or float, use normalized Gaussian noise,
        mystic.math.Distribution(numpy.random.normal, 0, sigma), where
        sigma is N * sum(bound) for each bound in the bounds, and N scales
        the amplitude of the noise (typically, N ~ 0.05).

    NOTE:
        if pts is negative (i.e. pts=-4), use solver-directed sampling.
        initial points are chosen by the sampler, then solvers run
        until converged. a LatticeSampler also accepts a list of pts,
        indicating the number of bins on each axis; if the product of
        npts is negative, then use solver-directed sampling.

    NOTE:
        given the model is cached, a klepto.dir_archive is created by default
    """
    from mystic.samplers import LatticeSampler
    searcher = kwds.pop('sampler', LatticeSampler)
    ax = getattr(model, '__axis__', None)
    axis = None if hasattr(ax, '__len__') else ax  # get default for axis
    axis = kwds.pop('axis', axis)  # allow override?
    ny = kwds.pop('ny', getattr(model, 'ny', None))  #XXX: best?
    mvl = ny is not None  # True if multivalued
    axis = axis if mvl else None  #XXX: allow multi-axis search?
    dist = kwds.pop('dist', None)
    if isinstance(dist, (int, float)):  # noise N(0, sig); sig = dist*(ub+lb)
        import numpy as np
        from mystic.math import Distribution
        sig = [dist * (ub + lb)
               for (lb, ub) in bounds]  #FIXME: allow None and inf
        dist = Distribution(np.random.normal, 0, sig)
    map_ = kwds.pop('map', map)
    if not hasattr(model, '__cache__') or not hasattr(model, '__inverse__'):
        import mystic.cache as mc
        name = getattr(model, '__name__', None)  #XXX: do better?
        model = mc.cached(archive=name, multivalued=mvl)(model)
    cache = model.__cache__
    imodel = model.__inverse__
    if hasattr(pts, '__len__'):
        import numpy as np
        pts, _pts = np.prod(pts), [abs(i) for i in pts]
    else:
        _pts = None
    if pts is None: pts = -1
    if pts == 0:  # don't sample, just grab the archive
        pass
    elif pts > 0:  # sample pts without optimizing
        pts = pts if _pts is None else _pts

        def doit(axis=None):
            _model = _modelaxis(model, axis)
            s = searcher(bounds, _model, npts=pts, dist=dist, **kwds)
            s.sample()
            return s

        if mvl and axis is None:
            # as we don't optimize, we really don't need axis...?
            doit(axis=0)
        else:
            doit(axis)
    else:  # search for minima until terminated
        pts = -pts if _pts is None else _pts

        def lower(axis=None):
            _model = _modelaxis(model, axis)
            s = searcher(bounds, _model, npts=pts, dist=dist, **kwds)
            s.sample_until(terminated=all)
            return s

        def upper(axis=None):
            model_ = _modelaxis(imodel, axis)
            si = searcher(bounds, model_, npts=pts, dist=dist, **kwds)
            si.sample_until(terminated=all)
            return si

        def _apply(f, arg):
            return f(arg)

        fs = lower, upper

        def doit(axis=None):
            return list(map_(_apply, fs, [axis] * len(fs)))

        if mvl and axis is None:
            if ny:
                import multiprocess.dummy as mt
                pool = mt.Pool()
                tmap = pool.map
                list(tmap(doit, range(ny)))
                pool.close()
                pool.join()
            else:  #XXX: default to 0, warn, or error?
                doit(axis=0)
        else:
            doit(axis)
    import dataset as ds
    return ds.from_archive(cache(), axis=None)
Esempio n. 5
0
def sample(model, bounds, pts=None, **kwds):
    """sample model within bounds, writing to an archive and returning data

    Inputs:
        model: a cached model function, of form y = model(x, axis=None)
        bounds: list of tuples of (lower,upper), bounds on each 'x'
        pts: int, number of points sampled by the sampler

    Additional Inputs:
        sampler: the mystic.sampler type [default: LatticeSampler]
        solver: the mystic.solver type [default: NelderMeadSimplexSolver]
        dist: a distribution type [default: numpy.random.normal]
        map: a map instance [default: builtins.map]
        ny: int, number of model outputs, len(y) [default: None]
        axis: int, index of output on which to search [default: None]

    Returns:
        the mystic.math.legacydata.dataset of sampled data

    NOTE:
        given the model is cached, a klepto.dir_archive is created by default

    NOTE:
        if pts is negative (i.e. pts=-4), use solver-directed sampling where
        initial points chosen by the sampler, then solvers run until converged

    NOTE:
        dist can be used to add randomness to the sampler
    """
    from mystic.samplers import LatticeSampler
    searcher = kwds.get('sampler', LatticeSampler)
    from mystic.solvers import NelderMeadSimplexSolver
    solver = kwds.get('solver', NelderMeadSimplexSolver)
    ax = getattr(model, '__axis__', None)
    axis = None if hasattr(ax, '__len__') else ax  # get default for axis
    axis = kwds.get('axis', axis)  # allow override?
    ny = kwds.get('ny', getattr(model, 'ny', None))  #XXX: best?
    mvl = ny is not None  # True if multivalued
    axis = axis if mvl else None  #XXX: allow multi-axis search?
    import numpy as np
    dist = kwds.get('dist', np.random.normal)
    map_ = kwds.get('map', map)
    if not hasattr(model, '__cache__') or not hasattr(model, '__inverse__'):
        import mystic.cache as mc
        name = getattr(model, '__name__', None)  #XXX: do better?
        model = mc.cached(archive=name, multivalued=mvl)(model)
    cache = model.__cache__
    imodel = model.__inverse__
    if hasattr(pts, '__len__'):
        pts, _pts = -np.prod(pts), pts
    else:
        _pts = None
    if pts is None: pts = -1
    if pts == 0:  # don't sample, just grab the archive
        pass
    elif pts > 0:  # sample pts without optimizing

        def doit(axis=None):
            _model = lambda x: model(x, axis=axis)
            s = searcher(bounds, _model, npts=pts, solver=solver, dist=dist)
            s.sample()
            return s

        if mvl and axis is None:
            # as we don't optimize, we really don't need axis...?
            doit(axis=0)
        else:
            doit(axis)
    else:  # search for minima until terminated
        pts = -pts if _pts is None else _pts

        def lower(axis=None):
            _model = lambda x: model(x, axis=axis)
            s = searcher(bounds, _model, npts=pts, solver=solver, dist=dist)
            s.sample_until(terminated=all)
            return s

        def upper(axis=None):
            model_ = lambda x: imodel(x, axis=axis)
            si = searcher(bounds, model_, npts=pts, solver=solver, dist=dist)
            si.sample_until(terminated=all)
            return si

        def _apply(f, arg):
            return f(arg)

        fs = lower, upper

        def doit(axis=None):
            return list(map_(_apply, fs, [axis] * len(fs)))

        if mvl and axis is None:
            if ny:
                import multiprocess.dummy as mt
                pool = mt.Pool()
                tmap = pool.map
                list(tmap(doit, range(ny)))
                pool.close()
                pool.join()
            else:  #XXX: default to 0, warn, or error?
                doit(axis=0)
        else:
            doit(axis)
    import dataset as ds
    return ds.from_archive(cache(), axis=None)