예제 #1
0
def prayer(configfile, nprays=0, savefile=None):
  """
  Implement prayer bead method to estimate parameter uncertainties.

  Parameters:
  -----------
  params: 1D-ndarray 
    Comment me, and all my friends.
  inonprior: 1D-ndarray
  stepsize: 1D-ndarray
  fit: a fits instance
  ncores: integer

  Notes:
  ------
  Believing in a prayer bead is a mere act of faith, we are scientists
  for god's sake!

  Modification History:
  ---------------------
  2012-10-29  patricio  Initial implementation.  [email protected]
  2013-09-03  patricio  Added documentation.  
  2014-05-19  patricio  Modified to work with MC3.
  """

  config = ConfigParser.SafeConfigParser()
  config.read([configfile])
  cfgsec = "MCMC" 

  data = mu.parray(config.get(cfgsec, 'data'))
  if isinstance(data[0], str):
    array = mu.readbin(data[0])
    data = array[0]
    if len(array) == 2:
      uncert = array[1]
    else:
      uncert = mu.parray(config.get(cfgsec, 'uncert'))

  params    = mu.parray(config.get(cfgsec, 'params'))
  if isinstance(params[0], str):
    array = mu.read2array(params[0])
    ninfo, nparams = np.shape(array)
    if ninfo == 7:                 # The priors
      prior    = array[4]
      priorlow = array[5]
      priorup  = array[6]
    else:
      try:
        prior     = mu.parray(config.get(cfgsec, 'prior'))
        priorlow  = mu.parray(config.get(cfgsec, 'priorlow'))
        priorup   = mu.parray(config.get(cfgsec, 'priorup'))
      except:
        prior   = np.zeros(nparams)  # Empty arrays
        priorup = priorlow = np.array([])
        iprior  = np.array([], int)

    if ninfo >= 4:                 # The stepsize
      stepsize = array[3]
    else:
      stepsize  = mu.parray(config.get(cfgsec, 'stepsize'))

    if ninfo >= 2:                 # The boundaries
      pmin     = array[1]
      pmax     = array[2]
    else:
      pmin      = mu.parray(config.get(cfgsec, 'pmin'))
      pmax      = mu.parray(config.get(cfgsec, 'pmax'))
    params = array[0]              # The initial guess

  indparams = mu.parray(config.get(cfgsec, 'indparams'))
  if indparams != [] and isinstance(indparams[0], str):
    indparams = mu.readbin(indparams[0])

  # Number of fitting parameters:
  nfree = np.sum(stepsize > 0)
  ifree  = np.where(stepsize > 0)[0] 
  iprior = np.where(priorlow > 0)[0] 

  # Get modeling function:
  func   = mu.parray(config.get(cfgsec, 'func'))
  if type(func) in [list, tuple, np.ndarray]:
    if len(func) == 3:
      sys.path.append(func[2])
    exec('from %s import %s as func'%(func[1], func[0]))
  elif not callable(func):
    return

  # Number of iterations:
  if nprays == 0:
    nprays = ndata
    shifts = np.arange(1, ndata)
  else:
    shifts = np.random.randint(0, ndata, nprays-1)

  # Allocate space for results:
  allfits = np.zeros((nprays, nfree))

  # Fit model:
  fitargs = (params, func, data, uncert, indparams, stepsize, pmin, pmax,
             (prior-params)[iprior], priorlow[iprior], priorup[iprior])
  chisq, dummy = mf.modelfit(params[ifree], args=fitargs)
  # Evaluate best model:
  fargs = [params] + indparams
  bestmodel = func(*fargs)
  chifactor = np.sqrt(chisq/(ndata-nfree))
  # Get residuals:
  residuals = data - bestmodel
  sigma     = np.copy(uncert*chifactor)

  allfits[0] = params[ifree]

  for i in np.arange(nprays-1):
    # Permuted data:
    pbdata = np.copy(bestmodel + np.roll(residuals, shifts[i]))
    # Permuted weights:
    pbunc  = np.roll(sigma, shifts[i])
    # Fitting parameters:
    pbfit = np.copy(params)[ifree]
    # Fit model:
    fitargs = (params, func, pbdata, pbunc, indparams, stepsize, pmin, pmax,
             (prior-params)[iprior], priorlow[iprior], priorup[iprior])
    chisq, dummy = mf.modelfit(pbfit, args=fitargs)
    allfits[i+1] = pbfit

  if savefile is not None:
    pbfile = open(savefile, "w")
    pbfile.write("Prayer-bead uncertainties:\n")
    pbunc = np.std(allfits,0)
    for j in np.arange(nfree):
      pbfile.write("%s  "%str(pbunc[j]))
    pbfile.close()

  return allfits, residuals
