<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""For generating empirical cumulative distribution function figures.

The outputs show empirical cumulative distribution functions (ECDFs) of
the running times of trials. These ECDFs show on the y-axis the fraction
of cases for which the running time (left subplots) or the df-value
(right subplots) was smaller than the value given on the x-axis. On the
left, ECDFs of the running times from trials are shown for different
target values. Light brown lines in the background show ECDFs for target
value 1e-8 of all algorithms benchmarked during BBOB-2009. On the right,
ECDFs of df-values from all trials are shown for different numbers of
function evaluations.

**Example**

.. plot::
   :width: 75%
   
   import urllib
   import tarfile
   import glob
   from pylab import *
   import bbob_pproc as bb
    
   # Collect and unarchive data (3.4MB)
   dataurl = 'http://coco.lri.fr/BBOB2009/pythondata/BIPOP-CMA-ES.tar.gz'
   filename, headers = urllib.urlretrieve(dataurl)
   archivefile = tarfile.open(filename)
   archivefile.extractall()
    
   # Empirical cumulative distribution function figure
   ds = bb.load(glob.glob('BBOB2009pythondata/BIPOP-CMA-ES/ppdata_f0*_20.pickle'))
   figure()
   bb.pprldistr.plot(ds)
   bb.pprldistr.beautify() # resize the window to view whole figure

CAVEAT: the naming conventions in this module mix up ERT (an estimate 
of the expected running length) and run lengths. 

