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. """ # 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( "--grexit", dest="grexit", help="Exit the MCMC loop if the MCMC satisfies the GR " "test two consecutive times [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 grexit = args2.grexit 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 '{:s}' not found.".format(params[0]), log) array = mu.loadascii(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 '{:s}' not found.".format(pmin[0]), log) pmin = mu.loadascii(pmin[0])[0] if pmax is not None and isinstance(pmax[0], str): if not os.path.isfile(pmax[0]): mu.error("pmax file '{:s}' not found.".format(pmax[0]), log) pmax = mu.loadascii(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 '{:s}' not found.".format(stepsize[0]), log) stepsize = mu.loadascii(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 '{:s}' not found.".format(prior[0]), log) prior = mu.loadascii(prior [0])[0] if priorlow is not None and isinstance(priorlow[0], str): if not os.path.isfile(priorlow[0]): mu.error("priorlow file '{:s}' not found.".format(priorlow[0]), log) priorlow = mu.loadascii(priorlow[0])[0] if priorup is not None and isinstance(priorup[0], str): if not os.path.isfile(priorup[0]): mu.error("priorup file '{:s}' not found.".format(priorup[0]), log) priorup = mu.loadascii(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 '{:s}' not found.".format(data[0]), log) array = mu.loadbin(data[0]) data = array[0] if len(array) == 2: uncert = array[1] if uncert is None: mu.error("'uncert' is a required argument.", log) elif isinstance(uncert[0], str): if not os.path.isfile(uncert[0]): mu.error("uncert file '{:s}' not found.".format(uncert[0]), log) uncert = mu.loadbin(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 '{:s}' not found.".format(indparams[0]), log) indparams = mu.loadbin(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, grexit, 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()
def prayer(configfile, nprays=0, savefile=None): """ Implement a Prayer-bead method to estimate parameter uncertainties. Parameters ---------- configfile: String Configuration file name nprays: Integer Number of prayer-bead shifts. If nprays==0, set to the number of data points. savefile: String Name of file where to store the prayer-bead results. Notes ----- Believing in a prayer bead is a mere act of faith, we are scientists for god's sake! """ config = ConfigParser.SafeConfigParser() config.read([configfile]) cfgsec = "MCMC" data = mu.parray(config.get(cfgsec, 'data')) if isinstance(data[0], str): array = mu.loadbin(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.loadascii(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.loadbin(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
def main(comm): """ Wrapper of modeling function for MCMC under MPI protocol. """ # 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.loadbin(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) # Check for the MCMC-end flag: if params[0] == np.inf: break # 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)