Beispiel #1
0
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate the cost function with bounds, penalties, monitors, etc"""
     #print("@%r %r %r" % (cost, ExtraArgs, max))
     evalmon = self._evalmon
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     self._fcalls, cost = wrap_function(cost, ExtraArgs, evalmon)
     if self._useStrictRange:
         if self.generations:
             #NOTE: pop[0] was best, may not be after resetting simplex
             for i, j in enumerate(self._setSimplexWithinRangeBoundary()):
                 self.population[i + 1] = self.population[0].copy()
                 self.population[i + 1][i] = j
         else:
             self.population[0] = self._clipGuessWithinRangeBoundary(
                 self.population[0])
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     cost = wrap_nested(cost, self._constraints)
     if self._reducer:
         #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
Beispiel #2
0
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     #print("@%r %r %r" % (cost, ExtraArgs, max))
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     from mystic.python_map import python_map
     if self._map != python_map:
         #FIXME: EvaluationMonitor fails for MPI, throws error for 'pp'
         from mystic.monitors import Null
         evalmon = Null()
     else:
         evalmon = self._evalmon
     fcalls, cost = wrap_function(cost, ExtraArgs, evalmon)
     if self._useStrictRange:
         indx = list(self.popEnergy).index(self.bestEnergy)
         ngen = self.generations  #XXX: no random if generations=0 ?
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(
                 self.population[i], (not ngen) or (i is indx))
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
         #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
Beispiel #3
0
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     #print ("@", cost, ExtraArgs, max)
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     from python_map import python_map
     if self._map != python_map:
         #FIXME: EvaluationMonitor fails for MPI, throws error for 'pp'
         from mystic.monitors import Null
         evalmon = Null()
     else: evalmon = self._evalmon
     fcalls, cost = wrap_function(cost, ExtraArgs, evalmon)
     if self._useStrictRange:
         indx = list(self.popEnergy).index(self.bestEnergy)
         ngen = self.generations #XXX: no random if generations=0 ?
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(self.population[i], (not ngen) or (i is indx))
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
Beispiel #4
0
    def _decorate_objective(self, cost, ExtraArgs=None):
        """decorate the cost function with bounds, penalties, monitors, etc

input::
    - cost is the objective function, of the form y = cost(x, *ExtraArgs),
      where x is a candidate solution, and ExtraArgs is the tuple of positional
      arguments required to evaluate the objective."""
        #print("@%r %r %r" % (cost, ExtraArgs, max))
        evalmon = self._evalmon
        raw = cost
        if ExtraArgs is None: ExtraArgs = ()
        self._fcalls, cost = wrap_function(cost, ExtraArgs, evalmon)
        if self._useStrictRange:
            indx = list(self.popEnergy).index(self.bestEnergy)
            ngen = self.generations  #XXX: no random if generations=0 ?
            for i in range(self.nPop):
                self.population[i] = self._clipGuessWithinRangeBoundary(
                    self.population[i], (not ngen) or (i == indx))
            cost = wrap_bounds(cost, self._strictMin,
                               self._strictMax)  #XXX: remove?
            from mystic.constraints import and_
            constraints = and_(self._constraints,
                               self._strictbounds,
                               onfail=self._strictbounds)
        else:
            constraints = self._constraints
        cost = wrap_penalty(cost, self._penalty)
        cost = wrap_nested(cost, constraints)
        if self._reducer:
            #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
            cost = reduced(self._reducer, arraylike=True)(cost)
        # hold on to the 'wrapped' and 'raw' cost function
        self._cost = (cost, raw, ExtraArgs)
        self._live = True
        return cost
Beispiel #5
0
 def _RegisterObjective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     if ExtraArgs == None: ExtraArgs = ()
     self._fcalls, cost = wrap_function(cost, ExtraArgs, self._evalmon)
     if self._useStrictRange:
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(self.population[i])
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' cost function
     self._cost = (cost, ExtraArgs)
     return cost
Beispiel #6
0
 def _RegisterObjective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     if ExtraArgs == None: ExtraArgs = ()
    #FIXME: EvaluationMonitor fails for MPI, throws error for 'pp'
     from python_map import python_map
     if self._map != python_map:
         self._fcalls = [0] #FIXME: temporary patch for removing the following line
     else:
         self._fcalls, cost = wrap_function(cost, ExtraArgs, self._evalmon)
     if self._useStrictRange:
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(self.population[i])
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' cost function
     self._cost = (cost, ExtraArgs)
     return cost
Beispiel #7
0
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     #print ("@", cost, ExtraArgs, max)
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     self._fcalls, cost = wrap_function(cost, ExtraArgs, self._evalmon)
     if self._useStrictRange:
         indx = list(self.popEnergy).index(self.bestEnergy)
         ngen = self.generations #XXX: no random if generations=0 ?
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(self.population[i], (not ngen) or (i is indx))
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate cost function with bounds, penalties, monitors, etc"""
     #print("@%r %r %r" % (cost, ExtraArgs, max))
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     self._fcalls, cost = wrap_function(cost, ExtraArgs, self._evalmon)
     if self._useStrictRange:
         indx = list(self.popEnergy).index(self.bestEnergy)
         ngen = self.generations #XXX: no random if generations=0 ?
         for i in range(self.nPop):
             self.population[i] = self._clipGuessWithinRangeBoundary(self.population[i], (not ngen) or (i is indx))
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
Beispiel #9
0
    def _decorate_objective(self, cost, ExtraArgs=None):
        """decorate the cost function with bounds, penalties, monitors, etc

input::
    - cost is the objective function, of the form y = cost(x, *ExtraArgs),
      where x is a candidate solution, and ExtraArgs is the tuple of positional
      arguments required to evaluate the objective."""
        #print("@%r %r %r" % (cost, ExtraArgs, max))
        evalmon = self._evalmon
        raw = cost
        if ExtraArgs is None: ExtraArgs = ()
        self._fcalls, cost = wrap_function(cost, ExtraArgs, evalmon)
        if self._useStrictRange:
            if self.generations:
                #NOTE: pop[0] was best, may not be after resetting simplex
                for i, j in enumerate(self._setSimplexWithinRangeBoundary()):
                    self.population[i + 1] = self.population[0].copy()
                    self.population[i + 1][i] = j
            else:
                self.population[0] = self._clipGuessWithinRangeBoundary(
                    self.population[0])
            cost = wrap_bounds(cost, self._strictMin,
                               self._strictMax)  #XXX: remove?
            from mystic.constraints import and_
            constraints = and_(self._constraints,
                               self._strictbounds,
                               onfail=self._strictbounds)
        else:
            constraints = self._constraints
        cost = wrap_penalty(cost, self._penalty)
        cost = wrap_nested(cost, constraints)
        if self._reducer:
            #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
            cost = reduced(self._reducer, arraylike=True)(cost)
        # hold on to the 'wrapped' and 'raw' cost function
        self._cost = (cost, raw, ExtraArgs)
        self._live = True
        return cost