예제 #2
0
def main(comm):
  """
  Wrapper of modeling function for MCMC under MPI protocol.

  Modification History:
  ---------------------
  2014-04-19  patricio  Initial implementation.  [email protected]
  2014-06-25  patricio  Added support for inner-MPI loop.
  2014-10-23  patricio  Removed inner-MPI loop.
  """
  # Parse arguments:
  cparser = argparse.ArgumentParser(description=__doc__, add_help=False,
                         formatter_class=argparse.RawDescriptionHelpFormatter)
  # Add config file option:
  cparser.add_argument("-c", "--config_file", 
                       help="Configuration file", metavar="FILE")
  # Remaining_argv contains all other command-line-arguments:
  args, remaining_argv = cparser.parse_known_args()

  # Get parameters from configuration file:
  cfile = args.config_file
  if cfile:
    config = ConfigParser.SafeConfigParser()
    config.read([cfile])
    defaults = dict(config.items("MCMC"))
  else:
    defaults = {}
  parser = argparse.ArgumentParser(parents=[cparser])
  parser.add_argument("-f", "--func",      dest="func",     type=mu.parray, 
                                           action="store",  default=None)
  parser.add_argument("-i", "--indparams", dest="indparams", type=mu.parray, 
                                           action="store",   default=[])
  parser.set_defaults(**defaults)
  args2, unknown = parser.parse_known_args(remaining_argv)

  # Add path to func:
  if len(args2.func) == 3:
    sys.path.append(args2.func[2])

  exec('from {:s} import {:s} as func'.format(args2.func[1], args2.func[0]))
  # Get indparams from configuration file:
  if args2.indparams != [] and os.path.isfile(args2.indparams[0]):
    indparams = mu.readbin(args2.indparams[0])


  # Get the number of parameters and iterations from MPI:
  array1 = np.zeros(2, np.int)
  mu.comm_bcast(comm, array1)
  npars, niter = array1

  # Allocate array to receive parameters from MPI:
  params = np.zeros(npars, np.double)

  # Main MCMC Loop:
  while niter >= 0:
    # Receive parameters from MCMC:
    mu.comm_scatter(comm, params)

    # Evaluate model:
    fargs = [params] + indparams  # List of function's arguments
    model = func(*fargs)

    # Send resutls:
    mu.comm_gather(comm, model, MPI.DOUBLE)
    niter -= 1

  # Close communications and disconnect:
  mu.comm_disconnect(comm)
