sho-lesson/expe.py
2019-01-22 10:57:19 +01:00

195 lines
5.2 KiB
Python

import os
import stat
import random
import itertools
import subprocess
class HowLocal:
def __init__(self, doe, job, directory):
self.doe = doe
self.job = job
self.dir = directory # TODO
def one_by_one(self):
for params in self.doe:
cmd = self.job(params)
subprocess.run(cmd, shell=True)
def all(self):
queue = []
for params in self.doe:
cmd = self.job(params)
p = subprocess.Popen(cmd, shell=True)
queue.append(p)
for p in queue:
p.wait()
def batch(self, batch_size = None):
if batch_size is None:
batch_size = os.cpu_count()
nb = 0
queue = []
for params in self.doe:
if nb >= batch_size:
for p in queue:
p.wait()
queue.clear()
nb = 0
else:
cmd = self.job(params)
p = subprocess.Popen(cmd, shell=True)
queue.append(p)
nb += 1
# in case batch_size = float("inf")
for p in queue:
p.wait()
class HowGridEngine:
def __init__(self, doe, job, directory):
self.doe = doe
self.job = job
self.dir = directory
def qsub(self, *flags, **kwargs):
for params in self.doe:
# Handy name
name = "_".join([str(params[k]) for k in params])
# Add default qsub name if not asked.
if "N" not in kwargs:
kwargs["N"] = name
# Default output in results directory.
if "o" not in kwargs:
kwargs["o"] = self.dir
if "e" not in kwargs:
kwargs["e"] = self.dir
qsub = ["qsub"]
# Add qsub flags
for arg in flags:
qsub.append("-{}".format(arg))
# Add qsub options
for k in kwargs:
qsub.append("-{opt} {val}".format(opt=k, val = kwargs[k]))
# Get the user's command.
cmd = self.job(params)
# Put it into a script.
script=os.path.join(self.dir,"run_{}.sh".format(name))
with open(script,'w') as fd:
fd.write("#!/bin/sh\n") # Shebang
fd.write(""">&2 echo "{}"\n""".format(cmd) ) # Log
fd.write("{}\n".format(cmd)) # Actual command
# Make script executable.
st = os.stat(script)
os.chmod(script, st.st_mode | stat.S_IEXEC) # chmod u+x
# Call qsub.
qsub.append(script)
# print(" ".join(qsub))
subprocess.run(" ".join(qsub), shell=True)
class Where:
def __init__(self, doe, job):
self.doe = doe
self.job = job
def __make_dir(self,directory):
if not directory:
directory = datetime.datetime.now().isoformat().replace(":","-")
try:
os.mkdir(directory)
except FileExistsError:
pass
def on_computer(self, directory=None):
self.__make_dir(directory)
return HowLocal(self.doe, self.job, directory)
def on_grid_engine(self, directory=None):
self.__make_dir(directory)
return HowGridEngine(self.doe, self.job, directory)
class run:
def __init__(self, doe):
self.doe = doe
def command(self, cmd):
def job(params):
return cmd.format(**params)
return Where(self.doe, job)
def func(self, job):
return Where(self.doe, job)
# def script(self, script):
# pass
class plan:
def __init__(self, **kwargs):
self.names = []
self.axes = []
self.static_params = kwargs
def __iter__(self):
for p in itertools.product( *(self.axes) ):
params = self.static_params
for i in range(len(self.names)):
params[self.names[i]] = p[i]
yield params
def forall(self, name, iters):
self.names.append(name)
self.axes.append(iters)
return self
def random(self, name, nb, rmin=0, rmax=1):
"""Note: this intentionally produce the SAME random sequence for each product of axes."""
self.names.append(name)
def rand():
for i in range(nb):
yield random.random() * (rmax-rmin) + rmin
self.axes.append(rand())
return self
if __name__ == "__main__":
const_args=" --nb-sensors 5 --sensor-range 0.2 --domain-width 50 --iters 10000"
solvers = ["num_greedy","bit_greedy","num_rand","bit_rand"]
nbruns = 2
outdir = "tests"
if not os.path.exists(outdir):
os.mkdir(outdir)
cmd = "echo \"--no-plot --dir {outdir} --seed {seed} --solver {solver}\""
doe = plan(outdir=outdir).forall("seed",range(nbruns)).forall("solver",solvers)
# run(doe).command(cmd).on_computer().one_by_one()
# run(doe).command(cmd).on_computer().all()
# run(doe).command(cmd).on_computer().batch(4)
# def job(params):
# return cmd.format(**params)
# run(doe).func(job).on_grid_engine(directory="tests").qsub("cwd",S="/bin/bash")
run(doe).command(cmd).on_grid_engine(outdir).qsub("cwd",S="/bin/bash")