Beispiel #10
0
 def _decorate_objective(self, cost, ExtraArgs=None):
     """decorate the cost function with bounds, penalties, monitors, etc"""
     #print ("@", cost, ExtraArgs, max)
     raw = cost
     if ExtraArgs is None: ExtraArgs = ()
     self._fcalls, cost = wrap_function(cost, ExtraArgs, self._evalmon)
     if self._useStrictRange:
         if self.generations:
             #NOTE: pop[0] was best, may not be after resetting simplex
             for i,j in enumerate(self._setSimplexWithinRangeBoundary()):
                 self.population[i+1] = self.population[0].copy()
                 self.population[i+1][i] = j
         else:
             self.population[0] = self._clipGuessWithinRangeBoundary(self.population[0])
         cost = wrap_bounds(cost, self._strictMin, self._strictMax)
     cost = wrap_penalty(cost, self._penalty)
     cost = wrap_nested(cost, self._constraints)
     if self._reducer:
        #cost = reduced(*self._reducer)(cost) # was self._reducer = (f,bool)
         cost = reduced(self._reducer, arraylike=True)(cost)
     # hold on to the 'wrapped' and 'raw' cost function
     self._cost = (cost, raw, ExtraArgs)
     self._live = True
     return cost
Beispiel #11
0
def model_plotter(model, logfile=None, **kwds):
    """
generate surface contour plots for model, specified by full import path
generate model trajectory from logfile (or solver restart file), if provided

Available from the command shell as:
  mystic_model_plotter.py model (filename) [options]

or as a function call as:
  mystic.model_plotter(model, filename=None, **options)

The option "bounds" takes an indicator string, where the bounds should
be given as comma-separated slices. For example, using bounds = "-1:10, 0:20"
will set the lower and upper bounds for x to be (-1,10) and y to be (0,20).
The "step" can also be given, to control the number of lines plotted in the
grid. Thus "-1:10:.1, 0:20" would set the bounds as above, but use increments
of .1 along x and the default step along y.  For models with > 2D, the bounds
can be used to specify 2 dimensions plus fixed values for remaining dimensions.
Thus, "-1:10, 0:20, 1.0" would plot the 2D surface where the z-axis was fixed
at z=1.0.  When called from a script, slice objects can be used instead of a
string, thus "-1:10:.1, 0:20, 1.0" becomes (slice(-1,10,.1), slice(20), 1.0).

The option "label" takes comma-separated strings. For example, label = "x,y,"
will place 'x' on the x-axis, 'y' on the y-axis, and nothing on the z-axis.
LaTeX is also accepted. For example, label = "$ h $, $ {\\alpha}$, $ v$" will
label the axes with standard LaTeX math formatting. Note that the leading
space is required, while a trailing space aligns the text with the axis
instead of the plot frame.

The option "reduce" can be given to reduce the output of a model to a scalar,
thus converting 'model(params)' to 'reduce(model(params))'. A reducer is given
by the import path (e.g. 'numpy.add'). The option "scale" will convert the plot
to log-scale, and scale the cost by 'z=log(4*z*scale+1)+2'. This is useful for
visualizing small contour changes around the minimium. If using log-scale
produces negative numbers, the option "shift" can be used to shift the cost
by 'z=z+shift'. Both shift and scale are intended to help visualize contours.

Required Inputs:
  model               full import path for the model (e.g. mystic.models.rosen)

Additional Inputs:
  filename            name of the convergence logfile (e.g. log.txt)
"""
    #FIXME: should be able to:
    # - apply a constraint as a region of NaN -- apply when 'xx,yy=x[ij],y[ij]'
    # - apply a penalty by shifting the surface (plot w/alpha?) -- as above
    # - build an appropriately-sized default grid (from logfile info)
    # - move all mulit-id param/cost reading into read_history
    #FIXME: current issues:
    # - 1D slice and projection work for 2D function, but aren't "pretty"
    # - 1D slice and projection for 1D function, is it meaningful and correct?
    # - should be able to plot from solver.genealogy (multi-monitor?) [1D,2D,3D?]
    # - should be able to scale 'z-axis' instead of scaling 'z' itself
    #   (see https://github.com/matplotlib/matplotlib/issues/209)
    # - if trajectory outside contour grid, will increase bounds
    #   (see support_hypercube.py for how to fix bounds)
    import shlex
    global __quit
    __quit = False
    _model = None
    _reducer = None
    _solver = None

    instance = None
    # handle the special case where list is provided by sys.argv
    if isinstance(model, (list, tuple)) and not logfile and not kwds:
        cmdargs = model  # (above is used by script to parse command line)
    elif isinstance(model, basestring) and not logfile and not kwds:
        cmdargs = shlex.split(model)
    # 'everything else' is essentially the functional interface
    else:
        cmdargs = kwds.get('kwds', '')
        if not cmdargs:
            out = kwds.get('out', None)
            bounds = kwds.get('bounds', None)
            label = kwds.get('label', None)
            nid = kwds.get('nid', None)
            iter = kwds.get('iter', None)
            reduce = kwds.get('reduce', None)
            scale = kwds.get('scale', None)
            shift = kwds.get('shift', None)
            fill = kwds.get('fill', False)
            depth = kwds.get('depth', False)
            dots = kwds.get('dots', False)
            join = kwds.get('join', False)
            verb = kwds.get('verb', False)

            # special case: bounds passed as list of slices
            if not isinstance(bounds, (basestring, type(None))):
                cmdargs = ''
                for b in bounds:
                    if isinstance(b, slice):
                        cmdargs += "{}:{}:{}, ".format(b.start, b.stop, b.step)
                    else:
                        cmdargs += "{}, ".format(b)
                bounds = cmdargs[:-2]
                cmdargs = ''

            if callable(reduce): _reducer, reduce = reduce, None

        # special case: model passed as model instance
    #model.__doc__.split('using::')[1].split()[0].strip()
        if callable(model): _model, model = model, "None"

        # handle logfile if given
        if logfile:
            if isinstance(logfile, basestring):
                model += ' ' + logfile
            else:  # special case of passing in monitor instance
                instance = logfile

        # process "commandline" arguments
        if not cmdargs:
            cmdargs = ''
            cmdargs += '' if out is None else '--out={} '.format(out)
            cmdargs += '' if bounds is None else '--bounds="{}" '.format(
                bounds)
            cmdargs += '' if label is None else '--label={} '.format(label)
            cmdargs += '' if nid is None else '--nid={} '.format(nid)
            cmdargs += '' if iter is None else '--iter={} '.format(iter)
            cmdargs += '' if reduce is None else '--reduce={} '.format(reduce)
            cmdargs += '' if scale is None else '--scale={} '.format(scale)
            cmdargs += '' if shift is None else '--shift={} '.format(shift)
            cmdargs += '' if fill == False else '--fill '
            cmdargs += '' if depth == False else '--depth '
            cmdargs += '' if dots == False else '--dots '
            cmdargs += '' if join == False else '--join '
            cmdargs += '' if verb == False else '--verb '
        else:
            cmdargs = ' ' + cmdargs
        cmdargs = model.split() + shlex.split(cmdargs)

    #XXX: note that 'argparse' is new as of python2.7
    from optparse import OptionParser

    def _exit(self, errno=None, msg=None):
        global __quit
        __quit = True
        if errno or msg:
            msg = msg.split(': error: ')[-1].strip()
            raise IOError(msg)

    OptionParser.exit = _exit

    parser = OptionParser(usage=model_plotter.__doc__.split('\n\nOptions:')[0])
    parser.add_option("-u","--out",action="store",dest="out",\
                      metavar="STR",default=None,
                      help="filepath to save generated plot")
    parser.add_option("-b","--bounds",action="store",dest="bounds",\
                      metavar="STR",default="-5:5:.1, -5:5:.1",
                      help="indicator string to set plot bounds and density")
    parser.add_option("-l","--label",action="store",dest="label",\
                      metavar="STR",default=",,",
                      help="string to assign label to axis")
    parser.add_option("-n","--nid",action="store",dest="id",\
                      metavar="INT",default=None,
                      help="id # of the nth simultaneous points to plot")
    parser.add_option("-i","--iter",action="store",dest="stop",\
                      metavar="STR",default=":",
                      help="string for smallest:largest iterations to plot")
    parser.add_option("-r","--reduce",action="store",dest="reducer",\
                      metavar="STR",default="None",
                      help="import path of output reducer function")
    parser.add_option("-x","--scale",action="store",dest="zscale",\
                      metavar="INT",default=0.0,
                      help="scale plotted cost by z=log(4*z*scale+1)+2")
    parser.add_option("-z","--shift",action="store",dest="zshift",\
                      metavar="INT",default=0.0,
                      help="shift plotted cost by z=z+shift")
    parser.add_option("-f","--fill",action="store_true",dest="fill",\
                      default=False,help="plot using filled contours")
    parser.add_option("-d","--depth",action="store_true",dest="surface",\
                      default=False,help="plot contours showing depth in 3D")
    parser.add_option("-o","--dots",action="store_true",dest="dots",\
                      default=False,help="show trajectory points in plot")
    parser.add_option("-j","--join",action="store_true",dest="line",\
                      default=False,help="connect trajectory points in plot")
    parser.add_option("-v","--verb",action="store_true",dest="verbose",\
                      default=False,help="print model documentation string")

    #   import sys
    #   if 'mystic_model_plotter.py' not in sys.argv:
    from StringIO import StringIO
    f = StringIO()
    parser.print_help(file=f)
    f.seek(0)
    if 'Options:' not in model_plotter.__doc__:
        model_plotter.__doc__ += '\nOptions:%s' % f.read().split(
            'Options:')[-1]
    f.close()

    try:
        parsed_opts, parsed_args = parser.parse_args(cmdargs)
    except UnboundLocalError:
        pass
    if __quit: return

    # get the import path for the model
    model = parsed_args[0]  # e.g. 'mystic.models.rosen'
    if "None" == model: model = None

    try:  # get the name of the parameter log file
        source = parsed_args[1]  # e.g. 'log.txt'
    except:
        source = None

    try:  # select the bounds
        options = parsed_opts.bounds  # format is "-1:10:.1, -1:10:.1, 1.0"
    except:
        options = "-5:5:.1, -5:5:.1"

    try:  # plot using filled contours
        fill = parsed_opts.fill
    except:
        fill = False

    try:  # plot contours showing depth in 3D
        surface = parsed_opts.surface
    except:
        surface = False

    #XXX: can't do '-x' with no argument given  (use T/F instead?)
    try:  # scale plotted cost by z=log(4*z*scale+1)+2
        scale = float(parsed_opts.zscale)
        if not scale: scale = False
    except:
        scale = False

    #XXX: can't do '-z' with no argument given
    try:  # shift plotted cost by z=z+shift
        shift = float(parsed_opts.zshift)
        if not shift: shift = False
    except:
        shift = False

    try:  # import path of output reducer function
        reducer = parsed_opts.reducer  # e.g. 'numpy.add'
        if "None" == reducer: reducer = None
    except:
        reducer = None

    style = '-'  # default linestyle
    if parsed_opts.dots:
        mark = 'o'  # marker=mark
        # when using 'dots', also can turn off 'line'
        if not parsed_opts.line:
            style = ''  # linestyle='None'
    else:
        mark = ''
    color = 'w' if fill else 'k'
    style = color + style + mark

    try:  # select labels for the axes
        label = parsed_opts.label.split(',')  # format is "x, y, z"
    except:
        label = ['', '', '']

    try:  # select which 'id' to plot results for
        ids = (int(parsed_opts.id), )  #XXX: allow selecting more than one id ?
    except:
        ids = None  # i.e. 'all'

    try:  # select which iteration to stop plotting at
        stop = parsed_opts.stop  # format is "1:10:1"
        stop = stop if ":" in stop else ":" + stop
    except:
        stop = ":"

    try:  # select whether to be verbose about model documentation
        verbose = bool(parsed_opts.verbose)
    except:
        verbose = False

    #################################################
    solver = None  # set to 'mystic.solvers.fmin' (or similar) for 'live' fits
    #NOTE: 'live' runs constrain params explicitly in the solver, then reduce
    #      dimensions appropriately so results can be 2D contour plotted.
    #      When working with legacy results that have more than 2 params,
    #      the trajectory WILL NOT follow the masked surface generated
    #      because the masked params were NOT fixed when the solver was run.
    #################################################

    from mystic.tools import reduced, masked, partial

    # process inputs
    if _model: model = _model
    if _reducer: reducer = _reducer
    if _solver: solver = _solver
    select, spec, mask = _parse_input(options)
    x, y = _parse_axes(spec, grid=True)  # grid=False for 1D plots
    #FIXME: does grid=False still make sense here...?
    if reducer: reducer = _reducer or _get_instance(reducer)
    if solver and (not source or not model):  #XXX: not instance?
        raise RuntimeError('a model and results filename are required')
    elif not source and not model and not instance:
        raise RuntimeError('a model or a results file is required')
    if model:
        model = _model or _get_instance(model)
        if verbose: print model.__doc__
        # need a reducer if model returns an array
        if reducer: model = reduced(reducer, arraylike=False)(model)

    if solver:
        # if 'live'... pick a solver
        solver = 'mystic.solvers.fmin'
        solver = _solver or _get_instance(solver)
        xlen = len(select) + len(mask)
        if solver.__name__.startswith('diffev'):
            initial = [(-1, 1)] * xlen
        else:
            initial = [0] * xlen
        from mystic.monitors import VerboseLoggingMonitor
        if instance:
            itermon = VerboseLoggingMonitor(new=True)
            itermon.prepend(instance)
        else:
            itermon = VerboseLoggingMonitor(filename=source, new=True)
        # explicitly constrain parameters
        model = partial(mask)(model)
        # solve
        sol = solver(model, x0=initial, itermon=itermon)

        #-OVERRIDE-INPUTS-#
        import numpy
        # read trajectories from monitor (comment out to use logfile)
        source = itermon
        # if negative minimum, shift by the 'solved minimum' plus an epsilon
        shift = max(-numpy.min(itermon.y), 0.0) + 0.5  # a good guess
        #-----------------#

    if model:  # for plotting, implicitly constrain by reduction
        model = masked(mask)(model)

        ## plot the surface in 1D
        #if solver: v=sol[-1]
        #elif source: v=cost[-1]
        #else: v=None
        #fig0 = _draw_slice(model, x=x, y=v, scale=scale, shift=shift)
        # plot the surface in 2D or 3D
        fig = _draw_contour(model,
                            x,
                            y,
                            surface=surface,
                            fill=fill,
                            scale=scale,
                            shift=shift)
    else:
        #fig0 = None
        fig = None

    if instance: source = instance
    if source:
        # params are the parameter trajectories
        # cost is the solution trajectory
        params, cost = _get_history(source, ids)
        if len(cost) > 1: style = style[1:]  # 'auto-color' #XXX: or grayscale?

        for p, c in zip(params, cost):
            ## project trajectory on a 1D slice of model surface #XXX: useful?
            #s = select[0] if len(select) else 0
            #px = p[int(s)] # _draw_projection requires one parameter
            ## ignore everything after 'stop'
            #_c = eval('c[%s]' % stop)
            #_x = eval('px[%s]' % stop)
            #fig0 = _draw_projection(_x,_c, style=style, scale=scale, shift=shift, figure=fig0)

            # plot the trajectory on the model surface (2D or 3D)
            # get two selected params #XXX: what if len(select)<2? or len(p)<2?
            p = [p[int(i)] for i in select[:2]]
            px, py = p  # _draw_trajectory requires two parameters
            # ignore everything after 'stop'
            _x = eval('px[%s]' % stop)
            _y = eval('py[%s]' % stop)
            _c = eval('c[%s]' % stop) if surface else None
            fig = _draw_trajectory(_x,
                                   _y,
                                   _c,
                                   style=style,
                                   scale=scale,
                                   shift=shift,
                                   figure=fig)

    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import axes3d
    # add labels to the axes
    if surface: kwds = {'projection': '3d'}  # 3D
    else: kwds = {}  # 2D
    ax = fig.gca(**kwds)
    ax.set_xlabel(label[0])
    ax.set_ylabel(label[1])
    if surface: ax.set_zlabel(label[2])

    if not parsed_opts.out:
        plt.show()
    else:
        fig.savefig(parsed_opts.out)