예제 #3
0
def prayer(configfile, nprays=0, savefile=None):
    """
  Implement prayer bead method to estimate parameter uncertainties.

  Parameters:
  -----------
  params: 1D-ndarray 
    Comment me, and all my friends.
  inonprior: 1D-ndarray
  stepsize: 1D-ndarray
  fit: a fits instance
  ncores: integer

  Notes:
  ------
  Believing in a prayer bead is a mere act of faith, we are scientists
  for god's sake!

  Modification History:
  ---------------------
  2012-10-29  patricio  Initial implementation.  [email protected]
  2013-09-03  patricio  Added documentation.  
  2014-05-19  patricio  Modified to work with MC3.
  """

    config = ConfigParser.SafeConfigParser()
    config.read([configfile])
    cfgsec = "MCMC"

    data = mu.parray(config.get(cfgsec, 'data'))
    if isinstance(data[0], str):
        array = mu.readbin(data[0])
        data = array[0]
        if len(array) == 2:
            uncert = array[1]
        else:
            uncert = mu.parray(config.get(cfgsec, 'uncert'))

    params = mu.parray(config.get(cfgsec, 'params'))
    if isinstance(params[0], str):
        array = mu.read2array(params[0])
        ninfo, nparams = np.shape(array)
        if ninfo == 7:  # The priors
            prior = array[4]
            priorlow = array[5]
            priorup = array[6]
        else:
            try:
                prior = mu.parray(config.get(cfgsec, 'prior'))
                priorlow = mu.parray(config.get(cfgsec, 'priorlow'))
                priorup = mu.parray(config.get(cfgsec, 'priorup'))
            except:
                prior = np.zeros(nparams)  # Empty arrays
                priorup = priorlow = np.array([])
                iprior = np.array([], int)

        if ninfo >= 4:  # The stepsize
            stepsize = array[3]
        else:
            stepsize = mu.parray(config.get(cfgsec, 'stepsize'))

        if ninfo >= 2:  # The boundaries
            pmin = array[1]
            pmax = array[2]
        else:
            pmin = mu.parray(config.get(cfgsec, 'pmin'))
            pmax = mu.parray(config.get(cfgsec, 'pmax'))
        params = array[0]  # The initial guess

    indparams = mu.parray(config.get(cfgsec, 'indparams'))
    if indparams != [] and isinstance(indparams[0], str):
        indparams = mu.readbin(indparams[0])

    # Number of fitting parameters:
    nfree = np.sum(stepsize > 0)
    ifree = np.where(stepsize > 0)[0]
    iprior = np.where(priorlow > 0)[0]

    # Get modeling function:
    func = mu.parray(config.get(cfgsec, 'func'))
    if type(func) in [list, tuple, np.ndarray]:
        if len(func) == 3:
            sys.path.append(func[2])
        exec('from %s import %s as func' % (func[1], func[0]))
    elif not callable(func):
        return

    # Number of iterations:
    if nprays == 0:
        nprays = ndata
        shifts = np.arange(1, ndata)
    else:
        shifts = np.random.randint(0, ndata, nprays - 1)

    # Allocate space for results:
    allfits = np.zeros((nprays, nfree))

    # Fit model:
    fitargs = (params, func, data, uncert, indparams, stepsize, pmin, pmax,
               (prior - params)[iprior], priorlow[iprior], priorup[iprior])
    chisq, dummy = mf.modelfit(params[ifree], args=fitargs)
    # Evaluate best model:
    fargs = [params] + indparams
    bestmodel = func(*fargs)
    chifactor = np.sqrt(chisq / (ndata - nfree))
    # Get residuals:
    residuals = data - bestmodel
    sigma = np.copy(uncert * chifactor)

    allfits[0] = params[ifree]

    for i in np.arange(nprays - 1):
        # Permuted data:
        pbdata = np.copy(bestmodel + np.roll(residuals, shifts[i]))
        # Permuted weights:
        pbunc = np.roll(sigma, shifts[i])
        # Fitting parameters:
        pbfit = np.copy(params)[ifree]
        # Fit model:
        fitargs = (params, func, pbdata, pbunc, indparams, stepsize, pmin,
                   pmax, (prior - params)[iprior], priorlow[iprior],
                   priorup[iprior])
        chisq, dummy = mf.modelfit(pbfit, args=fitargs)
        allfits[i + 1] = pbfit

    if savefile is not None:
        pbfile = open(savefile, "w")
        pbfile.write("Prayer-bead uncertainties:\n")
        pbunc = np.std(allfits, 0)
        for j in np.arange(nfree):
            pbfile.write("%s  " % str(pbunc[j]))
        pbfile.close()

    return allfits, residuals
