Package: pymoso
PyMOSOPyMOSO is software for solving multi-objective simulation optimization (MOSO) problems and for creating, comparing, and testing MOSO algorithms.ReferenceIf you use PyMOSO in work leading to publication, please cite the paper which introduces PyMOSO.Cooper, K., Hunter, S. R. 2019. PyMOSO: Software for Multi-Objective Simulation Optimization with R-PERLE and R-MinRLE.INFORMS Journal on Computing, to appear. Preprint:http://www.optimization-online.org/DB_HTML/2018/10/6876.html.Additional ReadingThe initial release of PyMOSO contains solvers that implement four total algorithms, in alphabetical order: R-MinRLE, R-PE, R-PERLE, and R-SPLINE.  The algorithms R-MinRLE, R-PE, R-PERLE were introduced in the following paper:Cooper, K., Hunter, S. R., and Nagaraj, K. 2019. Bi-objective simulation optimization on integer lattices using the epsilon-constraint method in a retrospective approximation framework.INFORMS Journal on Computing, to appear. Preprint:http://www.optimization-online.org/DB_HTML/2018/06/6649.html.The algorithm R-SPLINE was introduced in the following paper:Wang, H., Pasupathy, R., and Schmeiser, B. W. 2013. Integer-ordered simulation optimization using R-SPLINE: Retrospective Search with Piecewise-Linear Interpolation and Neighborhood Enumeration.ACM Transactions on Modeling and Computer Simulation, Vol. 23, No. 3, Article 17 (July 2013), 24 pages.http://dx.doi.org/10.1145/2499913.2499916We recommend reading these papers to understand the algorithms, what they return, and the algorithm parameter options that we describe in the user manual.Table of ContentsPyMOSOReferenceAdditional ReadingInstallationInstall PyMOSO from the Python Packaging Index using pipInstall PyMOSO from the repository using pipInstall PyMOSO from Source CodeCommand Line Interface (CLI)CLI helpThe listitems command for viewing solvers, testers, and oracles included in PyMOSOThe solve commandThe Example OracleTable of Algorithm-Specific ParametersThe testsolve CommandThe Example TesterImplementing problems, testers, and algorithms in PyMOSOImplementing PyMOSO OraclesExample Oracle that Wraps a C SimulationExample Wrapper with PyMOSO Random NumbersImplementing PyMOSO TestersExample Metric 1Example Metric 2Implementing PyMOSO AlgorithmsTemplate AcceleratorTemplate RA SolverTemplate MOSO SolverTake Simulation Replications at a PointFind Neighbors and Take Simulation ReplicationsArgsort a Dictionary of PointsSelect the Minimizer and its ValueUse SPLINE to Retrive a Local MinimizerFind the Non-Dominated Points in a DictionaryRandomly Select Points in a SetUsing solve andtestsolvein Python ProgramsMinimal solve ExampleSome solve Examples with OptionsA testsolve ExampleComputing a Metric on testsolve OutputPyMOSO Object ReferenceThe pymoso.prng.mrg32k3a ModuleThe pymoso.chnbase ModuleThe pymoso.chnutils ModuleThe Oracle ClassThe MOSOSolver ClassThe RASolver ClassThe RLESolver ClassInstallationSince PyMOSO is programmed in Python, every PyMOSO user must first install Python, which can be downloaded fromhttps://www.python.org/downloads/. PyMOSO is compatible with Python versions 3.6 and higher. In the remainder of this section, we assume an appropriate Python version is installed. We discuss three different methods to install PyMOSO: first, from the Python Packaging Index; second, directly from our source code using git; and third, manually installing PyMOSO from our source code.Install PyMOSO from the Python Packaging Index usingpipFor ease of distribution, we keep stable, recent releases of PyMOSO on the Python Packaging Index (PyPI). Since the programpipis included in Python versions 3.6 and higher, we recommend usingpipto install PyMOSO. To do so, open a terminal, type the following command, and press enter.pip install pymosoDepending on how users configure their Python installation and how many version of Python they install, they may need to replacepipwithpip3, or other variants ofpip.Install PyMOSO from the repository usingpipUsers withgitinstalled can usepipto install the most current version of PyMOSO directly from our source code:pip install git+https://github.com/pymoso/PyMOSO.gitWe consider the latest source to be less stable than the fixed releases we upload to PyPI, and thus we recommend most users install PyMOSO from PyPI.Install PyMOSO from Source CodeUsers may follow the steps below to manually install PyMOSO from any version of the source code.Acquire the PyMOSO source code, for example, by downloading it from the repositoryhttps://github.com/HunterResearch/PyMOSO.Install thewheelpackage, e.g. using thepip install wheelcommand.Open a terminal and navigate into the main project directory which contains the filesetup.py.Build the installable PyMOSO package, called a wheel, using the commandpython setup.py bdist_wheel. As withpip, some users may need to replacepythonwithpython3or something similar. The command should create a directory nameddistcontaining the PyMOSO wheel.Install the PyMOSO wheel using pip installdist/pymoso-x.x.x-py3-none-any.whl, where users replacex.x.xwith the appropriate PyMOSO version.Command Line Interface (CLI)PyMOSO users solving MOSO problems and testing MOSO algorithms may do so using the command line interface. First, we show how to access the included help file. Then, we show how to view the lists of solvers, testers, and oracles installed by default with PyMOSO. Finally, we discuss thesolveandtestsolvecommands.CLI helpPyMOSO includes a command line help file. The help file shows syntax templates for every PyMOSO command, the available options, and a selection of example invocations. Thepymoso --helpinvocation prints the file to the terminal. The file is also printed when PyMOSO cannot parse an invocation that begins withpymoso. We show the current help file below.Usage:
  pymoso listitems
  pymoso solve [--budget=B] [--odir=D] [--crn] [--simpar=P]
    [(--seed 
 
 
 
 
 
)] [(--param <param> <val>)]...
    <problem> <solver> <x>...
  pymoso testsolve [--budget=B] [--odir=D] [--crn] [--isp=T] [--proc=Q]
    [--metric] [(--seed 
 
 
 
 
 
)] [(--param <param> <val>)]...
    <tester> <solver> [<x>...]
  pymoso -h | --help
  pymoso -v | --version