Beispiel #12
0
def model_plotter(model, logfile=None, **kwds):
    """
generate surface contour plots for model, specified by full import path
generate model trajectory from logfile (or solver restart file), if provided

Available from the command shell as:
  mystic_model_plotter.py model (filename) [options]

or as a function call as:
  mystic.model_plotter(model, filename=None, **options)

The option "bounds" takes an indicator string, where the bounds should
be given as comma-separated slices. For example, using bounds = "-1:10, 0:20"
will set the lower and upper bounds for x to be (-1,10) and y to be (0,20).
The "step" can also be given, to control the number of lines plotted in the
grid. Thus "-1:10:.1, 0:20" would set the bounds as above, but use increments
of .1 along x and the default step along y.  For models with > 2D, the bounds
can be used to specify 2 dimensions plus fixed values for remaining dimensions.
Thus, "-1:10, 0:20, 1.0" would plot the 2D surface where the z-axis was fixed
at z=1.0.  When called from a script, slice objects can be used instead of a
string, thus "-1:10:.1, 0:20, 1.0" becomes (slice(-1,10,.1), slice(20), 1.0).

The option "label" takes comma-separated strings. For example, label = "x,y,"
will place 'x' on the x-axis, 'y' on the y-axis, and nothing on the z-axis.
LaTeX is also accepted. For example, label = "$ h $, $ {\\alpha}$, $ v$" will
label the axes with standard LaTeX math formatting. Note that the leading
space is required, while a trailing space aligns the text with the axis
instead of the plot frame.

The option "reduce" can be given to reduce the output of a model to a scalar,
thus converting 'model(params)' to 'reduce(model(params))'. A reducer is given
by the import path (e.g. 'numpy.add'). The option "scale" will convert the plot
to log-scale, and scale the cost by 'z=log(4*z*scale+1)+2'. This is useful for
visualizing small contour changes around the minimium. If using log-scale
produces negative numbers, the option "shift" can be used to shift the cost
by 'z=z+shift'. Both shift and scale are intended to help visualize contours.

Required Inputs:
  model               full import path for the model (e.g. mystic.models.rosen)

Additional Inputs:
  filename            name of the convergence logfile (e.g. log.txt)
"""
    #FIXME: should be able to:
    # - apply a constraint as a region of NaN -- apply when 'xx,yy=x[ij],y[ij]'
    # - apply a penalty by shifting the surface (plot w/alpha?) -- as above
    # - build an appropriately-sized default grid (from logfile info)
    # - move all mulit-id param/cost reading into read_history
    #FIXME: current issues:
    # - 1D slice and projection work for 2D function, but aren't "pretty"
    # - 1D slice and projection for 1D function, is it meaningful and correct?
    # - should be able to plot from solver.genealogy (multi-monitor?) [1D,2D,3D?]
    # - should be able to scale 'z-axis' instead of scaling 'z' itself
    #   (see https://github.com/matplotlib/matplotlib/issues/209)
    # - if trajectory outside contour grid, will increase bounds
    #   (see support_hypercube.py for how to fix bounds)
    import shlex
    try:
        basestring
        from StringIO import StringIO
    except NameError:
        basestring = str
        from io import StringIO
    global __quit
    __quit = False
    _model = None
    _reducer = None
    _solver = None

    instance = None
    # handle the special case where list is provided by sys.argv
    if isinstance(model, (list,tuple)) and not logfile and not kwds:
        cmdargs = model # (above is used by script to parse command line)
    elif isinstance(model, basestring) and not logfile and not kwds:
        cmdargs = shlex.split(model)
    # 'everything else' is essentially the functional interface
    else:
        cmdargs = kwds.get('kwds', '')
        if not cmdargs:
            out = kwds.get('out', None)
            bounds = kwds.get('bounds', None)
            label = kwds.get('label', None)
            nid = kwds.get('nid', None)
            iter = kwds.get('iter', None)
            reduce = kwds.get('reduce', None)
            scale = kwds.get('scale', None)
            shift = kwds.get('shift', None)
            fill = kwds.get('fill', False)
            depth = kwds.get('depth', False)
            dots = kwds.get('dots', False)
            join = kwds.get('join', False)
            verb = kwds.get('verb', False)

            # special case: bounds passed as list of slices
            if not isinstance(bounds, (basestring, type(None))):
                cmdargs = ''
                for b in bounds:
                    if isinstance(b, slice):
                        cmdargs += "{}:{}:{}, ".format(b.start, b.stop, b.step)
                    else:
                        cmdargs += "{}, ".format(b)
                bounds = cmdargs[:-2]
                cmdargs = ''

            if isinstance(reduce, collections.Callable): _reducer, reduce = reduce, None

        # special case: model passed as model instance
       #model.__doc__.split('using::')[1].split()[0].strip()
        if isinstance(model, collections.Callable): _model, model = model, "None"

        # handle logfile if given
        if logfile:
            if isinstance(logfile, basestring):
                model += ' ' + logfile
            else: # special case of passing in monitor instance
                instance = logfile

        # process "commandline" arguments
        if not cmdargs:
            cmdargs = ''
            cmdargs += '' if out is None else '--out={} '.format(out)
            cmdargs += '' if bounds is None else '--bounds="{}" '.format(bounds)
            cmdargs += '' if label is None else '--label={} '.format(label)
            cmdargs += '' if nid is None else '--nid={} '.format(nid)
            cmdargs += '' if iter is None else '--iter={} '.format(iter)
            cmdargs += '' if reduce is None else '--reduce={} '.format(reduce)
            cmdargs += '' if scale is None else '--scale={} '.format(scale)
            cmdargs += '' if shift is None else '--shift={} '.format(shift)
            cmdargs += '' if fill == False else '--fill '
            cmdargs += '' if depth == False else '--depth '
            cmdargs += '' if dots == False else '--dots '
            cmdargs += '' if join == False else '--join '
            cmdargs += '' if verb == False else '--verb '
        else:
            cmdargs = ' ' + cmdargs
        cmdargs = model.split() + shlex.split(cmdargs)

    #XXX: note that 'argparse' is new as of python2.7
    from optparse import OptionParser
    def _exit(self, errno=None, msg=None):
      global __quit
      __quit = True
      if errno or msg:
        msg = msg.split(': error: ')[-1].strip()
        raise IOError(msg)
    OptionParser.exit = _exit

    parser = OptionParser(usage=model_plotter.__doc__.split('\n\nOptions:')[0])
    parser.add_option("-u","--out",action="store",dest="out",\
                      metavar="STR",default=None,
                      help="filepath to save generated plot")
    parser.add_option("-b","--bounds",action="store",dest="bounds",\
                      metavar="STR",default="-5:5:.1, -5:5:.1",
                      help="indicator string to set plot bounds and density")
    parser.add_option("-l","--label",action="store",dest="label",\
                      metavar="STR",default=",,",
                      help="string to assign label to axis")
    parser.add_option("-n","--nid",action="store",dest="id",\
                      metavar="INT",default=None,
                      help="id # of the nth simultaneous points to plot")
    parser.add_option("-i","--iter",action="store",dest="stop",\
                      metavar="STR",default=":",
                      help="string for smallest:largest iterations to plot")
    parser.add_option("-r","--reduce",action="store",dest="reducer",\
                      metavar="STR",default="None",
                      help="import path of output reducer function")
    parser.add_option("-x","--scale",action="store",dest="zscale",\
                      metavar="INT",default=0.0,
                      help="scale plotted cost by z=log(4*z*scale+1)+2")
    parser.add_option("-z","--shift",action="store",dest="zshift",\
                      metavar="INT",default=0.0,
                      help="shift plotted cost by z=z+shift")
    parser.add_option("-f","--fill",action="store_true",dest="fill",\
                      default=False,help="plot using filled contours")
    parser.add_option("-d","--depth",action="store_true",dest="surface",\
                      default=False,help="plot contours showing depth in 3D")
    parser.add_option("-o","--dots",action="store_true",dest="dots",\
                      default=False,help="show trajectory points in plot")
    parser.add_option("-j","--join",action="store_true",dest="line",\
                      default=False,help="connect trajectory points in plot")
    parser.add_option("-v","--verb",action="store_true",dest="verbose",\
                      default=False,help="print model documentation string")