예제 #4
0
def main():
    """
  Multi-Core Markov-Chain Monte Carlo (MC cubed)

  This code calls MCMC to work under an MPI multiprocessor protocol or
  single-thread mode.  When using MPI it will launch one CPU per MCMC chain
  to work in parallel.

  Parameters:
  -----------
  cfile: String
     Filename of a configuration file.

  Modification History:
  ---------------------
  2014-04-19  patricio  Initial implementation.  [email protected]
  2014-05-04  patricio  Added cfile argument for Interpreter support.
  2014-05-26  patricio  Re-engineered the MPI support.
  2014-06-26  patricio  Fixed bug with copy when uncert is None.
  2014-09-14  patricio  Write/read now binary files.
  2014-10-23  patricio  Added support for func hack.
  2015-02-04  patricio  Added resume argument.
  2015-05-15  patricio  Added logfile argument.
  """

    # Parse the config file from the command line:
    cparser = argparse.ArgumentParser(
        description=__doc__,
        add_help=False,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    # Add config file option:
    cparser.add_argument("-c",
                         "--config_file",
                         help="Configuration file",
                         metavar="FILE")
    # Remaining_argv contains all other command-line-arguments:
    args, remaining_argv = cparser.parse_known_args()

    # Take configuration file from command-line:
    cfile = args.config_file

    # Incorrect configuration file name:
    if cfile is not None and not os.path.isfile(cfile):
        mu.error("Configuration file: '{:s}' not found.".format(cfile))
    if cfile:
        config = ConfigParser.SafeConfigParser()
        config.read([cfile])
        defaults = dict(config.items("MCMC"))
    else:
        defaults = {}

    # Parser for the MCMC arguments:
    parser = argparse.ArgumentParser(parents=[cparser])

    # MCMC Options:
    group = parser.add_argument_group("MCMC General Options")
    group.add_argument("-n",
                       "--numit",
                       dest="numit",
                       help="Number of MCMC samples [default: %(default)s]",
                       type=eval,
                       action="store",
                       default=100)
    group.add_argument("-x",
                       "--nchains",
                       dest="nchains",
                       help="Number of chains [default: %(default)s]",
                       type=int,
                       action="store",
                       default=10)
    group.add_argument("-w",
                       "--walk",
                       dest="walk",
                       help="Random walk algorithm [default: %(default)s]",
                       type=str,
                       action="store",
                       default="demc",
                       choices=('demc', 'mrw'))
    group.add_argument("--wlikelihood",
                       dest="wlike",
                       help="Calculate the likelihood in a wavelet base "
                       "[default: %(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument("--leastsq",
                       dest="leastsq",
                       help="Perform a least-square minimization before the "
                       "MCMC run [default: %(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument(
        "--chisq_scale",
        dest="chisqscale",
        help="Scale the data uncertainties such that the reduced "
        "chi-squared = 1. [default: %(default)s]",
        type=eval,
        action="store",
        default=False)
    group.add_argument("-g",
                       "--gelman_rubin",
                       dest="grtest",
                       help="Run Gelman-Rubin test [default: %(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument("-b",
                       "--burnin",
                       help="Number of burn-in iterations (per chain) "
                       "[default: %(default)s]",
                       dest="burnin",
                       type=eval,
                       action="store",
                       default=0)
    group.add_argument(
        "-t",
        "--thinning",
        dest="thinning",
        help="Chains thinning factor (use every thinning-th "
        "iteration) for GR test and plots [default: %(default)s]",
        type=int,
        action="store",
        default=1)
    group.add_argument(
        "--plots",
        dest="plots",
        help="If True plot parameter traces, pairwise posteriors, "
        "and marginal posterior histograms [default: %(default)s]",
        type=eval,
        action="store",
        default=False)
    group.add_argument("-o",
                       "--save_file",
                       dest="savefile",
                       help="Output filename to store the parameter posterior "
                       "distributions  [default: %(default)s]",
                       type=str,
                       action="store",
                       default="output.npy")
    group.add_argument("--savemodel",
                       dest="savemodel",
                       help="Output filename to store the evaluated models  "
                       "[default: %(default)s]",
                       type=str,
                       action="store",
                       default=None)
    group.add_argument("--mpi",
                       dest="mpi",
                       help="Run under MPI multiprocessing [default: "
                       "%(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument("--resume",
                       dest="resume",
                       help="If True, resume a previous run (load output) "
                       "[default: %(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument("--rms",
                       dest="rms",
                       help="If True, calculate the RMS of (data-bestmodel) "
                       "[default: %(default)s]",
                       type=eval,
                       action="store",
                       default=False)
    group.add_argument("--logfile",
                       dest="logfile",
                       help="Log file.",
                       action="store",
                       default=None)
    group.add_argument("-T",
                       "--tracktime",
                       dest="tractime",
                       action="store_true")
    # Fitting-parameter Options:
    group = parser.add_argument_group("Fitting-function Options")
    group.add_argument("-f",
                       "--func",
                       dest="func",
                       help="List of strings with the function name, module "
                       "name, and path-to-module [required]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-p",
                       "--params",
                       dest="params",
                       help="Filename or list of initial-guess model-fitting "
                       "parameter [required]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-m",
                       "--pmin",
                       dest="pmin",
                       help="Filename or list of parameter lower boundaries "
                       "[default: -inf]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-M",
                       "--pmax",
                       dest="pmax",
                       help="Filename or list of parameter upper boundaries "
                       "[default: +inf]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-s",
                       "--stepsize",
                       dest="stepsize",
                       help="Filename or list with proposal jump scale "
                       "[default: 0.1*params]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-i",
                       "--indparams",
                       dest="indparams",
                       help="Filename or list with independent parameters for "
                       "func [default: None]",
                       type=mu.parray,
                       action="store",
                       default=[])
    # Data Options:
    group = parser.add_argument_group("Data Options")
    group.add_argument("-d",
                       "--data",
                       dest="data",
                       help="Filename or list of the data being fitted "
                       "[required]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("-u",
                       "--uncertainties",
                       dest="uncert",
                       help="Filemane or list with the data uncertainties "
                       "[default: ones]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("--prior",
                       dest="prior",
                       help="Filename or list with parameter prior estimates "
                       "[default: %(default)s]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("--priorlow",
                       dest="priorlow",
                       help="Filename or list with prior lower uncertainties "
                       "[default: %(default)s]",
                       type=mu.parray,
                       action="store",
                       default=None)
    group.add_argument("--priorup",
                       dest="priorup",
                       help="Filename or list with prior upper uncertainties "
                       "[default: %(default)s]",
                       type=mu.parray,
                       action="store",
                       default=None)

    # Set the defaults from the configuration file:
    parser.set_defaults(**defaults)
    # Set values from command line:
    args2, unknown = parser.parse_known_args(remaining_argv)

    # Unpack configuration-file/command-line arguments:
    numit = args2.numit
    nchains = args2.nchains
    walk = args2.walk
    wlike = args2.wlike
    leastsq = args2.leastsq
    chisqscale = args2.chisqscale
    grtest = args2.grtest
    burnin = args2.burnin
    thinning = args2.thinning
    plots = args2.plots
    savefile = args2.savefile
    savemodel = args2.savemodel
    mpi = args2.mpi
    resume = args2.resume
    tracktime = args2.tractime
    logfile = args2.logfile
    rms = args2.rms

    func = args2.func
    params = args2.params
    pmin = args2.pmin
    pmax = args2.pmax
    stepsize = args2.stepsize
    indparams = args2.indparams

    data = args2.data
    uncert = args2.uncert
    prior = args2.prior
    priorup = args2.priorup
    priorlow = args2.priorlow

    nprocs = nchains

    # Open a log FILE if requested:
    if logfile is not None:
        log = open(logfile, "w")
    else:
        log = None

    # Handle arguments:
    if params is None:
        mu.error("'params' is a required argument.", log)
    elif isinstance(params[0], str):
        # If params is a filename, unpack:
        if not os.path.isfile(params[0]):
            mu.error("'params' file not found.", log)
        array = mu.read2array(params[0])
        # Array size:
        ninfo, ndata = np.shape(array)
        if ninfo == 7:  # The priors
            prior = array[4]
            priorlow = array[5]
            priorup = array[6]
        if ninfo >= 4:  # The stepsize
            stepsize = array[3]
        if ninfo >= 2:  # The boundaries
            pmin = array[1]
            pmax = array[2]
        params = array[0]  # The initial guess

    # Check for pmin and pmax files if not read before:
    if pmin is not None and isinstance(pmin[0], str):
        if not os.path.isfile(pmin[0]):
            mu.error("'pmin' file not found.", log)
        pmin = mu.read2array(pmin[0])[0]

    if pmax is not None and isinstance(pmax[0], str):
        if not os.path.isfile(pmax[0]):
            mu.error("'pmax' file not found.", log)
        pmax = mu.read2array(pmax[0])[0]

    # Stepsize:
    if stepsize is not None and isinstance(stepsize[0], str):
        if not os.path.isfile(stepsize[0]):
            mu.error("'stepsize' file not found.", log)
        stepsize = mu.read2array(stepsize[0])[0]

    # Priors:
    if prior is not None and isinstance(prior[0], str):
        if not os.path.isfile(prior[0]):
            mu.error("'prior' file not found.", log)
        prior = mu.read2array(prior[0])[0]

    if priorlow is not None and isinstance(priorlow[0], str):
        if not os.path.isfile(priorlow[0]):
            mu.error("'priorlow' file not found.", log)
        priorlow = mu.read2array(priorlow[0])[0]

    if priorup is not None and isinstance(priorup[0], str):
        if not os.path.isfile(priorup[0]):
            mu.error("'priorup' file not found.", log)
        priorup = mu.read2array(priorup[0])[0]

    # Process the data and uncertainties:
    if data is None:
        mu.error("'data' is a required argument.", log)
    # If params is a filename, unpack:
    elif isinstance(data[0], str):
        if not os.path.isfile(data[0]):
            mu.error("'data' file not found.", log)
        array = mu.readbin(data[0])
        data = array[0]
        if len(array) == 2:
            uncert = array[1]

    if uncert is not None and isinstance(uncert[0], str):
        if not os.path.isfile(uncert[0]):
            mu.error("'uncert' file not found.", log)
        uncert = mu.readbin(uncert[0])[0]

    # Process the independent parameters:
    if indparams != [] and isinstance(indparams[0], str):
        if not os.path.isfile(indparams[0]):
            mu.error("'indparams' file not found.", log)
        indparams = mu.readbin(indparams[0])

    if tracktime:
        start_mpi = timeit.default_timer()

    if mpi:
        # Checks for mpi4py:
        try:
            from mpi4py import MPI
        except:
            mu.error("Attempted to use MPI, but mpi4py is not installed.", log)

        # Get source dir:
        mcfile = mc.__file__
        iright = mcfile.rfind('/')
        if iright == -1:
            sdir = "."
        else:
            sdir = mcfile[:iright]

        # Hack func here:
        funccall = sdir + "/func.py"
        if func[0] == 'hack':
            funccall = func[2] + "/" + func[1] + ".py"

        # Call wrapper of model function:
        args = [funccall, "-c" + cfile] + remaining_argv
        comm = MPI.COMM_SELF.Spawn(sys.executable, args=args, maxprocs=nprocs)
    else:
        comm = None

    # Use a copy of uncert to avoid overwrite on it.
    if uncert is not None:
        unc = np.copy(uncert)
    else:
        unc = None

    if tracktime:
        start_loop = timeit.default_timer()
    # Run the MCMC:
    allp, bp = mc.mcmc(data, unc, func, indparams, params, pmin, pmax,
                       stepsize, prior, priorlow, priorup, numit, nchains,
                       walk, wlike, leastsq, chisqscale, grtest, burnin,
                       thinning, plots, savefile, savemodel, comm, resume, log,
                       rms)

    if tracktime:
        stop = timeit.default_timer()

    # Close communications and disconnect:
    if mpi:
        mu.comm_disconnect(comm)

    #if bench == True:
    if tracktime:
        mu.msg(1, "Total execution time:   %10.6f s" % (stop - start), log)
    if log is not None:
        log.close()
예제 #5
0
def main():
  """
  Multi-Core Markov-Chain Monte Carlo (MC cubed)

  This code calls MCMC to work under an MPI multiprocessor protocol or
  single-thread mode.  When using MPI it will launch one CPU per MCMC chain
  to work in parallel.

  Parameters:
  -----------
  cfile: String
     Filename of a configuration file.

  Modification History:
  ---------------------
  2014-04-19  patricio  Initial implementation.  [email protected]
  2014-05-04  patricio  Added cfile argument for Interpreter support.
  2014-05-26  patricio  Re-engineered the MPI support.
  2014-06-26  patricio  Fixed bug with copy when uncert is None.
  2014-09-14  patricio  Write/read now binary files.
  2014-10-23  patricio  Added support for func hack.
  2015-02-04  patricio  Added resume argument.
  2015-05-15  patricio  Added logfile argument.
  """

  # Parse the config file from the command line:
  cparser = argparse.ArgumentParser(description=__doc__, add_help=False,
                         formatter_class=argparse.RawDescriptionHelpFormatter)
  # Add config file option:
  cparser.add_argument("-c", "--config_file",
                       help="Configuration file", metavar="FILE")
  # Remaining_argv contains all other command-line-arguments:
  args, remaining_argv = cparser.parse_known_args()

  # Take configuration file from command-line:
  cfile = args.config_file

  # Incorrect configuration file name:
  if cfile is not None and not os.path.isfile(cfile):
    mu.error("Configuration file: '{:s}' not found.".format(cfile))
  if cfile:
    config = ConfigParser.SafeConfigParser()
    config.read([cfile])
    defaults = dict(config.items("MCMC"))
  else:
    defaults = {}

  # Parser for the MCMC arguments:
  parser = argparse.ArgumentParser(parents=[cparser])

  # MCMC Options:
  group = parser.add_argument_group("MCMC General Options")
  group.add_argument("-n", "--numit",
                     dest="numit",
                     help="Number of MCMC samples [default: %(default)s]",
                     type=eval,   action="store", default=100)
  group.add_argument("-x", "--nchains",
                     dest="nchains",
                     help="Number of chains [default: %(default)s]",
                     type=int,   action="store", default=10)
  group.add_argument("-w", "--walk",
                     dest="walk",
                     help="Random walk algorithm [default: %(default)s]",
                     type=str,   action="store", default="demc",
                     choices=('demc', 'mrw'))
  group.add_argument(      "--wlikelihood",
                     dest="wlike",
                     help="Calculate the likelihood in a wavelet base "
                     "[default: %(default)s]",
                     type=eval,  action="store", default=False)
  group.add_argument(      "--leastsq",
                     dest="leastsq",
                     help="Perform a least-square minimization before the "
                     "MCMC run [default: %(default)s]",
                     type=eval,  action="store", default=False)
  group.add_argument(     "--chisq_scale",
                     dest="chisqscale",
                     help="Scale the data uncertainties such that the reduced "
                     "chi-squared = 1. [default: %(default)s]",
                     type=eval,  action="store", default=False)
  group.add_argument("-g", "--gelman_rubin",
                     dest="grtest",
                     help="Run Gelman-Rubin test [default: %(default)s]",
                     type=eval,  action="store", default=False)
  group.add_argument("-b", "--burnin",
                     help="Number of burn-in iterations (per chain) "
                     "[default: %(default)s]",
                     dest="burnin",
                     type=eval,   action="store", default=0)
  group.add_argument("-t", "--thinning",
                     dest="thinning",
                     help="Chains thinning factor (use every thinning-th "
                     "iteration) for GR test and plots [default: %(default)s]",
                     type=int,     action="store",  default=1)
  group.add_argument(      "--plots",
                     dest="plots",
                     help="If True plot parameter traces, pairwise posteriors, "
                     "and marginal posterior histograms [default: %(default)s]",
                     type=eval,    action="store",  default=False)
  group.add_argument("-o", "--save_file",
                     dest="savefile",
                     help="Output filename to store the parameter posterior "
                     "distributions  [default: %(default)s]",
                     type=str,     action="store",  default="output.npy")
  group.add_argument(      "--savemodel",
                     dest="savemodel",
                     help="Output filename to store the evaluated models  "
                     "[default: %(default)s]",
                     type=str,     action="store",  default=None)
  group.add_argument(       "--mpi",
                     dest="mpi",
                     help="Run under MPI multiprocessing [default: "
                     "%(default)s]",
                     type=eval,  action="store", default=False)
  group.add_argument(      "--resume",
                     dest="resume",
                     help="If True, resume a previous run (load output) "
                     "[default: %(default)s]",
                     type=eval,    action="store",  default=False)
  group.add_argument(      "--rms",
                     dest="rms",
                     help="If True, calculate the RMS of (data-bestmodel) "
                     "[default: %(default)s]",
                     type=eval,    action="store",  default=False)
  group.add_argument(      "--logfile",
                     dest="logfile",
                     help="Log file.",
                     action="store", default=None)
  group.add_argument("-T", "--tracktime", dest="tractime", action="store_true")
  # Fitting-parameter Options:
  group = parser.add_argument_group("Fitting-function Options")
  group.add_argument("-f", "--func",
                     dest="func",
                     help="List of strings with the function name, module "
                     "name, and path-to-module [required]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-p", "--params",
                     dest="params",
                     help="Filename or list of initial-guess model-fitting "
                     "parameter [required]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-m", "--pmin",
                     dest="pmin",
                     help="Filename or list of parameter lower boundaries "
                     "[default: -inf]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-M", "--pmax",
                     dest="pmax",
                     help="Filename or list of parameter upper boundaries "
                     "[default: +inf]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-s", "--stepsize",
                     dest="stepsize",
                     help="Filename or list with proposal jump scale "
                     "[default: 0.1*params]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-i", "--indparams",
                     dest="indparams",
                     help="Filename or list with independent parameters for "
                     "func [default: None]",
                     type=mu.parray,  action="store", default=[])
  # Data Options:
  group = parser.add_argument_group("Data Options")
  group.add_argument("-d", "--data",
                     dest="data",
                     help="Filename or list of the data being fitted "
                     "[required]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument("-u", "--uncertainties",
                     dest="uncert",
                     help="Filemane or list with the data uncertainties "
                     "[default: ones]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument(     "--prior",
                     dest="prior",
                     help="Filename or list with parameter prior estimates "
                     "[default: %(default)s]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument(     "--priorlow",
                     dest="priorlow",
                     help="Filename or list with prior lower uncertainties "
                     "[default: %(default)s]",
                     type=mu.parray,  action="store", default=None)
  group.add_argument(     "--priorup",
                     dest="priorup",
                     help="Filename or list with prior upper uncertainties "
                     "[default: %(default)s]",
                     type=mu.parray,  action="store", default=None)

  # Set the defaults from the configuration file:
  parser.set_defaults(**defaults)
  # Set values from command line:
  args2, unknown = parser.parse_known_args(remaining_argv)

  # Unpack configuration-file/command-line arguments:
  numit      = args2.numit
  nchains    = args2.nchains
  walk       = args2.walk
  wlike      = args2.wlike
  leastsq    = args2.leastsq
  chisqscale = args2.chisqscale
  grtest     = args2.grtest
  burnin     = args2.burnin
  thinning   = args2.thinning
  plots      = args2.plots
  savefile   = args2.savefile
  savemodel  = args2.savemodel
  mpi        = args2.mpi
  resume     = args2.resume
  tracktime  = args2.tractime
  logfile    = args2.logfile
  rms        = args2.rms

  func      = args2.func
  params    = args2.params
  pmin      = args2.pmin
  pmax      = args2.pmax
  stepsize  = args2.stepsize
  indparams = args2.indparams

  data     = args2.data
  uncert   = args2.uncert
  prior    = args2.prior
  priorup  = args2.priorup
  priorlow = args2.priorlow

  nprocs   = nchains

  # Open a log FILE if requested:
  if logfile is not None:
    log = open(logfile, "w")
  else:
    log = None

  # Handle arguments:
  if params is None:
    mu.error("'params' is a required argument.", log)
  elif isinstance(params[0], str):
    # If params is a filename, unpack:
    if not os.path.isfile(params[0]):
      mu.error("'params' file not found.", log)
    array = mu.read2array(params[0])
    # Array size:
    ninfo, ndata = np.shape(array)
    if ninfo == 7:                 # The priors
      prior    = array[4]
      priorlow = array[5]
      priorup  = array[6]
    if ninfo >= 4:                 # The stepsize
      stepsize = array[3]
    if ninfo >= 2:                 # The boundaries
      pmin     = array[1]
      pmax     = array[2]
    params = array[0]              # The initial guess

  # Check for pmin and pmax files if not read before:
  if pmin is not None and isinstance(pmin[0], str):
    if not os.path.isfile(pmin[0]):
      mu.error("'pmin' file not found.", log)
    pmin = mu.read2array(pmin[0])[0]

  if pmax is not None and isinstance(pmax[0], str):
    if not os.path.isfile(pmax[0]):
      mu.error("'pmax' file not found.", log)
    pmax = mu.read2array(pmax[0])[0]

  # Stepsize:
  if stepsize is not None and isinstance(stepsize[0], str):
    if not os.path.isfile(stepsize[0]):
      mu.error("'stepsize' file not found.", log)
    stepsize = mu.read2array(stepsize[0])[0]

  # Priors:
  if prior    is not None and isinstance(prior[0], str):
    if not os.path.isfile(prior[0]):
      mu.error("'prior' file not found.", log)
    prior    = mu.read2array(prior   [0])[0]

  if priorlow is not None and isinstance(priorlow[0], str):
    if not os.path.isfile(priorlow[0]):
      mu.error("'priorlow' file not found.", log)
    priorlow = mu.read2array(priorlow[0])[0]

  if priorup  is not None and isinstance(priorup[0], str):
    if not os.path.isfile(priorup[0]):
      mu.error("'priorup' file not found.", log)
    priorup  = mu.read2array(priorup [0])[0]

  # Process the data and uncertainties:
  if data is None:
     mu.error("'data' is a required argument.", log)
  # If params is a filename, unpack:
  elif isinstance(data[0], str):
    if not os.path.isfile(data[0]):
      mu.error("'data' file not found.", log)
    array = mu.readbin(data[0])
    data = array[0]
    if len(array) == 2:
      uncert = array[1]

  if uncert is not None and isinstance(uncert[0], str):
    if not os.path.isfile(uncert[0]):
      mu.error("'uncert' file not found.", log)
    uncert = mu.readbin(uncert[0])[0]

  # Process the independent parameters:
  if indparams != [] and isinstance(indparams[0], str):
    if not os.path.isfile(indparams[0]):
      mu.error("'indparams' file not found.", log)
    indparams = mu.readbin(indparams[0])

  if tracktime:
    start_mpi = timeit.default_timer()

  if mpi:
    # Checks for mpi4py:
    try:
      from mpi4py import MPI
    except:
      mu.error("Attempted to use MPI, but mpi4py is not installed.", log)

    # Get source dir:
    mcfile = mc.__file__
    iright = mcfile.rfind('/')
    if iright == -1:
      sdir = "."
    else:
      sdir = mcfile[:iright]

    # Hack func here:
    funccall = sdir + "/func.py"
    if func[0] == 'hack':
      funccall = func[2] + "/" + func[1] + ".py"

    # Call wrapper of model function:
    args = [funccall, "-c" + cfile] + remaining_argv
    comm = MPI.COMM_SELF.Spawn(sys.executable, args=args, maxprocs=nprocs)
  else:
    comm = None

  # Use a copy of uncert to avoid overwrite on it.
  if uncert is not None:
    unc = np.copy(uncert)
  else:
    unc = None

  if tracktime:
    start_loop = timeit.default_timer()
  # Run the MCMC:
  allp, bp = mc.mcmc(data, unc, func, indparams,
                     params, pmin, pmax, stepsize,
                     prior, priorlow, priorup,
                     numit, nchains, walk, wlike,
                     leastsq, chisqscale, grtest, burnin,
                     thinning, plots, savefile, savemodel,
                     comm, resume, log, rms)

  if tracktime:
    stop = timeit.default_timer()

  # Close communications and disconnect:
  if mpi:
    mu.comm_disconnect(comm)

  #if bench == True:
  if tracktime:
    mu.msg(1, "Total execution time:   %10.6f s"%(stop - start), log)
  if log is not None:
    log.close()