Options:
  --budget=B                Set the simulation budget [default: 200]
  --odir=D                  Set the output file directory name. [default: testrun]
  --crn                     Set if common random numbers are desired.
  --seed                    Set the random number seed with 6 spaced integers.
  --simpar=P                Set number of parallel processes for simulation replications. [default: 1]
  --isp=T                   Set number of algorithm instances to solve. [default: 1]
  --proc=Q                  Set number of parallel processes for the algorithm instances. [default: 1]
  --metric                  Set if metric computation is desired.
  --param                   Specify a solver-specific parameter <param> <val>.
  -h --help                 Show this screen.
  -v --version              Show version.

Examples:
  pymoso listitems
  pymoso solve ProbTPA RPERLE 4 14
  pymoso solve --budget=100000 --odir=test1  ProbTPB RMINRLE 3 12
  pymoso solve --seed 12345 32123 5322 2 9543 666666666 ProbTPC RPERLE 31 21 11
  pymoso solve --simpar=4 --param betaeps 0.4 ProbTPA RPERLE 30 30
  pymoso solve --param radius 3 ProbTPA RPERLE 45 45
  pymoso testsolve --isp=16 --proc=4 TPATester RPERLE
  pymoso testsolve --isp=20 --proc=10 --metric --crn TPBTester RMINRLE 9 9For now, PyMOSO has three commands:listitems,solve, andtestsolve, which we explain below.Thelistitemscommand for viewing solvers, testers, and oracles included in PyMOSOThe default installation of PyMOSO includes a selection of solvers, testers, and oracles. Users can view the complete lists of included solvers, testers, and oracles using thepymoso listitemscommand. We show the current listing below. Test problems A, B, and C refer to those in Cooper et al (2018).Solver                         Description
************************       ************************
RMINRLE                        A solver using R-MinRLE for integer-ordered MOSO.
RPE                            A solver using R-Pe for integer-ordered bi-objective MOSO.
RPERLE                         A solver using RPERLE for integer-ordered bi-objective MOSO.
RSPLINE                        A solver using R-SPLINE for single objective SO.