#   import sys
#   if 'mystic_model_plotter.py' not in sys.argv:
    if PY3:
      f = StringIO()
      parser.print_help(file=f)
      f.seek(0)
      if 'Options:' not in model_plotter.__doc__:
        model_plotter.__doc__ += '\nOptions:%s' % f.read().split('Options:')[-1]
      f.close()
    else:
      if 'Options:' not in model_plotter.__doc__:
        model_plotter.__doc__ += '\nOptions:%s' % parser.format_help().split('Options:')[-1]

    try:
      parsed_opts, parsed_args = parser.parse_args(cmdargs)
    except UnboundLocalError:
      pass
    if __quit: return

    # get the import path for the model
    model = parsed_args[0]  # e.g. 'mystic.models.rosen'
    if "None" == model: model = None

    try: # get the name of the parameter log file
      source = parsed_args[1]  # e.g. 'log.txt'
    except:
      source = None

    try: # select the bounds
      options = parsed_opts.bounds  # format is "-1:10:.1, -1:10:.1, 1.0"
    except:
      options = "-5:5:.1, -5:5:.1"

    try: # plot using filled contours
      fill = parsed_opts.fill
    except:
      fill = False

    try: # plot contours showing depth in 3D
      surface = parsed_opts.surface
    except:
      surface = False

    #XXX: can't do '-x' with no argument given  (use T/F instead?)
    try: # scale plotted cost by z=log(4*z*scale+1)+2
      scale = float(parsed_opts.zscale)
      if not scale: scale = False
    except:
      scale = False

    #XXX: can't do '-z' with no argument given
    try: # shift plotted cost by z=z+shift
      shift = float(parsed_opts.zshift)
      if not shift: shift = False
    except:
      shift = False

    try: # import path of output reducer function
      reducer = parsed_opts.reducer  # e.g. 'numpy.add'
      if "None" == reducer: reducer = None
    except:
      reducer = None

    style = '-' # default linestyle
    if parsed_opts.dots:
      mark = 'o' # marker=mark
      # when using 'dots', also can turn off 'line'
      if not parsed_opts.line:
        style = '' # linestyle='None'
    else:
      mark = ''
    color = 'w' if fill else 'k'
    style = color + style + mark

    try: # select labels for the axes
      label = parsed_opts.label.split(',')  # format is "x, y, z"
    except:
      label = ['','','']

    try: # select which 'id' to plot results for
      ids = (int(parsed_opts.id),) #XXX: allow selecting more than one id ?
    except:
      ids = None # i.e. 'all'

    try: # select which iteration to stop plotting at
      stop = parsed_opts.stop  # format is "1:10:1"
      stop = stop if ":" in stop else ":"+stop
    except:
      stop = ":"

    try: # select whether to be verbose about model documentation
      verbose = bool(parsed_opts.verbose)
    except:
      verbose = False

    #################################################
    solver = None  # set to 'mystic.solvers.fmin' (or similar) for 'live' fits
    #NOTE: 'live' runs constrain params explicitly in the solver, then reduce
    #      dimensions appropriately so results can be 2D contour plotted.
    #      When working with legacy results that have more than 2 params,
    #      the trajectory WILL NOT follow the masked surface generated
    #      because the masked params were NOT fixed when the solver was run.
    #################################################

    from mystic.tools import reduced, masked, partial

    # process inputs
    if _model: model = _model
    if _reducer: reducer = _reducer
    if _solver: solver = _solver
    select, spec, mask = _parse_input(options)
    x,y = _parse_axes(spec, grid=True) # grid=False for 1D plots
    #FIXME: does grid=False still make sense here...?
    if reducer: reducer = _reducer or _get_instance(reducer)
    if solver and (not source or not model): #XXX: not instance?
        raise RuntimeError('a model and results filename are required')
    elif not source and not model and not instance:
        raise RuntimeError('a model or a results file is required')
    if model:
        model = _model or _get_instance(model)
        if verbose: print(model.__doc__)
        # need a reducer if model returns an array
        if reducer: model = reduced(reducer, arraylike=False)(model)

    if solver:
        # if 'live'... pick a solver
        solver = 'mystic.solvers.fmin'
        solver = _solver or _get_instance(solver)
        xlen = len(select)+len(mask)
        if solver.__name__.startswith('diffev'):
            initial = [(-1,1)]*xlen
        else:
            initial = [0]*xlen
        from mystic.monitors import VerboseLoggingMonitor
        if instance:
            itermon = VerboseLoggingMonitor(new=True)
            itermon.prepend(instance)
        else:
            itermon = VerboseLoggingMonitor(filename=source, new=True)
        # explicitly constrain parameters
        model = partial(mask)(model)
        # solve
        sol = solver(model, x0=initial, itermon=itermon)

        #-OVERRIDE-INPUTS-#
        import numpy
        # read trajectories from monitor (comment out to use logfile)
        source = itermon
        # if negative minimum, shift by the 'solved minimum' plus an epsilon
        shift = max(-numpy.min(itermon.y), 0.0) + 0.5 # a good guess
        #-----------------#

    if model: # for plotting, implicitly constrain by reduction
        model = masked(mask)(model)

       ## plot the surface in 1D
       #if solver: v=sol[-1]
       #elif source: v=cost[-1]
       #else: v=None
       #fig0 = _draw_slice(model, x=x, y=v, scale=scale, shift=shift)
        # plot the surface in 2D or 3D
        fig = _draw_contour(model, x, y, surface=surface, fill=fill, scale=scale, shift=shift)
    else:
       #fig0 = None
        fig = None

    if instance: source = instance
    if source:
        # params are the parameter trajectories
        # cost is the solution trajectory
        params, cost = _get_history(source, ids)
        if len(cost) > 1: style = style[1:] # 'auto-color' #XXX: or grayscale?

        for p,c in zip(params, cost):
           ## project trajectory on a 1D slice of model surface #XXX: useful?
           #s = select[0] if len(select) else 0
           #px = p[int(s)] # _draw_projection requires one parameter
           ## ignore everything after 'stop'
           #_c = eval('c[%s]' % stop)
           #_x = eval('px[%s]' % stop)
           #fig0 = _draw_projection(_x,_c, style=style, scale=scale, shift=shift, figure=fig0)

            # plot the trajectory on the model surface (2D or 3D)
            # get two selected params #XXX: what if len(select)<2? or len(p)<2?
            p = [p[int(i)] for i in select[:2]]
            px,py = p # _draw_trajectory requires two parameters
            # ignore everything after 'stop'
            locals = dict(px=px, py=py, c=c)
            _x = eval('px[%s]' % stop, locals)
            _y = eval('py[%s]' % stop, locals)
            _c = eval('c[%s]' % stop, locals) if surface else None
            fig = _draw_trajectory(_x,_y,_c, style=style, scale=scale, shift=shift, figure=fig)

    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import axes3d
    # add labels to the axes
    if surface: kwds = {'projection':'3d'} # 3D
    else: kwds = {}                        # 2D
    ax = fig.gca(**kwds)
    ax.set_xlabel(label[0])
    ax.set_ylabel(label[1])
    if surface: ax.set_zlabel(label[2])

    if not parsed_opts.out:
        plt.show()
    else:
        fig.savefig(parsed_opts.out)