"""
from __future__ import absolute_import

import os
import numpy as np
import pickle, gzip
import matplotlib.pyplot as plt
from pdb import set_trace
from bbob_pproc import toolsstats, genericsettings, pproc
from bbob_pproc.ppfig import consecutiveNumbers, plotUnifLogXMarkers, saveFigure, logxticks

single_target_values = pproc.TargetValues((10., 1e-1, 1e-4, 1e-8))  # possibly changed in config
single_runlength_factors = [0.5, 1, 2, 4, 10] + [10**i for i in range(2, 12)]

caption_part_one = r"""%
     Empirical cumulative distribution functions (ECDF), plotting the fraction of 
     trials with an outcome not larger than the respective value on the $x$-axis. """
caption_left_fixed_targets = r"""%
     Left subplots: ECDF of the number of function evaluations (FEvals) divided by search space dimension $D$, 
     to fall below $\fopt+\Df$ with $\Df=10^{k}$, where $k$ is the first value in the legend. 
     The thick red line represents the most difficult target value $\fopt+10^{-8}$. """
caption_left_rlbased_targets = r"""%
     Left subplots: ECDF of number of function evaluations (FEvals) divided by search space dimension $D$, 
     to fall below $\fopt+\Df$ where \Df\ is the largest $\Df$-value $\ge10^{-8}$ 
     for which the best \ERT\ seen in the GECCO-BBOB-2009  
     was yet above $k\times\DIM$ evaluations, where $k$ is the first value in the legend. """
caption_right = r"""%
     Right subplots: ECDF of the 
     best achieved $\Df$ 
     for running times of #1
     function evaluations 
     (from right to left magenta-black-$2\times$cyan-$2\times$magenta and further cycling black-cyan-magenta) and final $\Df$-value (red). """
caption_wrap_up = r"""%
     Legends indicate for each target the number of functions that were solved in at
     least one trial.
     \Df\ and \textsf{Df} denote the difference to the optimal function value. """
caption_single_fixed = caption_part_one + caption_left_fixed_targets + caption_right + r"""
     Light brown lines in the background show ECDFs for $\Df=10^{-8}$ of all algorithms benchmarked during BBOB-2009.""" 
caption_single_rlbased = caption_part_one + caption_left_rlbased_targets + caption_right

# TODO: the method names in this module seem to be overly unclear or misleading and should be revised. 
   
refcolor = 'wheat'
nbperdecade = 1  # markers in x-axis decades in ecdfs

runlen_xlimits_max = None
runlen_xlimits_min = 1  # not in use, should become -0.5 in runlength case
# Used as a global to store the largest xmax and align the FV ECD figures.
fmax = None
evalfmax = runlen_xlimits_max  # is manipulated/stored in this module

# TODO: the target function values and the styles of the line only make sense
# together. Therefore we should either:
# 1. keep the targets as input argument and make rldStyles depend on them or
# 2. remove the targets as input argument and put them here.
rldStyles = ({'color': 'k', 'ls': '-'},
             {'color': 'c'},
             {'color': 'm', 'ls': '-'},
             {'color': 'r', 'linewidth': 3.},
             {'color': 'k'},
             {'color': 'c'},
             {'color': 'm'},
             {'color': 'r'},
             {'color': 'k'},
             {'color': 'c'},
             {'color': 'm'},
             {'color': 'r', 'linewidth': 3.})
rldUnsuccStyles = (
                   {'color': 'm', 'ls': '-'},
                   {'color': 'k', 'ls': '-'},
                   {'color': 'c'},
                   {'color': 'c'},
                   {'color': 'm', 'ls': '-'},
                   {'color': 'm'},
                   {'color': 'k', 'ls': '-'},
                   {'color': 'c'},
                   {'color': 'm', 'ls': '-'},
                   {'color': 'k'},
                   {'color': 'c', 'ls': '-'},
                   {'color': 'm'})  # should not be too short

previous_data_filename = 'pprldistr2009_1e-8.pickle.gz'
previous_data_filename = os.path.join(os.path.split(__file__)[0], previous_data_filename)
previous_algorithm_data_found = True
try:
    # cocofy(previous_data_filename)
    f = gzip.open(previous_data_filename,'r')
    dictprevalg = pickle.load(f)
except IOError, (errno, strerror):
    print "I/O error(%s): %s" % (errno, strerror)
    previous_algorithm_data_found = False
    print 'Could not find file: ', previous_data_filename
else:
    f.close()

def caption_single(max_evals_div_dim):
    caption = caption_single_rlbased if genericsettings.runlength_based_targets else caption_single_fixed 
    return caption.replace(r'#1', '$' + 'D, '.join([str(i) for i in single_runlength_factors[:7]]) + 'D,\dots$') 

def beautifyECDF():
    """Generic formatting of ECDF figures."""
    plt.ylim(-0.0, 1.01)  # was plt.ylim(-0.01, 1.01)
    plt.yticks(np.arange(0., 1.001, 0.2)) #, ('0.0', '', '0.5', '', '1.0'))
    plt.grid(True)
    xmin, xmax = plt.xlim()
    # plt.xlim(xmin=xmin*0.90)  # why this?
    c = plt.gca().get_children()
    for i in c: # TODO: we only want to extend ECDF lines...
        try:
            if i.get_drawstyle() == 'steps' and not i.get_linestyle() in ('', 'None'):
                xdata = i.get_xdata()
                ydata = i.get_ydata()
                if len(xdata) > 0:
                    #if xmin < min(xdata):
                    #    xdata = np.hstack((xmin, xdata))
                    #    ydata = np.hstack((ydata[0], ydata))
                    if xmax > max(xdata):
                        xdata = np.hstack((xdata, xmax))
                        ydata = np.hstack((ydata, ydata[-1]))
                    plt.setp(i, 'xdata', xdata, 'ydata', ydata)
            elif (i.get_drawstyle() == 'steps' and i.get_marker() != '' and
                  i.get_linestyle() in ('', 'None')):
                xdata = i.get_xdata()
                ydata = i.get_ydata()
                if len(xdata) > 0:
                    #if xmin < min(xdata):
                    #    minidx = np.ceil(np.log10(xmin) * nbperdecade)
                    #    maxidx = np.floor(np.log10(xdata[0]) * nbperdecade)
                    #    x = 10. ** (np.arange(minidx, maxidx + 1) / nbperdecade)
                    #    xdata = np.hstack((x, xdata))
                    #    ydata = np.hstack(([ydata[0]] * len(x), ydata))
                    if xmax > max(xdata):
                        minidx = np.ceil(np.log10(xdata[-1]) * nbperdecade)
                        maxidx = np.floor(np.log10(xmax) * nbperdecade)
                        x = 10. ** (np.arange(minidx, maxidx + 1) / nbperdecade)
                        xdata = np.hstack((xdata, x))
                        ydata = np.hstack((ydata, [ydata[-1]] * len(x)))
                    plt.setp(i, 'xdata', xdata, 'ydata', ydata)
        except (AttributeError, IndexError):
            pass

def beautifyRLD(evalfmax=None):
    """Format and save the figure of the run length distribution.
    
    After calling this function, changing the boundaries of the figure
    will not update the ticks and tick labels.
    
    """
    a = plt.gca()
    a.set_xscale('log')
    a.set_xlabel('log10 of FEvals / DIM')
    a.set_ylabel('proportion of trials')
    logxticks()
    if evalfmax:
        plt.xlim(xmax=evalfmax ** 1.05)
    plt.xlim(xmin=runlen_xlimits_min)
    beautifyECDF()

def beautifyFVD(isStoringXMax=False, ylabel=True):
    """Formats the figure of the run length distribution.

    This function is to be used with :py:func:`plotFVDistr`

    :param bool isStoringMaxF: if set to True, the first call
                               :py:func:`beautifyFVD` sets the global
                               :py:data:`fmax` and all subsequent call
                               will have the same maximum xlim
    :param bool ylabel: if True, y-axis will be labelled.

    """
    a = plt.gca()
    a.set_xscale('log')

    if isStoringXMax:
        global fmax
    else:
        fmax = None

    if not fmax:
        xmin, fmax = plt.xlim()
    plt.xlim(1.01e-8, fmax) # 1e-8 was 1.

    #axisHandle.invert_xaxis()
    a.set_xlabel('log10 of Df')  # / Dftarget
    if ylabel:
        a.set_ylabel('proportion of trials')
    logxticks()
    beautifyECDF()
    if not ylabel:
        a.set_yticklabels(())

def plotECDF(x, n=None, **plotArgs):
    """Plot an empirical cumulative distribution function.

    :param seq x: data
    :param int n: number of samples, if not provided len(x) is used
    :param plotArgs: optional keyword arguments provided to plot.

    :returns: handles of the plot elements.

    """
    if n is None:
        n = len(x)

    nx = len(x)
    if n == 0 or nx == 0:
        res = plt.plot([], [], **plotArgs)
    else:
        x = sorted(x) # do not sort in place
        x = np.hstack((x, x[-1]))
        y = np.hstack((np.arange(0., nx) / n, float(nx)/n))
        res = plotUnifLogXMarkers(x, y, nbperdecade=nbperdecade,
                                 drawstyle='steps', **plotArgs)
    return res

def plotERTDistr(dsList, target, **plotArgs):
    """This method is obsolete, should be removed? The replacement for simulated runlengths is in pprldmany? 
    Creates simulated run time distributions (it is not an ERT distribution) from a DataSetList.

    :keyword DataSet dsList: Input data sets
    :keyword dict target: target precision
    :keyword plotArgs: keyword arguments to pass to plot command

    :return: resulting plot.

    Details: calls ``plotECDF``. 
    
    """
    x = []
    nn = 0
    samplesize = 1000 # samplesize is at least 1000
    percentiles = 0.5 # could be anything...

    for i in dsList:
        #funcs.add(i.funcId)
        for j in i.evals:
            if j[0] <= target[i.funcId]:
                runlengthsucc = j[1:][np.isfinite(j[1:])]
                runlengthunsucc = i.maxevals[np.isnan(j[1:])]
                tmp = toolsstats.drawSP(runlengthsucc, runlengthunsucc,
                                       percentiles=percentiles,
                                       samplesize=samplesize)
                x.extend(tmp[1])
                break
        nn += samplesize
    res = plotECDF(x, nn, **plotArgs)

    return res

def plotRLDistr_old(dsList, target, **plotArgs):
    """Creates run length distributions from a sequence dataSetList.

    Labels of the line (for the legend) will be set automatically with
    the following format: %+d: %d/%d % (log10()
    

    :param DataSetList dsList: Input data sets
    :param dict or float target: target precision
    :param plotArgs: additional arguments passed to the plot command

    :returns: handles of the resulting plot.

    """
    x = []
    nn = 0
    fsolved = set()
    funcs = set()
    for i in dsList:
        funcs.add(i.funcId)
        try:
            target = target[i.funcId]  # TODO: this can only work for a single function, generally looks like a bug
            if not genericsettings.test:
                print 'target:', target
                print 'function:', i.funcId
                raise Exception('please check this, it looks like a bug')
        except TypeError:
            target = target
        tmp = i.detEvals((target, ))[0] / i.dim
        tmp = tmp[np.isnan(tmp) == False] # keep only success
        if len(tmp) > 0:
            fsolved.add(i.funcId)
        x.extend(tmp)
        nn += i.nbRuns()
    kwargs = plotArgs.copy()
    label = ''
    try:
        label += '%+d:' % (np.log10(target))
    except NameError:
        pass
    label += '%d/%d' % (len(fsolved), len(funcs))
    kwargs['label'] = kwargs.setdefault('label', label)
    res = plotECDF(x, nn, **kwargs)
    return res

def plotRLDistr(dsList, target, label, max_fun_evals=np.inf, **plotArgs):
    """Creates run length distributions from a sequence dataSetList.

    Labels of the line (for the legend) will be appended with the number
    of functions at least solved once. 
    

    :param DataSetList dsList: Input data sets
    :param target: a method that delivers target values like ``target((fun, dim))``
    :param str label: target value label to be displayed in the legend
    :param plotArgs: additional arguments passed to the plot command

    :returns: handles of the resulting plot.

    Details: ``target`` is a function taking a (function_number, dimension) pair 
    as input and returning a ``float``. It can be defined as 
    ``lambda fun_dim: targets(fun_dim)[j]`` returning the j-th element of 
    ``targets(fun_dim)``, where ``targets`` is an instance of 
    ``class pproc.TargetValues`` (see the ``pproc.TargetValues.__call__`` method).  
    
    """
    x = []
    nn = 0
    fsolved = set()
    funcs = set()
    for ds in dsList: # ds is a DataSet
        funcs.add(ds.funcId)
        tmp = ds.detEvals((target((ds.funcId, ds.dim)), ))[0] / ds.dim
        tmp = tmp[np.isnan(tmp) == False] # keep only success
        if len(tmp) > 0 and sum(tmp <= max_fun_evals):
            fsolved.add(ds.funcId)
        x.extend(tmp)
        nn += ds.nbRuns()
    kwargs = plotArgs.copy()
    label += ': %d/%d' % (len(fsolved), len(funcs))
    kwargs['label'] = kwargs.setdefault('label', label)
    res = plotECDF(x, nn, **kwargs)
    return res

def plotFVDistr(dsList, target, maxEvalsF=np.inf, **plotArgs):
    """Creates ECDF of final function values plot from a DataSetList.
    
    :param dsList: data sets
    :param dict or float target: used for the lower limit of the plot
    :param float maxEvalsF: maximum evaluations that "count"
    :param plotArgs: additional arguments passed to plot

    :returns: handle

    """
    x = []
    nn = 0
    for i in dsList:
        for j in i.funvals:
            if j[0] >= maxEvalsF * i.dim:
                break
        try:
            # tmp = j[1:].copy() / target[i.funcId]
            tmp = j[1:].copy()
        except TypeError:
            # tmp = j[1:].copy() / target
            tmp = j[1:].copy()
        # Integrate the negative values of df / ftarget together
        # this is to prevent problem when passing on a log scale
        # TODO: there should not be negative values, should there? 
        #print tmp
        #raw_input('press return')
        tmp[tmp<=0.] = min(np.append(tmp[tmp > 0],[target]))
        #print tmp<commit_msg>clean up of buggy treatment of negative Df values by vhm in pprldistr<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""For generating empirical cumulative distribution function figures.

The outputs show empirical cumulative distribution functions (ECDFs) of
the running times of trials. These ECDFs show on the y-axis the fraction
of cases for which the running time (left subplots) or the df-value
(right subplots) was smaller than the value given on the x-axis. On the
left, ECDFs of the running times from trials are shown for different
target values. Light brown lines in the background show ECDFs for target
value 1e-8 of all algorithms benchmarked during BBOB-2009. On the right,
ECDFs of df-values from all trials are shown for different numbers of
function evaluations.

**Example**

.. plot::
   :width: 75%
   
   import urllib
   import tarfile
   import glob
   from pylab import *
   import bbob_pproc as bb
    
   # Collect and unarchive data (3.4MB)
   dataurl = 'http://coco.lri.fr/BBOB2009/pythondata/BIPOP-CMA-ES.tar.gz'
   filename, headers = urllib.urlretrieve(dataurl)
   archivefile = tarfile.open(filename)
   archivefile.extractall()
    
   # Empirical cumulative distribution function figure
   ds = bb.load(glob.glob('BBOB2009pythondata/BIPOP-CMA-ES/ppdata_f0*_20.pickle'))
   figure()
   bb.pprldistr.plot(ds)
   bb.pprldistr.beautify() # resize the window to view whole figure

CAVEAT: the naming conventions in this module mix up ERT (an estimate 
of the expected running length) and run lengths. 

"""
from __future__ import absolute_import

import os
import numpy as np
import pickle, gzip
import matplotlib.pyplot as plt
from pdb import set_trace
from bbob_pproc import toolsstats, genericsettings, pproc
from bbob_pproc.ppfig import consecutiveNumbers, plotUnifLogXMarkers, saveFigure, logxticks

single_target_values = pproc.TargetValues((10., 1e-1, 1e-4, 1e-8))  # possibly changed in config
single_runlength_factors = [0.5, 1, 2, 4, 10] + [10**i for i in range(2, 12)]

caption_part_one = r"""%
     Empirical cumulative distribution functions (ECDF), plotting the fraction of 
     trials with an outcome not larger than the respective value on the $x$-axis. """
caption_left_fixed_targets = r"""%
     Left subplots: ECDF of the number of function evaluations (FEvals) divided by search space dimension $D$, 
     to fall below $\fopt+\Df$ with $\Df=10^{k}$, where $k$ is the first value in the legend. 
     The thick red line represents the most difficult target value $\fopt+10^{-8}$. """
caption_left_rlbased_targets = r"""%
     Left subplots: ECDF of number of function evaluations (FEvals) divided by search space dimension $D$, 
     to fall below $\fopt+\Df$ where \Df\ is the largest $\Df$-value $\ge10^{-8}$ 
     for which the best \ERT\ seen in the GECCO-BBOB-2009  
     was yet above $k\times\DIM$ evaluations, where $k$ is the first value in the legend. """
caption_right = r"""%
     Right subplots: ECDF of the 
     best achieved $\Df$ 
     for running times of #1
     function evaluations 
     (from right to left magenta-black-$2\times$cyan-$2\times$magenta and further cycling black-cyan-magenta) and final $\Df$-value (red). """
caption_wrap_up = r"""%
     Legends indicate for each target the number of functions that were solved in at
     least one trial.
     \Df\ and \textsf{Df} denote the difference to the optimal function value. """
caption_single_fixed = caption_part_one + caption_left_fixed_targets + caption_right + r"""
     Light brown lines in the background show ECDFs for $\Df=10^{-8}$ of all algorithms benchmarked during BBOB-2009.""" 
caption_single_rlbased = caption_part_one + caption_left_rlbased_targets + caption_right

# TODO: the method names in this module seem to be overly unclear or misleading and should be revised. 
   
refcolor = 'wheat'
nbperdecade = 1  # markers in x-axis decades in ecdfs

runlen_xlimits_max = None
runlen_xlimits_min = 1  # not in use, should become -0.5 in runlength case
# Used as a global to store the largest xmax and align the FV ECD figures.
fmax = None
evalfmax = runlen_xlimits_max  # is manipulated/stored in this module

# TODO: the target function values and the styles of the line only make sense
# together. Therefore we should either:
# 1. keep the targets as input argument and make rldStyles depend on them or
# 2. remove the targets as input argument and put them here.
rldStyles = ({'color': 'k', 'ls': '-'},
             {'color': 'c'},
             {'color': 'm', 'ls': '-'},
             {'color': 'r', 'linewidth': 3.},
             {'color': 'k'},
             {'color': 'c'},
             {'color': 'm'},
             {'color': 'r'},
             {'color': 'k'},
             {'color': 'c'},
             {'color': 'm'},
             {'color': 'r', 'linewidth': 3.})
rldUnsuccStyles = (
                   {'color': 'm', 'ls': '-'},
                   {'color': 'k', 'ls': '-'},
                   {'color': 'c'},
                   {'color': 'c'},
                   {'color': 'm', 'ls': '-'},
                   {'color': 'm'},
                   {'color': 'k', 'ls': '-'},
                   {'color': 'c'},
                   {'color': 'm', 'ls': '-'},
                   {'color': 'k'},
                   {'color': 'c', 'ls': '-'},
                   {'color': 'm'})  # should not be too short

previous_data_filename = 'pprldistr2009_1e-8.pickle.gz'
previous_data_filename = os.path.join(os.path.split(__file__)[0], previous_data_filename)
previous_algorithm_data_found = True
try:
    # cocofy(previous_data_filename)
    f = gzip.open(previous_data_filename,'r')
    dictprevalg = pickle.load(f)
except IOError, (errno, strerror):
    print "I/O error(%s): %s" % (errno, strerror)
    previous_algorithm_data_found = False
    print 'Could not find file: ', previous_data_filename
else:
    f.close()

def caption_single(max_evals_div_dim):
    caption = caption_single_rlbased if genericsettings.runlength_based_targets else caption_single_fixed 
    return caption.replace(r'#1', '$' + 'D, '.join([str(i) for i in single_runlength_factors[:7]]) + 'D,\dots$') 

def beautifyECDF():
    """Generic formatting of ECDF figures."""
    plt.ylim(-0.0, 1.01)  # was plt.ylim(-0.01, 1.01)
    plt.yticks(np.arange(0., 1.001, 0.2)) #, ('0.0', '', '0.5', '', '1.0'))
    plt.grid(True)
    xmin, xmax = plt.xlim()
    # plt.xlim(xmin=xmin*0.90)  # why this?
    c = plt.gca().get_children()
    for i in c: # TODO: we only want to extend ECDF lines...
        try:
            if i.get_drawstyle() == 'steps' and not i.get_linestyle() in ('', 'None'):
                xdata = i.get_xdata()
                ydata = i.get_ydata()
                if len(xdata) > 0:
                    #if xmin < min(xdata):
                    #    xdata = np.hstack((xmin, xdata))
                    #    ydata = np.hstack((ydata[0], ydata))
                    if xmax > max(xdata):
                        xdata = np.hstack((xdata, xmax))
                        ydata = np.hstack((ydata, ydata[-1]))
                    plt.setp(i, 'xdata', xdata, 'ydata', ydata)
            elif (i.get_drawstyle() == 'steps' and i.get_marker() != '' and
                  i.get_linestyle() in ('', 'None')):
                xdata = i.get_xdata()
                ydata = i.get_ydata()
                if len(xdata) > 0:
                    #if xmin < min(xdata):
                    #    minidx = np.ceil(np.log10(xmin) * nbperdecade)
                    #    maxidx = np.floor(np.log10(xdata[0]) * nbperdecade)
                    #    x = 10. ** (np.arange(minidx, maxidx + 1) / nbperdecade)
                    #    xdata = np.hstack((x, xdata))
                    #    ydata = np.hstack(([ydata[0]] * len(x), ydata))
                    if xmax > max(xdata):
                        minidx = np.ceil(np.log10(xdata[-1]) * nbperdecade)
                        maxidx = np.floor(np.log10(xmax) * nbperdecade)
                        x = 10. ** (np.arange(minidx, maxidx + 1) / nbperdecade)
                        xdata = np.hstack((xdata, x))
                        ydata = np.hstack((ydata, [ydata[-1]] * len(x)))
                    plt.setp(i, 'xdata', xdata, 'ydata', ydata)
        except (AttributeError, IndexError):
            pass

def beautifyRLD(evalfmax=None):
    """Format and save the figure of the run length distribution.
    
    After calling this function, changing the boundaries of the figure
    will not update the ticks and tick labels.
    
    """
    a = plt.gca()
    a.set_xscale('log')
    a.set_xlabel('log10 of FEvals / DIM')
    a.set_ylabel('proportion of trials')
    logxticks()
    if evalfmax:
        plt.xlim(xmax=evalfmax ** 1.05)
    plt.xlim(xmin=runlen_xlimits_min)
    beautifyECDF()

def beautifyFVD(isStoringXMax=False, ylabel=True):
    """Formats the figure of the run length distribution.

    This function is to be used with :py:func:`plotFVDistr`

    :param bool isStoringMaxF: if set to True, the first call
                               :py:func:`beautifyFVD` sets the global
                               :py:data:`fmax` and all subsequent call
                               will have the same maximum xlim
    :param bool ylabel: if True, y-axis will be labelled.

    """
    a = plt.gca()
    a.set_xscale('log')

    if isStoringXMax:
        global fmax
    else:
        fmax = None

    if not fmax:
        xmin, fmax = plt.xlim()
    plt.xlim(1.01e-8, fmax) # 1e-8 was 1.

    #axisHandle.invert_xaxis()
    a.set_xlabel('log10 of Df')  # / Dftarget
    if ylabel:
        a.set_ylabel('proportion of trials')
    logxticks()
    beautifyECDF()
    if not ylabel:
        a.set_yticklabels(())

def plotECDF(x, n=None, **plotArgs):
    """Plot an empirical cumulative distribution function.

    :param seq x: data
    :param int n: number of samples, if not provided len(x) is used
    :param plotArgs: optional keyword arguments provided to plot.

    :returns: handles of the plot elements.

    """
    if n is None:
        n = len(x)

    nx = len(x)
    if n == 0 or nx == 0:
        res = plt.plot([], [], **plotArgs)
    else:
        x = sorted(x) # do not sort in place
        x = np.hstack((x, x[-1]))
        y = np.hstack((np.arange(0., nx) / n, float(nx)/n))
        res = plotUnifLogXMarkers(x, y, nbperdecade=nbperdecade,
                                 drawstyle='steps', **plotArgs)
    return res

def plotERTDistr(dsList, target, **plotArgs):
    """This method is obsolete, should be removed? The replacement for simulated runlengths is in pprldmany? 
    Creates simulated run time distributions (it is not an ERT distribution) from a DataSetList.

    :keyword DataSet dsList: Input data sets
    :keyword dict target: target precision
    :keyword plotArgs: keyword arguments to pass to plot command

    :return: resulting plot.

    Details: calls ``plotECDF``. 
    
    """
    x = []
    nn = 0
    samplesize = 1000 # samplesize is at least 1000
    percentiles = 0.5 # could be anything...

    for i in dsList:
        #funcs.add(i.funcId)
        for j in i.evals:
            if j[0] <= target[i.funcId]:
                runlengthsucc = j[1:][np.isfinite(j[1:])]
                runlengthunsucc = i.maxevals[np.isnan(j[1:])]
                tmp = toolsstats.drawSP(runlengthsucc, runlengthunsucc,
                                       percentiles=percentiles,
                                       samplesize=samplesize)
                x.extend(tmp[1])
                break
        nn += samplesize
    res = plotECDF(x, nn, **plotArgs)

    return res

def plotRLDistr_old(dsList, target, **plotArgs):
    """Creates run length distributions from a sequence dataSetList.

    Labels of the line (for the legend) will be set automatically with
    the following format: %+d: %d/%d % (log10()
    

    :param DataSetList dsList: Input data sets
    :param dict or float target: target precision
    :param plotArgs: additional arguments passed to the plot command

    :returns: handles of the resulting plot.

    """
    x = []
    nn = 0
    fsolved = set()
    funcs = set()
    for i in dsList:
        funcs.add(i.funcId)
        try:
            target = target[i.funcId]  # TODO: this can only work for a single function, generally looks like a bug
            if not genericsettings.test:
                print 'target:', target
                print 'function:', i.funcId
                raise Exception('please check this, it looks like a bug')
        except TypeError:
            target = target
        tmp = i.detEvals((target, ))[0] / i.dim
        tmp = tmp[np.isnan(tmp) == False] # keep only success
        if len(tmp) > 0:
            fsolved.add(i.funcId)
        x.extend(tmp)
        nn += i.nbRuns()
    kwargs = plotArgs.copy()
    label = ''
    try:
        label += '%+d:' % (np.log10(target))
    except NameError:
        pass
    label += '%d/%d' % (len(fsolved), len(funcs))
    kwargs['label'] = kwargs.setdefault('label', label)
    res = plotECDF(x, nn, **kwargs)
    return res

def plotRLDistr(dsList, target, label, max_fun_evals=np.inf, **plotArgs):
    """Creates run length distributions from a sequence dataSetList.

    Labels of the line (for the legend) will be appended with the number
    of functions at least solved once. 
    

    :param DataSetList dsList: Input data sets
    :param target: a method that delivers target values like ``target((fun, dim))``
    :param str label: target value label to be displayed in the legend
    :param plotArgs: additional arguments passed to the plot command

    :returns: handles of the resulting plot.

    Details: ``target`` is a function taking a (function_number, dimension) pair 
    as input and returning a ``float``. It can be defined as 
    ``lambda fun_dim: targets(fun_dim)[j]`` returning the j-th element of 
    ``targets(fun_dim)``, where ``targets`` is an instance of 
    ``class pproc.TargetValues`` (see the ``pproc.TargetValues.__call__`` method).  
    
    """
    x = []
    nn = 0
    fsolved = set()
    funcs = set()
    for ds in dsList: # ds is a DataSet
        funcs.add(ds.funcId)
        tmp = ds.detEvals((target((ds.funcId, ds.dim)), ))[0] / ds.dim
        tmp = tmp[np.isnan(tmp) == False] # keep only success
        if len(tmp) > 0 and sum(tmp <= max_fun_evals):
            fsolved.add(ds.funcId)
        x.extend(tmp)
        nn += ds.nbRuns()
    kwargs = plotArgs.copy()
    label += ': %d/%d' % (len(fsolved), len(funcs))
    kwargs['label'] = kwargs.setdefault('label', label)
    res = plotECDF(x, nn, **kwargs)
    return res

def plotFVDistr(dsList, target, maxEvalsF=np.inf, **plotArgs):
    """Creates ECDF of final function values plot from a DataSetList.
    
    :param dsList: data sets
    :param dict or float target: used for the lower limit of the plot
    :param float maxEvalsF: maximum evaluations that "count"
    :param plotArgs: additional arguments passed to plot

    :returns: handle

    """
    x = []
    nn = 0
    for i in dsList:
        for j in i.funvals:
            if j[0] >= maxEvalsF * i.dim:
                break
        try:
            # tmp = j[1:].copy() / target[i.funcId]
            tmp = j[1:].copy()
        except TypeError:
            # tmp = j[1:].copy() / target
            tmp = j[1:].copy()
        # Integrate the negative values of df / ftarget together
        # this is to prevent problem when passing on a log scale
        # TODO: there should not be negative values, should there? 
        tmp[tmp <= 0] = min((min(tmp[tmp > 0]), [target]))
{'fraction_non_alphanumeric': 0.11963593244239336, 'fraction_numerical': 0.011665544979649393, 'mean_word_length': 2.335899080607227, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4644644', 'n_tokens_mistral': 10198, 'n_tokens_neox': 9670, 'n_words': 3135}