Problems                       Description                    Test Name (if available)
************************       ************************       ************************
ProbSimpleSO                   x^2 + noise.                   SimpleSOTester
ProbTPA                        Test Problem A                 TPATester
ProbTPB                        Test Problem B                 TPBTester
ProbTPC                        Test Problem C                 TPCTesterThesolvecommandThe PyMOSOsolvecommand is for solving MOSO problems. Users can solve the built-in problems (use thelistitemscommand to view the built-in problems), however, PyMOSOsolveusers typically will have their own MOSO problem they wish to solve. Thus, we assume users have implemented a PyMOSO oracle namedMyProbleminmyproblem.py.  In the examples that follow, we assume theMyProblemimplementation below, which is a bi-objective oracle with one-dimensional feasible points. SeeImplementing PyMOSO Oraclesfor instructions on implementing a MOSO problem as a PyMOSO oracle.The Example Oracle# import the Oracle base classfrompymoso.chnbaseimportOracleclassMyProblem(Oracle):'''Example implementation of a user-defined MOSO problem.'''def__init__(self,rng):'''Specify the number of objectives and dimensionality of points.'''self.num_obj=2self.dim=1super().__init__(rng)defg(self,x,rng):'''Check feasibility and simulate objective values.'''# feasible values for x in this examplefeas_range=range(-100,101)# initialize obj to empty and is_feas to Falseobj=[]is_feas=False# check that dimensions of x match self.dimiflen(x)==self.dim:is_feas=True# then check that each component of x is in the range aboveforiinx:ifnotiinfeas_range:is_feas=False# if x is feasible, simulate the objectivesifis_feas:#use rng to generate random numbersz0=rng.normalvariate(0,1)z1=rng.normalvariate(0,1)obj1=x[0]**2+z0obj2=(x[0]-2)**2+z1obj=(obj1,obj2)returnis_feas,objThe templatesolvecommand ispymoso solve oracle solver x0, whereoracleis a built-in or user-defined oracle,solveris a built-in or user-defined algorithm, andx0is a feasible starting point for the solver, with a space between each component. As a first example, we solve the user-definedMyProblemusing the built-in R-PERLE starting at the feasible point 97.pymoso solve myproblem.py RPERLE 97Similarly, we can solve built-in problems, such asProbTPAwhich has two-dimensional feasible points.pymoso solve ProbTPA RPERLE 40 40Henceforth, we presentsolveexamples only for solvingMyProblem.  SinceMyProblemis bi-objective, we recommend using theR-PERLEsolver. However, for two or more objectives, PyMOSO implementsR-MinRLE.pymoso solve myproblem.py RMINRLE 97For a single objective problem, PyMOSO implementsR-SPLINE. We remark that if given a multi-objective problem,R-SPLINEwill simply minimize the first objective. We do not necessarily prohibit such use, but urge that users take care when using R-SPLINE to minimize one objective of a many-objective problem.pymoso solve myproblem.py RSPLNE 97Regardless of the chosen solver, PyMOSO creates a new sub-directory of the working directory containing output. There will be a metadata file, indicating the date, time, solver, problem, and any other specified options. In addition, PyMOSO creates a file containing the solver-generated solution. PyMOSO provides additional options for users solving MOSO problems. We present examples of each option below. First, users can specify the name of the output directory.pymoso solve --odir=OutDirectory myproblem.py RPERLE 45Users can specify the simulation budget, which is currently set to a default of 200.pymoso solve --budget=100000 myproblem.py RPERLE 12Users may specify to take simulation replications in parallel. We only recommend doing so if the user has thought through appropriate pseudo-random number stream control issues (seeImplementing PyMOSO Oracles). Furthermore, due to the overhead of parallelization, we only recommend using the parallel simulation replications feature if observations are sufficiently "expensive" to compute, e.g. the simulation takes a half second or more to generate a single observation. We remark that the run-time complexity of the simulation oracle may not perfectly indicate when it is appropriate to use parallelization; other factors include, e.g., the total simulation budget.pymoso solve --simpar=4 myproblem.py RPERLE 44Currently, all PyMOSO solvers support using common random numbers. Users may enable the functionality using the--crnoption.pymoso solve --crn myproblem.py RMINRLE 62We do not recommend this option unless the oracle is implemented to be compatible, that is, the oracle uses PyMOSO's pseudo-random number generator to generate pseudo-random numbers or to provide a seed to an externalmrg32k3agenerator (seeImplementing PyMOSO Oracles).Users may specify an initial seed to PyMOSO'smrg32k3apseudo-random number generator. Seeds must be 6 positive integers with spaces. The default is 12345 for each of the 6 components.pymoso solve --seed 1111 2222 3333 4444 5555 6666 myproblem.py RPERLE 23Users may specify algorithm-specific parameters (see the papers in which the algorithms were introduced for detailed explanations of the parameters.) All parameters are specified in the form--param name value. For example, the RLE relaxation parameter can be specified and set asbetadelto a real number. We refer the reader tothe tablefor the full list of currently available algorithm-specific parameters.pymoso solve --param betadel 0.2 myproblem.py RPERLE 34Finally, users may specify any number of options in one invocation. However, all options must be specified in after thesolvecommand and before themyproblem.pyargument. Furthermore, any--paramoptions must be the last options. (Note that the\at the end of the first line continues the command to the second line.)pymoso solve --crn --simpar=4 --budget=10000 --seed 1 2 3 4 5 6 \--odir=Exp1 --param mconst 4 --param betadel 0.7 myproblem.py RPERLE 97Table of Algorithm-Specific ParametersParameter NameDefault ValueAffected SolversDescriptionmconst2RPERLE,RMINRLE,RPE,RSPLINEInitialize the sample size and subsequent schedule of sample sizes.bconst8RPERLE,RMINRLE,RPE,RSPLINEInitialize the search sampling limit and subsequent schedule of limits.radius1RPERLE,RMINRLE,RPE,RSPLINESets radius that determines a point's neighborhood.betadel0.5RPERLE,RMINRLERoughly, affects how likely it is for RLE to keep its given solution.betaeps0.5RPERLE,RPERoughly, affects how likely PE will perform a search from a point.ThetestsolveCommandThe PyMOSOtestsolvecommand tests algorithms on problems using a PyMOSO tester. Users can test built-in or user-defined solvers with built-in or user-defined testers. In the examples that follow, we assume users have implementedMyProblemas inThe Example Oracleand the corresponding tester namedMyTesterinmytester.py, shown inThe Example Tester. SeeImplementing PyMOSO Testersfor instructions on implementing a user-defined tester, including a metric for comparing algorithms, in PyMOSO.The Example Testerimportsys,ossys.path.insert(0,os.path.dirname(__file__))# use hausdorff distance (dh) as an example metricfrompymoso.chnutilsimportdh# import the MyProblem oraclefrommyproblemimportMyProblem# optionally, define a function to randomly choose a MyProblem feasible x0defget_ranx0(rng):val=rng.choice(range(-100,101))x0=(val,)returnx0# compute the true values of x, for computing the metricdeftrue_g(x):'''Compute the objective values.'''obj1=x[0]**2obj2=(x[0]-2)**2returnobj1,obj2# define an answer as appropriate for the metricmyanswer={(0,4),(4,0),(1,1)}classMyTester(object):'''Example tester implementation for MyProblem.'''def__init__(self):self.ranorc=MyProblemself.answer=myanswerself.true_g=true_gself.get_ranx0=get_ranx0defmetric(self,eles):'''Metric to be computed per retrospective iteration.'''epareto=[self.true_g(point)forpointineles]haus=dh(epareto,self.answer)returnhausThe templatetestsolvecommand ispymoso testsolve tester solverwheretesteris a built-in or user-defined tester, andsolveris a built-in or user-defined solver. Users may also specify anx0, as in thesolvecommand, if thetesterdoes not implement the function to generate feasible points. As a first example, we testRPERLEonMyProblemusingMyTester. Since some options are compatible with bothsolveandtestsolve, we include those options in this example.pymoso testsolve --budget=999 --odir=exp1 \--crn --seed 1 2 3 4 5 6 mytester.py RPERLEUsers may want to compute some metric on the algorithm-generated solutions. If a metric is defined as part of the tester, such as inMyTester, thetestsolvecommand can compute the metric on every algorithm iteration using the--metricoption.pymoso testsolve  --metric  mytester.py RPERLEThetestsolvecommand cannot perform simulation replications in parallel. However, testers can apply the solvers to independent sample paths of the problems. For example, to testRPERLEon 100 independent sample paths ofMyProblem, compute the metrics for each sample path, and use common random numbers in each sample path, use the following command.pymoso testsolve --crn --metric --isp=100 mytester.py RPERLEPyMOSO can perform independent algorithm runs in parallel. Use the--procoption to specify the number of processes available to PyMOSO.pymoso testsolve --crn --metric --isp=100 --proc=20 mytester.py RPERLEWe remark here that, to ensure the algorithm runs remain independent using PyMOSO's pseudo-random number generator (seeImplementing PyMOSO Oracles), researchers should set the total simulation budget so that the included algorithms do not surpass 200 retrospective approximation (RA) iterations. For reference, using the default settings, the sample size at every point in the 200th RA iteration is almost 380 million.Thetestsolvecommand creates a results file for each independent sample path. The file contains the solutions generated at every algorithm iteration, such that the solution of iteration 2 is on line 2, iteration 10 on line 10, and so forth. If--metricis specified, PyMOSO generates a second file for each independent sample path containing the collection of triples (iteration number, simulations used at end of iteration, metric).Implementing problems, testers, and algorithms in PyMOSOTo use PyMOSO, users solving MOSO problems must implement a PyMOSO oracle, and users testing MOSO algorithms should implement, at least, a PyMOSO oracle and tester. In this section, we provide template Python code to help users quickly implement oracles, testers, and perhaps solvers in PyMOSO.Implementing PyMOSO OraclesUsually, implementing a PyMOSO oracle implies implementing a Monte Carlo simulation oracle as a black box function while following the PyMOSO rules put forth in this section. For reference, we discuss the example PyMOSO oracleMyProbleminThe Example Oracle. Users may copy the code inThe Example Oracleand re-implement the functiongas needed. We now list the basic requirements of everygimplementation.The functiongmust be an instance method of anOraclesub-class, and thus takeselfas its first parameter.The functiongmust take an arbitrarily-named second parameter which is a tuple of lengthself.dimand represents a point. Stylistically, PyMOSO consistently names this parameterx.The functiongmust take an arbitrarily-named third parameter which is a modified Pythonrandom.Randomobject. Stylistically, PyMOSO consistently names this parameterrng.The functiongmust return a boolean first and a tuple of lengthself.num_objsecond.The boolean isTrueifxis feasible, andFalseotherwise.Ifxis feasible, the tuple contains a single observation of every objective. Ifxis not feasible, each element in the tuple isNone.If users already have an implemented simulation oracle, they may find it convenient to implementgas wrapper which calls that simulation from Python. As an example, suppose a user has implemented a simulation in C which is compiled to a C library calledmysim.soand placed in the working directory. Suppose further that the simulation function takes the following as parameters: an array of integers representing a point and an unsigned integer representing the number of observations to take atx. The function output is defined asstruct Simoutwith membersfeasset to 0 or 1,obja double array set to the mean of the observed objective values, andvara double array set to the sample variance of the observed objective values. Then users can modify the template to wrap the C functionstruct Simout c_func(int x, int n)as inthe example.Example Oracle that Wraps a C SimulationfromctypesimportCDLL,c_double,c_uint,c_int,Structureimportos.pathlibname='mysim.so'libabspath=os.path.dirname(os.path.abspath(__file__))+os.path.sep+dll_namelibobj=CDLL(libabspath)classSimout(Structure):_fields_=[("feas",c_int),("obj",c_double*2),("var",c_double*2)]csimout=libobj.c_funccsimout.restype=Simoutfrompymoso.chnbaseimportOracleclassMyProblem(Oracle):'''Example implementation of a user-defined MOSO problem.'''def__init__(self,rng):'''Specify the number of objectives and dimensionality of points.'''self.num_obj=2self.dim=1super().__init__(rng)defg(self,x,rng):'''Check feasibility and simulate objective values.'''is_feasible=Trueobjective_values=(None,None)# g takes only one observation so set the c_func parameter to 1c_n=c_uint(1)# c_func requires is an integer so convert it -- this is a 1D examplec_x=c_int(x[0])# call the C functionmysimout=csimout(c_x,c_n)ifnotmysimout.feas:is_feasible=Falseelse:is_feasible=Trueifis_feasible:objective_values=tuple(mysimout.obj)returnis_feasible,objective_valuesThe C wrapper exampleis a valid PyMOSO oracle which wraps a C function. However, PyMOSO algorithms cannot enable common random numbers on this oracle. Furthermore, PyMOSO cannot guarantee that observations are independent when taken in parallel. To enable these properties, the external simulation must usemrg32k3aas the generator and must accept a user-specified seed.Suppose the librarymysim.soalso implements the functionset_simseedwhich accepts a long array representing anmrg32k3aseed. We modify thewrapperfor compatibility with common random numbers and to guarantee independence of parallel observations.The new wrapperdemonstrates usingrng.get_seed()to return the currentmrg32k3aseed.Example Wrapper with PyMOSO Random NumbersfromctypesimportCDLL,c_double,c_uint,c_int,Structure,c_longimportos.pathlibname='mysim.so'libabspath=os.path.dirname(os.path.abspath(__file__))+os.path.sep+dll_namelibobj=CDLL(libabspath)classSimout(Structure):_fields_=[("feas",c_int),("obj",c_double*2),("var",c_double*2)]csimout=libobj.c_funccsetseed=libobj.set_simseedcsimout.restype=Simoutfrompymoso.chnbaseimportOracleclassMyProblem(Oracle):'''Example implementation of a user-defined MOSO problem.'''def__init__(self,rng):'''Specify the number of objectives and dimensionality of points.'''self.num_obj=2self.dim=1super().__init__(rng)defg(self,x,rng):'''Check feasibility and simulate objective values.'''is_feasible=Trueobjective_values=(None,None)# get the PyMOSO seed from rngseed=rng.get_seed()# convert the seed to c_long arrayc_longarr=c_long*6c_seed=c_longarr(seed[0],seed[1],seed[2],seed[3],seed[4],seed[5])# use the library function to set the sim seedcsetseed(c_seed)# g takes only one observation so set the c_func parameter to 1c_n=c_uint(1)# c_func requires is an integer so convert it -- this is a 1D examplec_x=c_int(x[0])# call the C functionmysimout=csimout(c_x,c_n)ifnotmysimout.feas:is_feasible=Falseelse:is_feasible=Trueifis_feasible:objective_values=tuple(mysimout.obj)returnis_feasible,objective_valuesAlternatively, if the number of required pseudo-random numbers is known, users can userng.random()to generate pseudo-random numbers and then pass them to an external simulation if such functionality is supported.Therngobject is implemented as a sub-class of Python'srandom.Randomclass, thus the official Python documentation forrandomapplies torngand is found athttps://docs.python.org/3/library/random.html. In addition torngusingmrg32k3aas its generator, we also implementrng.normalvariatesuch that it uses the Beasley-Springer-Moro algorithm (Law 2015, p. 458) to approximate the inverse of the standard normal cumulative distribution function.When usingrng, to ensure independent sampling of observations, PyMOSO "jumps" forward in the pseudo-random number stream after obtaining every simulation replication. Each jump is of fixed size 2^76 pseudo-random numbers. Thus, we require that every simulation replication use fewer than 2^76 pseudo-random numbers. We ensure independence among parallel replications by "giving" each processor a stream (anrng), each of which is 2^127 pseudo-random numbers apart. When using the current PyMOSO algorithms that rely on RA, each RA iteration begins the next available independent stream 2^127, where PyMOSO accounts for the possibility of parallel computation within an RA iteration. Thus, in a given RA iteration, a user may simulate 100 million points at a sample size of 1 million, without common random numbers, and easily not reach the limit.Implementing PyMOSO TestersConsider again theexample tester. As a minimal valid PyMOSO tester, users may do nothing but assign theMyTestermemberself.ranorcto a PyMOSO oracle, such asMyProblem, in Line 27. However, we expect most users to leverage PyMOSO features by implementing metrics and  feasible point generators. The functionget_ranx0allows the tester to generate feasible points toMyProblemandmetricallows the tester to compute a metric on sets returned by a solver. Researchers may implement any number of additional supporting functions, including members and methods of the tester class. Thetrue_gfunction is an example of such a supporting function, which is used to compute the example metric.First, we list the rules for implementing a feasible point generator.The function is arbitrarily named but must be set to theself.get_ranx0member of a tester.The function must take a single parameter, an arbitrarily namedrandom.Randomobject we suggest namingrng.The function must return a tuple with length corresponding to theself.dimmember of theself.ranorcmember of the tester.Since a researcher's desired metric depends on the algorithm capabilities and problem complexity, PyMOSO allows researchers to implement any metric they choose. We provide three example metrics, but first, we list the implementation rules of themetricfunction.The \inline{metric} function must be an instance method of a tester, and thus take \inline{self} as its first parameter.The second parameter of \inline{metric} is arbitrarily named and is a Python set of tuples.PyMOSO does not enforce the return value of \inline{metric}, but we recommend a scalar real number.The metric implemented in theexample testeris the Hausdorff distance from (a) the true image of an estimated solution returned by an algorithm, to (b) the true solution hard-coded asmyanswer.For an example of a different metric, consider a MOSO problem that has more than one local efficient set (LES) and such that each LES contains no members of another LES. Since an algorithm that converges to a LES is may find only one LES, we may define the metric to compute the Hausdorff distance between the true image of the estimated solution and the "closest" true LES, as follows. Letself.answerbe implemented as a list of sets, and assume aself.true_gimplementation. Then thefollowing exampleimplements the described metric.Example Metric 1defmetric(self,eles):# use the distance to the closest set.epareto=[self.true_g(point)forpointineles]# self.soln is a list of setsdist_list=[dh(epareto,les)forlesinself.answer]returnmin(dist_list)For single-objective problems with one correct solutionx*, a simple metric that takes an estimated solutionXis|g(X) - g(x*)|, which we implement in thesecond example metricassuming an appropriate implementation ofself.answerandself.true_g.Example Metric 2defmetric(self,singleton_set):# single objective algorithms still return a setpoint,=singleton_set# let self.soln be a real numberdist=abs(self.true_g(point)-self.answer)returndistImplementing PyMOSO AlgorithmsResearchers can implement simulation optimization algorithms in the PyMOSO framework. PyMOSO provides support for algorithms in three categories:PyMOSO provides strong support for implementing new MOSO algorithms that rely on RLE in an RA framework.PyMOSO provides strong support for implementing general RA algorithms.PyMOSO provides basic support, such as pseudo-random number control, for implementing other simulation optimization algorithms.
We provide templates of algorithms implemented in each of these three categories, along with example code snippets.In the first category, programmers can use PyMOSO to create new RA algorithms that use RLE for convergence. The novel part of these algorithms, created by the user, will be theaccelfunction which should collect points to send to RLE for certification. Here, we list the rules foraccel.Theaccelfunction must be an instance method of anRLESolverobject, and thus its first parameter must beself.The second parameter is arbitrarily named and is a set of tuples. We recommend naming the parameterwarm_start, as it represents the sample-path solution of the previous RA iteration.The return value must be a set of tuples representing feasible points; we do not recommend any particular name.
In every RA iteration, PyMOSO will first callaccel(self, warm_start)and send the returned set torle(self, candidate_les). The return value must be a set of tuples. The implementer does not need to implement or call RLE, as in thetemplate accelerator.Template Acceleratorfrompymoso.chnbaseimportRLESolver# create a subclass of RLESolverclassMyAccel(RLESolver):'''Example implementation of an RLE accelerator.'''defaccel(self,warm_start):'''Return a collection of points to send to RLE.'''# implement algorithm logic here and return a setreturnwarm_startIn the second category, algorithm designers can quickly implement any RA algorithm by sub-classingRASolverand implementing thespsolvefunction, as shown in theRA solver template. The algorithm can be a single-objective algorithm. PyMOSO cannot guarantee the convergence of such algorithms. The template is technically valid in PyMOSO but is probably not effective.Template RA Solverfrompymoso.chnbaseimportRASolverclassMyRAAlg(RASolver):'''Template implementation of an RA solver.'''defspsolve(self,warm_start):'''Return the sample path solution.'''# implement algorithm logic here and return a setreturnwarm_startThough analogous to those of anRLESolver.accelmethod, for completeness, we list the requirements for anRASolver.spsolvemethod.Thespsolvefunction must be an instance method of anRASolverobject, and thus its first parameter must beself.The second parameter is arbitrarily named and is a set of tuples. We recommend naming the parameterwarm_startas it represents the sample-path solution of the previous RA iteration.The return value must be a set of tuples representing feasible points; we do not recommend any particular name.In the third category, PyMOSO can accommodate any simulation optimization algorithm by implementing thesolvefunction of aMOSOSolversub-classas shown. It does not have to be a multi-objective algorithm. PyMOSO will require users to send an initial feasible pointx0whether or not the algorithm needs it. The initial feasible pointx0is accessed throughself.x0which is a tuple. We now list the rules for implementing anyMOSOSolver.solvefunction.Thesolvefunction must be an instance method ofMOSOSolver, and thus takeselfas its first parameter.The second parameter is the simulation budget, a natural number.Thesolvefunction must return a dictionary (we name itresultsin our example) with at least 3 keys:'itersoln','simcalls','endseed'. Researchers may track additional data and add it toresultsas desired.The'itersoln'key itself corresponds to a dictionary with a key for each algorithm iteration labeled {0, 1, ...}. The value at each iteration is a set containing the estimated solution at the end of the iteration.The'simcalls'key itself corresponds to a dictionary with a key for each algorithm iteration labeled {0, 1, ...}. The value at each iteration is a natural number containing the cumulative number of simulation replications taken at the end of the iteration.The'endseed'key corresponds to a tuple of length 6, representing anmrg32k3aseed. The algorithm programmer should ensure the stream generated byresults['endseed']is independent of all streams used by the algorithm.Researchers may use theMOSO templateto implement new simulation optimization algorithms.Template MOSO Solverfrompymoso.chnbaseimportMOSOSolverclassMyMOSOAlg(MOSOSolver):'''Template implementation of a MOSO solver.'''defsolve(self,budget):whileself.num_calls<=budget:# implement algorithm logic and return the resultsreturnresultsFor convenience, in the mini-sections below, we also provide some example code snippets that we find useful when implementing algorithms in PyMOSO. They work without modification when using the templates above that inherit \inline{RLESolver} or \inline{RASolver}, but some functions may require implementation or modification for use in aMOSOSolver. For reference,this sectioncontains a list of most objects accessible to PyMOSO programmers.Take Simulation Replications at a Pointfrompymoso.chnutilsimportget_nbors# pretend x has not yet been visited in this RA iteration and is feasiblex=(1,1,1)# self.m is the sample size of the current RA iterationm=self.m# self.num_calls is the cumulative number of simulations used till nowstart_num_calls=self.num_calls# use estimate to sample x and put results in self.gbar and self.sehatisfeas,fx,se=self.estimate(x)calls_used=self.num_calls-start_num_callsprint(m==calls_used)# Trueprint(fx==self.gbar[x])# Trueprint(se==self.sehat[x])# True# estimate will not simulate again in subsequent visits to a pointstart_num_calls=self.num_callsisfeas,fx,se=self.estimate(x)calls_used=self.num_calls-start_num_callsprint(calls_used==0)# TrueFind Neighbors and Take Simulation Replications# neighborhood radiussr=self.nbor_radnbors=get_nbors(x0,r)self.upsample(nbors)forninnbors:print(ninself.gbar)# True if n feasible else False# upsample also returns the feasible subsetnbors=self.upsample(nbors)Argsort a Dictionary of Points# 0 index for first objectivesorted_feas=sorted(nbors|{x},key=lambdat:self.gbar[t][0])Select the Minimizer and its Valuexmin=sorted_feas[0]fxmin=self.gbar[x]Use SPLINE to Retrive a Local Minimizer# no constraints and minimize the 2nd objectivex0=(2,2,2)isfeas,fx,sex=self.estimate(x0)# the suppressed value is the set visited along SPLINE's trajectory_,xmin,fxmin,sexmin=self.spline(x0,float('inf'),1,0)print(self.gbar[xmin]==fxmin)# TrueFind the Non-Dominated Points in a Dictionaryfromchnutilsimportget_nondomnondom=get_nondom(self.gbar)Randomly Select Points in a Setsolver_rng=self.sprn# pick 5 points -- returns a list, not a set.ran_pts=solver_rng.sample(list(nondom),5)one_in_five=solver_rng.choice(ran_pts)Usingsolveandtestsolvein Python ProgramsUsers may thesolveandtestsolvefunctions within a Python program.MinimalsolveExample# import the solve functionfrompymoso.chnutilsimportsolve# import the module containing the RPERLE implementationimportpymoso.solvers.rperleasrp# import MyProblem - myproblem.py should usually be in the script directoryimportmyproblemasmp# specify an x0. In MyProblem, it is a tuple of length 1x0=(97,)soln=solve(mp.MyProblem,rp.RPERLE,x0)print(soln)SomesolveExamples with Options# example for specifying budget and seedbudget=10000seed=(111,222,333,444,555,666)soln1=solve(mp.MyProblem,rp.RPERLE,x0,budget=budget,seed=seed)# specify crn and simparsoln2=solve(mp.MyProblem,rp.RPERLE,x0,crn=True,simpar=4)# specify algorithm specific parameterssoln3=solve(mp.MyProblem,rp.RPERLE,x0,radius=2,betaeps=0.3,betadel=0.4)# mix themsoln4=solve(mp.MyProblem,rp.RPERLE,x0,crn=True,seed=seed,radius=5)AtestsolveExample# import the testsolve functionsfrompymoso.chnutilsimporttestsolve# import the module containing RPERLEimportpymoso.solvers.rperleasrp# import the MyTester classfrommytesterimportMyTester# testsolve needs a "dummy" x0 even if MyTester will generate themx0=(1,)run_data=testsolve(MyTester,rp.RPERLE,x0,isp=100,crn=True,radius=2)Computing a Metric ontestsolveOutputProgrammers must compute their metric. Here,run_datais a dictionary of the form describedhereand we compute the metric on the 5th iteration of of the 12th independent algorithm instance.iter5_soln=run_data[11]['itersoln'][4]isp12_iter5_metric=MyTester.metric(iter5_soln)PyMOSO Object ReferenceThepymoso.prng.mrg32k3aModuleThepymoso.prng.mrg32k3amodule exposes the pseudo-random number generator and functions to manipulate it.ObjectDescriptionMRG32k3aSub-class of random.Random, defines allrngobjects.get_next_prnstream(seed)Return anrngobject seeded 2^127 steps from the input seed.jump_substream(rng)Seed the inputrngobject 2^76 steps forward.Thepymoso.chnbaseModuleThepymoso.chnbasemodule implements the base classes for oracles and solvers. Programmers should sub-class these when creating new PyMOSO implementations.ClassDescriptionOracleBase class for implementing oracles.RLESolverBase class for implementing solvers using RLE.RASolverBase class for implementing RA solvers.MOSOSolverBase class for all solvers.Thepymoso.chnutilsModuleThepymoso.chnutilscontains useful functions for implementing and testing algorithms.FunctionDescriptionsolve(oracle, solver, x0, **kwargs)See herefor instructions.testsolve(tester, solver, x0, **kwargs)See herefor instructions.does_weak_dominate(g, h, relg, relh)All inputs are tuples of equal length. ReturnsTrueifgweakly dominateshwith the relaxations.does_dominate(g, h, relg, relh)ReturnsTrueifgdominateshwith the relaxations.does_strict_dominate(g, h, relg, relh)ReturnsTrueifgstrictly dominateshwith the relaxations.get_nondom(obj_dict)Input: a dictionary with tuples for keys and values. The keys are feasible points; the values are their objective values. Return: a set of tuples representing non-dominated points.get_nbors(x, r)Input: a tuplex, a positive real scalarrindicating the neighborhood radius. Return: Set of tuples which are the neighbors.get_setnbors(S, r)Input: a set of tuples, and the neighborhood radius. Return: the union ofget_nbors(s, r)for everysinS.dh(A, B)Returns the Hausdorff distance between setAand setB.edist(x1, x2)Return the Euclidean distance fromx1tox2.get_metric(results, tester)Input:resultsis a dictionary, the output of each sample path oftestsolve.testermust implementmetric. Returns: The set of triples (iteration, simulation count, metric) for an algorithm run.TheOracleClassWhen implementingRASolveralgorithms, programmers may not need to accessOracleobjects directly at all. When implementingMOSOSolveralgorithms, programmers will use (or wrap)hitandcrn_advance().Member/MethodDescriptionnum_objA positive integer, the number of objectives.dimA positive integer, the dimensionality of feasible points.rngAn instance ofMRG32k3a.hit(x, n)Takenobservations ofx. Return:True, and a tuple containing the mean of the observations for each objective, and a tuple containing the standard error for each objective ifxis feasible. The function handles CRN internally.set_crnflag(bool)Turn CRN on (True) or off.set_crnold(state)Save therngstate as the CRN baseline, e.g. for an algorithm iteration. Get the state usingrng.getstate().crn_reset()Back the oraclerngto the CRN baseline.crn_advance()If CRN is on, reset, and then jump to the next independent pseudo-random stream and save the new baseline, e.g. before starting a new algorithm iteration.crn_setobs()Set an intermediate CRN for individual oracle observations.crn_nextobs()Jump therngforward, e.g. after taking an observation, andcrn_setobsthe seed.crn_check()f CRN is on, return to the baseline. Otherwise, usecrn_nextobsbefore taking the next observation.TheMOSOSolverClassThe class provides a basic structure for implementing new MOSO algorithms in PyMOSO.Member/MethodDescriptionorcThe oracle object for the solver to solve.dimNumber of dimensions of points in the feasible set. Should matchself.orcnum_objSimilarly, the number of objectives inself.orc.num_callsA running count of the number of simulation replications taken ofself.orc.x0A feasible starting point. This point is additionally supplied to algorithms that don't need one.TheRASolverClassThe class implements a common structure for all RA algorithms, including: caching of simulation replications, scheduling and updating of sample sizes and limits, and a wrapper toOracle.hit.Member/MethodDescriptionsprnAn instance ofMRG32k3afor the solver to use.nbor_radThe neighborhood radius used by solvers seeking local optimality.gbarA dictionary where every key and value is a tuple. The keys are feasible points, values are their objective values.gbaris "wiped" every retrospective iteration.sehatExactly likegbarexcept the values are standard errors.mThe sample size of the current iteration.calc_m(nu)Compute the sample size of the current iteration. RA algorithms automatically do this every iteration and assign the value to `m'.bThe searching sample limit of the current iteration.calc_b(nu)Exactly ascalc_mbut for the searching sample limit.estimate(x, c, obj)Theestimatefunction is essentially a smart wrapper forself.orc.hit. Inputs: tuplexto sample,ca feasibility constraint,objthe objective to constrain. Return: same asOracle.hit. Retrieves or saves the results from/togbarandsehatas appropriate. Returns not feasible if the otherwise feasible result is not less than the constraint.upsample(S)A version ofestimatefor sets. Returns the feasible subset ofS.spline(x, c, obmin, obcon)Return a sample path local minimizer. Input: a feasible start, constraint, objective to minimize, objective to constrain. Return: a set of tuples of the trajectory, the minimizer tuple, the minimum tuple, the standard error tuple.TheRLESolverClassThe sub-class of \inlinetwo{RASolver} adds RLE and its relaxation.Member/MethodDescriptionbetadelAffects the relaxation values computed in RLE.calc_delta(se)Computes the RLE relaxation given a standard error, usingself.mandself.betadel.rle(candidate_les)Input: set of tuples, Returns: set of tuples. Finds the LES at sample sizeself.m.