Beispiel #13
0
    from mystic.tools import reduced, masked, partial

    # process inputs
    select, spec, mask = parse_input(options)
    x, y = parse_axes(spec, grid=True)  # grid=False for 1D plots
    #FIXME: does grid=False still make sense here...?
    if reducer: reducer = get_instance(reducer)
    if solver and (not source or not model):
        raise RuntimeError('a model and results filename are required')
    elif not source and not model:
        raise RuntimeError('a model or a results file is required')
    if model:
        model = get_instance(model)
        # need a reducer if model returns an array
        if reducer: model = reduced(reducer, arraylike=False)(model)

    if solver:
        # if 'live'... pick a solver
        solver = 'mystic.solvers.fmin'
        solver = get_instance(solver)
        xlen = len(select) + len(mask)
        if solver.__name__.startswith('diffev'):
            initial = [(-1, 1)] * xlen
        else:
            initial = [0] * xlen
        from mystic.monitors import VerboseLoggingMonitor
        itermon = VerboseLoggingMonitor(filename=source, new=True)
        # explicitly constrain parameters
        model = partial(mask)(model)
        # solve
Beispiel #14
0
    from mystic.tools import reduced, masked, partial

    # process inputs
    select, spec, mask = parse_input(options)
    x,y = parse_axes(spec, grid=True) # grid=False for 1D plots
    #FIXME: does grid=False still make sense here...?
    if reducer: reducer = get_instance(reducer)
    if solver and (not source or not model):
        raise RuntimeError('a model and results filename are required')
    elif not source and not model:
        raise RuntimeError('a model or a results file is required')
    if model:
        model = get_instance(model)
        # need a reducer if model returns an array
        if reducer: model = reduced(reducer, arraylike=False)(model)

    if solver:
        # if 'live'... pick a solver
        solver = 'mystic.solvers.fmin'
        solver = get_instance(solver)
        xlen = len(select)+len(mask)
        if solver.__name__.startswith('diffev'):
            initial = [(-1,1)]*xlen
        else:
            initial = [0]*xlen
        from mystic.monitors import VerboseLoggingMonitor
        itermon = VerboseLoggingMonitor(filename=source, new=True)
        # explicitly constrain parameters
        model = partial(mask)(model)
        # solve